|beginnosearch| Introduction ============ Welcome to the documentation for Xojo, the cross-platform development tool that makes it fast and easy to build apps for the Desktop, Web and Mobile. To learn more about what Xojo is visit our `website `_. Organization of the documentation --------------------------------- This documentation is organized into six sections: * :ref:`sec-getting_started` provides step by step tutorials as well as a high-level overview of all you need to know to learn Xojo. * :ref:`sec-topics` provides categorized discussions on how to accomplish specific tasks. * :ref:`sec-api` is the complete guide to the specifics of the Xojo language and framework. * :ref:`sec-resources` has additional information including licensing, system requirements, how to report bugs or request features and more. * :ref:`sec-español` has resources for Spanish-speaking Xojo users. * :ref:`sec-fine_print` has all the legalese we have to include. .. tip:: Read our :doc:`Tips for searching the documentation` to help you find what you're looking for fast. New to Xojo ----------- If you are new to Xojo, the best place to start is the :doc:`Introduction ` and then do a QuickStart for :doc:`Desktop`, :doc:`Web`, :doc:`iOS` or :doc:`Android`. .. toctree:: :maxdepth: 1 :caption: GETTING STARTED :name: sec-getting_started getting_started/introduction/index getting_started/quickstarts/index getting_started/tutorials/index getting_started/using_the_ide/index getting_started/using_the_xojo_language/index getting_started/object-oriented_programming/index getting_started/example_projects/examples getting_started/debugging/index .. toctree:: :maxdepth: 1 :caption: TOPICS :name: sec-topics topics/advanced_features/index topics/android/index topics/api_design/index topics/application_deployment/index topics/application_structure/index topics/build_automation/index topics/code_management/index topics/communication/index topics/custom_controls/index topics/data_processing/index topics/databases/index topics/debugging/index topics/declares/index topics/file_management/index topics/graphics/index topics/ios/index topics/linux/index topics/localizing_your_apps/index topics/macos/index topics/migrating_from_other_tools/index topics/office_automation/index topics/os_information/index topics/printing/index topics/raspberry_pi/index topics/text_handling/index topics/threading/index topics/user_interface/index topics/web/index topics/windows/index topics/xojo_cloud/index topics/xojoscript/index .. toctree:: :maxdepth: 1 :caption: API :name: sec-api api/android/index api/compiler_directives/index api/console/index api/cryptography/index api/data_types/index api/databases/index api/deprecated/index api/exceptions/index api/files/index api/graphics/index api/hardware/index api/ios/index api/language/index api/macos/index api/math/index api/mobile/index api/networking/index api/os/index api/pdf/index api/printing/index api/text/index api/user_interface/index api/web/index api/windows/index api/xojocloud/index .. toctree:: :maxdepth: 1 :caption: RESOURCES :name: sec-resources resources/deprecations resources/learn_object-oriented_programming resources/previous_releases/index resources/programming_the_raspberry_pi_with_xojo resources/release_notes/2025r2.1 resources/reporting_bugs_and_making_feature_requests resources/roadmap resources/system_requirements_for_current_release resources/system_requirements_for_previous_releases/index resources/extras/index resources/updating_older_projects resources/videos/index resources/xojotalk_podcast .. toctree:: :maxdepth: 1 :caption: ESPAÑOL :name: sec-español espanol/guia_rapida/index espanol/iniciacion espanol/tutorial/index .. toctree:: :maxdepth: 1 :caption: FINE PRINT :name: sec-fine_print fine_print/copyrights_and_trademarks fine_print/end_user_license_agreement |endnosearch| Introduction ============ .. toctree:: :maxdepth: 1 :name: sec-introduction Creating desktop apps Creating web apps Creating Android apps Creating iOS apps What is Xojo? Activating Your Xojo License and Building Apps ===================================== Getting started creating Desktop apps ===================================== If you're new to Xojo and/or new to developing applications, there are some things you can do to get up and running quickly: 1. `Download `_ and install Xojo. 2. Launch Xojo and sign in (optionally) with your Xojo.com account. 3. Do the :doc:`QuickStart` (15 minutes). 4. Do the :doc:`Tutorial` (60 minutes). 5. Get some :doc:`general information` about Xojo. New to programming ------------------ * Read our `Introduction to Xojo Programming book `_ * Prefer video? Check our our `Video Course: `_ * Learn about Object-Oriented Programming in `this webinar `_ Got a question? --------------- * The `Xojo Forum `_ is full of helpful users and members of the Xojo team. A quick search usually reveals a lot of common questions have already been answered, so be sure to take a look around before you post your questions as it might save you some time! * Search the `Xojo Documentation `_ to find more about your topics of interest * Check out our `YouTube Channel `_ * The `Xojo Blog `_ is the place to get tips, tutorials, examples of new features and a lot more. * `Contact us `_. Migrating from another tool --------------------------- * :doc:`Visual Basic Migration Guide` * :doc:`FileMaker Migration Guide` * :doc:`Other tools` ===================================== Getting started creating Web apps ===================================== If you're new to Xojo and/or new to developing applications, there are some things you can do to get up and running quickly: 1. `Download `_ and install Xojo. 2. Launch Xojo and sign in (optionally) with your Xojo.com account. 3. Do the :doc:`QuickStart` (15 minutes). 4. Do the :doc:`Tutorial` (60 minutes). 5. Get some :doc:`general information` about Xojo. New to programming ------------------ * Read our `Introduction to Xojo Programming book `_ * Prefer video? Check our our `Video Course: `_ * Learn about Object-Oriented Programming in `this webinar `_ Got a question? --------------- * The `Xojo Forum `_ is full of helpful users and members of the Xojo team. A quick search usually reveals a lot of common questions have already been answered, so be sure to take a look around before you post your questions as it might save you some time! * Search the `Xojo Documentation `_ to find more about your topics of interest * Check out our `YouTube Channel `_ * The `Xojo Blog `_ is the place to get tips, tutorials, examples of new features and a lot more. * `Contact us `_. Migrating from another tool --------------------------- * :doc:`Visual Basic Migration Guide` * :doc:`FileMaker Migration Guide` * :doc:`Other tools` ===================================== Getting started creating Android apps ===================================== If you're new to Xojo and/or new to developing applications, there are some things you can do to get up and running quickly: 1. `Download `_ and install Xojo. 2. Launch Xojo and sign in (optionally) with your Xojo.com account. 3. Do the :doc:`QuickStart` (15 minutes). 4. Do the :doc:`Tutorial` (60 minutes). 5. Get some :doc:`general information` about Xojo. .. note:: Android support is considered `beta `_ for 2023r2. New to programming ------------------ * Read our `Introduction to Xojo Programming book `_ * Prefer video? Check our our `Video Course: `_ * Learn about Object-Oriented Programming in `this webinar `_ Got a question? --------------- * The `Xojo Forum `_ is full of helpful users and members of the Xojo team. A quick search usually reveals a lot of common questions have already been answered, so be sure to take a look around before you post your questions as it might save you some time! * Search the `Xojo Documentation `_ to find more about your topics of interest * Check out our `YouTube Channel `_ * The `Xojo Blog `_ is the place to get tips, tutorials, examples of new features and a lot more. * `Contact us `_. Migrating from another tool --------------------------- * :doc:`Visual Basic Migration Guide` * :doc:`FileMaker Migration Guide` * :doc:`Other tools` ================================= Getting started creating iOS apps ================================= If you're new to Xojo and/or new to developing applications, there are some things you can do to get up and running quickly: 1. `Download `_ and install Xojo. 2. Launch Xojo and sign in (optionally) with your Xojo.com account. 3. Do the :doc:`QuickStart` (15 minutes). 4. Do the :doc:`Tutorial` (60 minutes). 5. Get some :doc:`general information` about Xojo. New to programming ------------------ * Read our `Introduction to Xojo Programming book `_ * Prefer video? Check our our `Video Course: `_ * Learn about Object-Oriented Programming in `this webinar `_ Got a question? --------------- * The `Xojo Forum `_ is full of helpful users and members of the Xojo team. A quick search usually reveals a lot of common questions have already been answered, so be sure to take a look around before you post your questions as it might save you some time! * Search the `Xojo Documentation `_ to find more about your topics of interest * Check out our `YouTube Channel `_ * The `Xojo Blog `_ is the place to get tips, tutorials, examples of new features and a lot more. * `Contact us `_. Migrating from another tool --------------------------- * :doc:`Visual Basic Migration Guide` * :doc:`FileMaker Migration Guide` * :doc:`Other tools` ============= What is Xojo? ============= Welcome to Xojo, an integrated software developer tool and programming language that is the easiest way to make desktop, web and mobile apps. With Xojo you can create desktop apps for `Windows`, `Mac` and `Linux` (including Raspberry Pi), web apps for all popular web browsers, and mobile apps for Android and iOS devices. .. image:: https://documentation.xojo.com/getting_started/introduction/images/what_is_xojo_workspace_on_macos.png Xojo is a rapid application development (RAD) tool with a user interface (UI) builder that lets you create your app's user interface with little to no programming required. If you know how to drag and drop, you can build the UI using the wide variety of built-in controls, making it easy to create powerful, multi-platform desktop, web and mobile applications faster than you ever thought possible. If you are new to programming, you will find Xojo's object-oriented programming language easy to learn. If you are an experienced programmer, you will find the language to be powerful and robust. In either case, you will find you can accomplish quite a bit in a short amount of time. One way Xojo makes app development faster and easier than traditional tools is by removing the need to learn how to use the complex Application Programming Interface (API) for the operating system. Instead, you use the Xojo Programming Language and Framework which provides a simpler and faster way to create your apps. Xojo doesn't force you to use a specific OS. Are you a Mac developer that wants to create Windows apps? You can do that with Xojo right from your Mac. Do you use Windows, but want to create Mac apps? You can do that from within Windows. In fact, you can develop using Xojo on `Windows`, `Mac or `Linux` to create apps that work on `Windows`, `Mac`, `Linux` or web (iOS app development does require a `Mac`, however). .. _/getting_started/introduction/what_is_xojo/about_the_xojo_programming_language: About the Xojo programming language ----------------------------------- Xojo is an integrated tool, but it is also a programming language for `Windows`, `Mac`, `Linux`, mobile, web and Raspberry Pi. Xojo builds on languages such as Visual Basic and Java to provide the fastest and easiest way for you to create the apps you need for the platforms you use. Xojo uses safe programming patterns (strong data typing, for example), is object-oriented and has modern programming features such as introspection, extension methods and delegates. Programming with Xojo is fast, easy and most importantly, fun! Although maybe you haven't heard of it before, Xojo has been around for many years. Since 1998, in fact. Xojo was originally called REALbasic, then eventually Real Studio, but the programming language remains largely the same. Xojo was one of the first languages to use Automatic Reference Counting (ARC), something that other languages such as Swift and Objective-C now use. Although Xojo is easy to learn and use, it also has advanced features such as namespaces, extension methods, exception handling, introspection, delegates and more. And Xojo is always being modernized. For 64-bit apps (and Raspberry Pi), Xojo uses the LLVM compiler tools to compile your apps for the best possible performance. LLVM are the same compiler tools used by Apple with Swift and Objective-C. A new Xojo framework is being made available, which uses a more modern design, standardizes API calls and takes advantage of new technologies. Xojo is ready for the future and the next 20 years of software development. Xojo feels familiar to programmers who have used other languages such as Visual Basic, Java and C# because it uses a similar object-oriented programming model, with similar data types and constructs. Using Xojo, you'll be able to create apps even faster than you ever thought possible. Xojo is also friendly to new programmers. A big problem with most programming languages is that they are overcomplicated and overwhelm those new to them. Xojo has a powerful, integrated code editor with auto-complete that makes it easy to learn the language. The Xojo IDE is also incredibly easy to use, making experimentation (one of the best ways to learn) fast and fun. Xojo is a great programming language for creating all types of apps, from simple "hello, world" to powerful enterprise software. With Xojo you'll create your apps in less time than before at less cost, making it a great investment for you or your company. It is a wonderful way to create apps for `Windows`, `Mac`, `Linux`, mobile, web and Raspberry Pi and continues to evolve with new features and capabilities added on a regular basis (quarterly, in fact). Our goals with Xojo have always been to make programming simpler. We can't wait to see what you create with it! .. _/getting_started/introduction/what_is_xojo?/installing_xojo: Installing Xojo --------------- You can download Xojo for your operating system (`Windows`, `Mac` or `Linux`) from the Xojo Download page. You'll have to create an account in order to download Xojo. Once downloaded, Xojo is free to use. You can purchase a license when you are ready to create stand-alone apps. Xojo works on all current operation systems. For more specifics, you can review the Xojo System Requirements. After the download has completed, you can run the installer (or copy the files) for your OS. Once the installation is finished (it only takes a minute or so), you can start Xojo to begin creating your first app. .. _/getting_started/introduction/what_is_xojo?/starting_with_xojo: Starting with Xojo ------------------ After starting Xojo, the Project Chooser window appears. In order for you to quickly dive right into Xojo, follow these steps to create your first (admittedly simple) desktop app. 1. In the Project Chooser, choose "Desktop" and fill in the App Name as "MyFirstApp" and click OK. This displays the IDE (or Integrated Development Environment) with the Workspace for the project. This is where you work on your Xojo projects, creating UI layouts and writing code. Don't worry about everything that you see there, it will be covered later . #. Now, drag a Button from the Control Library on the right onto the Window layout in the center. You can position the button anywhere you like within the window. #. With the button selected, press Return to change its caption to "Click Me". #. Double-click the button on the layout to add an Event Handler. This is where you'll put a single line of code to display a message when the button is clicked. #. After double-clicking the button, the Add Event Handler window appears. .. image:: https://documentation.xojo.com/getting_started/introduction/images/what_is_xojo__add_event_dialog.png 1. Make sure the Pressed event is selected and click OK to add the Pressed event handler. This event handler is called when the button is pressed. #. The Code Editor now appears. Add this simple line of code to display a message: .. code:: xojo MessageBox("Welcome to Xojo!") 1. Click the Run button on the toolbar to run your app. #. Click the button to display the message. .. image:: https://documentation.xojo.com/getting_started/introduction/images/what_is_xojo_welcometoxojo!.png :scale: 50 % That's it. You've now created an app. Xojo uses all native controls so your Welcome to Xojo dialog box might look a bit different depending on what OS you are running. Although Xojo makes it easy to create simple apps, it can also create much larger and more powerful apps just as easily. In fact, Xojo is actually written in Xojo! ============================================== Activating your Xojo license and building apps ============================================== .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/license_activation: License activation ------------------ Xojo is free to download and use for development and testing. A Xojo license is required in order to compile/build Xojo apps. There are two ways to activate your Xojo license: Sign In or Manual Activation. .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/sign_in: Sign in ******* Sign in to the Xojo IDE (the Xojo application, not the website). Signing in with the same account used to purchase your license will automatically download your license and allow you to begin compiling your projects. If you are already signed in to Xojo and have just purchased a license, open the License Key Window (on macOS go to Xojo>License Keys, on Windows and Linux go to Help>License Keys) and click "Update" to download your new license to compile. Xojo Desktop, Web and Mobile licenses allow for 2 simultaneous activations, Xojo Pro allows for 3 and Xojo Pro Plus allows for 6 simultaneous activations. We make it simple to move your license or activations between machines as much as you need, Activating Your Xojo License and Building Apps .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/manual_activation: Manual activation ***************** Manual activation can be used to activate your license on an offline machine, for example if you were in a secure facility or on a research vessel in the sub-arctic. Manual activation is also recommended if you have multiple licenses and wish to activate them on multiple different machines. To activate your Xojo license manually, go to your `License Key page `_ and click on the blue download icon to the right of that license to download the license file. Then, in the Xojo IDE, open the License Key window (on macOS go to Xojo>License Keys, on Windows and Linux go to Help>License Keys) and drag and drop that license file into the window (or click the Add button to add the file). You may be asked to verify your email address, in which case use the email address that is the current default address in your Xojo account. .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/troubleshooting: Troubleshooting *************** If you have a Xojo license but cannot build, either because the Build button is disabled or you get an error saying you need a license in order to build, here's what to check: * **Have you activated your license in Xojo?** If you have activated your Xojo license in Xojo you will see it listed in the License Key Window in the Xojo IDE. (On macOS go to Xojo>License Keys, on Windows and Linux go to Help>License Keys) If you are signed in to the Xojo IDE but don't see your license listed there, click "Update" in the Licenses Window or sign in. .. image:: https://documentation.xojo.com/getting_started/introduction/images/activating_your_xojo_license_and_building_apps_screen_shot_2022-01-19_at_10.47.33_am.png :alt: License Key Window 2021r3 * **Are you signing in to Xojo with the correct account?** Make sure to use the same Xojo account that you used when you purchased, both username and passwords are case sensitive. If you have set up a username other than your email address you *must* use the username, it is not interchangeable with your email address. If you have questions about your account or licenses, `contact us `_. * **Do you have a target OS selected in the Build Settings?** You must have a target selected in Build Settings in order to build. If you don't have a target selected the Build button on the toolbar will be disabled. For Desktop apps "This Computer" is selected by default. For web apps, by default, "Xojo Cloud" is selected. Make sure to select only targets that your Xojo license can build for. * **Do you have the appropriate Xojo license to build your project?** Selecting targets other than those included in your Xojo license will result in an error. .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/more_xojo_license_options_and_tips: More Xojo license options and tips ---------------------------------- .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/licenses: Licenses ******** All Xojo licenses are cross-platform, that means you can activate your license on Mac, Windows and Linux. A Xojo license gives you access to new Xojo releases for a 12 month period. After your Xojo license expires you may continue to build with those releases distributed during that 12 month period, indefinitely. You are never required to renew your Xojo license and can do so anytime before or after its expiration. Xojo licenses allow you to build with all Xojo releases dated before your license expiration date. Point releases are minor updates to the current release where the version number is incremented by ``.1``. Point releases are available to anyone whose license was current during the original release date. Download as many releases of Xojo to your computers as you need. See :doc:`System Requirements` for supported Operating Systems and the System Requirements Archive for releases other than the current Xojo release. .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/moving_an_activation_or_activating_on_a_new_computer: Moving an activation or activating on a new computer **************************************************** Xojo makes it easy to move your license activation between machines as you need. If you have a free activation, sign in to Xojo to automatically download your license and begin building. If you don't see your license after signing in or using the "Update" button, go to your `License Key page `_ at the Xojo website and verify you have a free activation. (If you see a duplicate machine listed under one of your licenses, delete the duplicate.) Click the 'x" next to one of the machines listed below that license to deactivate it, freeing up an activation for the new machine. Sign in to the Xojo IDE or click "Update" in the License Key Window on the new machine to activate your license there and begin building. .. image:: https://documentation.xojo.com/getting_started/introduction/images/activating_your_xojo_license_and_building_apps_screen_shot_2021-01-18_at_9.58.52_am.png .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/activation_error: Activation error **************** If you have a Xojo license but cannot build, either because the Build button is disabled or you get an error saying you need a license in order to build, Activating Your Xojo License and Building Apps. If you are using an older release of Xojo (2015 or earlier) and have received an activation error, please try Activating Your Xojo License and Building Apps. If manual activation does not resolve the issue, download a current release and sign in on that release (disregard any messages indicating that you don't have a license for the release). Now launch your older release and you should have no problem signing in to it then. Upgrading to Catalina or Big Sur changes storage in such a way that it creates a different activation when using Xojo releases from before 2021r1. The solution is to remove the old Xojo activation on that machine at your `license key page `_, then update your licenses in the Xojo IDE. .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/assigning_your_xojo_license_to_someone_else: Assigning your Xojo license to someone else ******************************************* In your Xojo account you can build your Team. Contractors, end-users, developers, the billing department or management -your Team can consist of anyone else with a Xojo account. Assign any of your Xojo licenses to any member of your Team. The assignee gets full access to that Xojo license plus any related technical support and special forum channels. The license owner retains the ability to revoke that license anytime as well as manage the billing. 1. Owner and assignee log in and go to their `Team `_ page. 2. The assignee shares their connection key with the license owner. 3. From the Team page, the owner selects “Add Connection” and “Adds” the assignee's connection key. Now the accounts are connected and the license owner will see that assignee listed in their Team. .. image:: https://documentation.xojo.com/getting_started/introduction/images/activating_your_xojo_license_and_building_apps_add-connection.png 4. Now the license owner can assign their licenses to anyone in their team from their `licenses key page `_. Select the box next to a license, scroll down to “Assign A License” and choose a name from the drop-down. .. image:: https://documentation.xojo.com/getting_started/introduction/images/activating_your_xojo_license_and_building_apps_screen-shot-2018-04-17-at-1.08.55-pm.png Now that the license is assigned, the assignee sees it in their licenses key page, and the owner can no longer use that license for building apps. Add multiple team members and change who the license is assigned to as needed. .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/eight_digit_codes_and_legacy_authorization_codes: Eight digit codes and legacy authorization codes ************************************************ You can find online Legacy Authorization Codes, aka 8 Digit Codes, at the `Legacy Authorization page `_ from your Account Settings page. If you have trouble with this link, please make sure you are logged in and try pasting the link into your browser. Follow along with the instructions in those web pages in Real Studio. Please note that you must remain on the final screen prompting you for this code in the IDE for **2 minutes** prior to pressing "Continue." .. _/getting_started/introduction/activating_your_xojo_license_and_building_apps/compiling_and_building_apps: Compiling and building apps *************************** For more information about building your apps, refer to the appropriate section: * :ref:`Desktop` * :ref:`Web` * :ref:`iOS` * :ref:`Console` QuickStarts =========== .. toctree:: :maxdepth: 1 :name: sec-quickstarts Desktop QuickStart Web QuickStart iOS QuickStart Android QuickStart ================== Desktop QuickStart ================== Welcome to Xojo! Xojo is made up of a rich set of graphical user interface objects, the modern object-oriented Xojo programming language, an integrated debugger, and a multi-platform compiler. Together they make up the Xojo Integrated Development Environment or IDE. You'll use the IDE to build your app's interface by dragging and dropping interface objects onto the apps's windows and dialogs, then you'll add your code to each of these objects. This QuickStart is for people who are new to Xojo. It will give you an introduction to the Xojo development environment and lead you through the development of a working desktop web browser app. It may take you up to 15 minutes to complete this QuickStart. .. _/getting_started/quickstarts/desktop_quickstart/getting_started: Getting started --------------- Launch Xojo. 1. Double-click the **Xojo app icon** to start Xojo. After it finishes loading, the Project Chooser window appears. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/simplebrowserdesktopchooser.png :align: center 2. You are building a Desktop app, if it's not already selected, click on **Desktop**. The dialog has three fields: * **Application Name**: the name of your app. * **Company Name**: the name of your company. * **Application Identifier**: a unique identifier for this app. It will automatically populate using what you enter for the Application and Company Names, but you can also change it to whatever you want. 3. Enter your company name, your own name or leave it blank. #. Enter ``SimpleBrowser`` as the Application Name. #. Click **Create**. .. _/getting_started/quickstarts/desktop_quickstart/ide_workspace: IDE workspace ------------- The Workspace is the main window in Xojo where you design your app. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/desktop_ide_parts.png :align: center * **Navigator**: Shows all the items in your project like windows, menus, pictures, sounds and more. Click on items in this list to edit them. * **Layout Editor**: Use this to create layouts for windows and dialog boxes. It's blank now because you haven't added any controls to it yet. * **Library**: Shows all the controls and other widgets you can drag to your layout to create your user interface. .. _/getting_started/quickstarts/desktop_quickstart/making_a_browser_app: Making a browser app -------------------- In this Desktop QuickStart, you will create a simple web browser. You use the default Window class to create your window and you add controls (user interface classes) to the window to create the design. The app uses these controls: * Text Field: A Text Field control is used to enter text. In this project, the URL to display is typed into a Text Field at the top of the window. * Button: A Button is used to trigger an action. The user clicks the button to load the web page at the URL into the HTML Viewer. * HTML Viewer: An HTML Viewer is used to display HTML (a web page). .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/inspector_-_library.jpg :scale: 50 % :align: center .. _/getting_started/quickstarts/desktop_quickstart/building_the_user_interface: Building the user interface *************************** Window1 is open in the Layout Editor. Let's start adding controls. 1. In the Library, click on the **Text Field** icon and drag it to the top-left corner of the window in the Layout Editor. As you get close to the edges of the window, you will see alignment indicators that help you position the control. #. In the Library, click on the **Default Button** icon and drag it to the top-right corner of the window. #. Drag the **HTML Viewer** icon to the remaining empty area on the page (you may have to scroll down through the controls to see it). #. Resize the **HTML Viewer** control (using the selection handles so that it fills most of the window below the Text Field and Button). #. Resize the **Text Field** by clicking it to show the selection handles. Click the center-right handle and drag it to the right until the alignment guides tell you it is close enough to the Button. Your finished window layout should look like this: .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/simple_browser_initial_layout.png :scale: 50 % :align: center .. _/getting_started/quickstarts/desktop_quickstart/setting_the_properties: Setting the properties ********************** A property describes something about an object such as a window or button. Changing property values allows you to change the look or behavior of that object. .. _/getting_started/quickstarts/desktop_quickstart/inspector: Inspector ^^^^^^^^^ The Inspector is used to change view and control properties. It shares the same area on the right of the Workspace as the Library. .. _/getting_started/quickstarts/desktop_quickstart/setting_the_properties_for_the_window: Setting the properties for the window ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. Display the Inspector by clicking the Inspector button on the toolbar or press :kbd:`⌘ I` if you're on macOS or :kbd:`Ctrl I` on Windows and Linux. You need to change the **Name** and **Title** properties: 2. In the Layout Editor, click on the **title bar** of the window to select it. The Inspector now shows the properties for the window. 3. In the Name field (located in the ID group), change the **name** from ``Window1`` to ``BrowserWindow``. Press :kbd:`RETURN` to see the name change in the Navigator. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/window_name.png :scale: 50 % :align: center 4. In the Title field (located in the Frame group), change the **name** from ``Untitled`` to ``Web Browser``. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/window_title.png :scale: 50 % :align: center .. _/getting_started/quickstarts/desktop_quickstart/setting_the_properties_for_the_text_field: Setting the properties for the text field ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Text Field is where your user enters the URL they want to see in the browser. 1. On the window, select the **TextField** control. The Inspector changes to show the Text Field properties. 2. In the Name field, change the **name** from ``TextField1`` to ``AddressField``. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/textfield_name.png :scale: 50 % :align: center 3. Now make changes to the locking so that the Text Field gets larger or smaller as the window resizes. Click the **locks** so that top, left and right are locked and bottom is unlocked. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/locking_properties.png :scale: 50 % :align: center .. _/getting_started/quickstarts/desktop_quickstart/setting_the_properties_for_the_button: Setting the properties for the button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Your users click the button to display the web page. 1. On the window, select the **Button1** control. The Inspector changes to show the Button properties. 2. In the Name field, change the **name** from ``Button1`` to ``GoButton``. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/button_name.png :scale: 50 % :align: center 3. Now you need to make changes to the locking so that the Button stays attached to the right side of the window when it is resized. Click the **locks** so that top and right are locked and left and bottom are unlocked. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/button_locking.png :scale: 50 % :align: center 4. Give your button a caption. In the Caption field (located in the Appearance group), change the **caption** from ``OK`` to ``Go``. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/button_caption.png :scale: 50 % :align: center .. _/getting_started/quickstarts/desktop_quickstart/setting_the_properties_for_the_html_viewer: Setting the properties for the HTML viewer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The last user interface change you need to make is for the HTML Viewer. 1. On the window, select the **HTMLViewer1** control. The Inspector changes to show the HTML Viewer properties. 2. In the Name field, change the **name** from ``HTMLViewer1`` to ``WebViewer``. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/htmlviewer_name.png :scale: 50 % :align: center 3. Finally, you need to make changes to the locking so that the HTML Viewer continues to fill the window when it is resized. Click the **locks** so that top, bottom, left and right are locked. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/htmlviewer_locking.png :scale: 50 % :align: center .. _/getting_started/quickstarts/desktop_quickstart/adding_code: Adding code *********** Your app is almost complete. Now it is time to add the code that will tell the HTML Viewer which web page to display. 1. On the window, double-click the **Button** control, that you named *Go*. The Add Event Handler window appears. When a user presses a Button, your app runs the code in its Pressed event handler. 2. Make sure *Pressed* is selected in the Event Handler list and click **OK**. #. Notice the Navigator updates to show the Pressed event underneath the GoButton control and the Code Editor displays. #. Go ahead and add this code to the Code Editor. If you don't see the cursor flashing in the Code Editor (the text editing area in the middle of the window), click in that area below the Pressed() event name and then type this code: (It's good practice to type it rather than copy and pasting it) .. code:: xojo WebViewer.LoadURL(AddressField.Text) What does this code do? The URL that a user will type into the Text Field is stored in its Text property, AddressField.Text. Then we want our code to display the web page in the WebViewer, which is done by calling the LoadURL method and sending it to the URL that the user typed. That's it! Your first app is complete. .. _/getting_started/quickstarts/desktop_quickstart/saving_your_project: Saving your project ******************* You should save your work periodically and always before running your project. To do so: 1. Save the project by choosing **File > Save**. #. Click **Save**. .. _/getting_started/quickstarts/desktop_quickstart/running_the_app: Running the app *************** Now you can test your finished app: 1. Click the **Run** button in the toolbar to run your project. The app opens in its own window. #. Type a URL of your choice, remembering to start with ``https://``. #. Click the **OK** button. Your webpage appears. #. Try resizing the window to make it bigger so you can see more of the page. Notice the controls resize or move when you do this. #. Try another URL. This time, enter ``https://www.wikipedia.org``. #. You will see Wikipedia website. #. When you are finished experimenting with the Simple Browser app, you can close the window to return to Xojo. On macOS, choose **SimpleBrowser.debug > Quit** to Quit the app. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/desktop/completed_desktop_quickstart_app.png :scale: 50 % :align: center .. _/getting_started/quickstarts/desktop_quickstart/next_steps: Next steps ---------- This QuickStart has introduced you to Xojo and you've learned how to make your first app by designing a window, adding controls and writing code. A good next step is the :doc:`Desktop Tutorial`. Then explore the Topics and API sections of the documentation to continue learning how to create great apps using Xojo. ============== Web QuickStart ============== Welcome to Xojo, the easiest tool for creating your own web apps. Xojo is made up of a rich set of graphical user interface objects, the modern object-oriented Xojo programming language, an integrated debugger, and a multi-platform compiler. Together they make up the Xojo Integrated Development Environment or IDE. With the IDE, you can build your app's interface by dragging and dropping interface objects onto the app's web pages and dialogs. In this QuickStart, you will see how easy it is. Xojo provides you with the tools you need to build the apps you want. This QuickStart is for people who are new to programming or new to Xojo. It will give you a gentle introduction to the Xojo development environment and lead you through the development of a working web app (an app that uses a web service to search country flags). It may take you up to 15 minutes to complete this QuickStart. This is what the completed app will look like: .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/flag_viewer_app.png :scale: 50 % :align: center .. _/getting_started/quickstarts/web_quickstart/getting_started: Getting started --------------- Now is the time to start Xojo. When it finishes launching, the Project Chooser appears. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/projectchooser.png :scale: 50 % :align: center Xojo lets you build several different types of apps (Desktop, Web, Console and iOS). For this QuickStart, you are building a Web app, so click on Web. You will now see three fields that need values: * **Application Name**: the name of your app. This will be the filename of the actual app file that gets created. * **Company Name**: the name of your company or your own name. You may choose to leave this blank. * **Application Identifier**: a unique identifier for this app. It will automatically populate using what you enter for the Application and Company Names, but you can also change it to whatever you want. In this QuickStart, you will build a web application that makes use of a web service that lets you search for and display flags of various nations around the world. 1. Enter ``FlagSearch`` as the Application Name. You can enter a company name or leave it blank. #. Click **Create**. The main Xojo window (called the Workspace) opens. This is where you will begin designing your app. .. _/getting_started/quickstarts/web_quickstart/using_the_workspace: Using the workspace ------------------- Xojo opens a Workspace window displaying your project with the default Webpage displayed in the Layout Editor. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/layouteditor.png :scale: 50 % :align: center * **Navigator**: the area on the top left shows you all the items in your project. By default you can see WebPage1 (which is selected), the App object and the Session object. You use the Navigator to navigate within your project. * **Layout Editor**: the center area is the Layout Editor. You use the Layout Editor to design the user interface for the windows in your app. It shows the window and previews how it looks when the app runs. In this illustration, the window is blank because you haven't yet added any user interface controls from the Library. * **Library**: the area on the right is the Library and shows the controls and interface elements that you can add to a window or to the project. You design the window by dragging controls from the Library to the window. You can also add a control to the window by double-clicking it. You can change how the controls display in the Library by clicking the small gear icon and choosing a different setting. .. tip:: If the Library is not visible, click the Library button on the toolbar to show it. **Inspector**: Not shown in the above illustration is the Inspector, which allows you to see and change the properties for the selected control. This area of the Main window is shared with the Library. You can show the Inspector by clicking the Inspector button on the toolbar. The Inspector shows information about the selected item in the Navigator or Editor. The contents of the Inspector changes as you click on different items. You change an Inspector value by entering a new value in the field to the right of the field label. .. _/getting_started/quickstarts/web_quickstart/creating_the_flag_search_app: Creating the flag search app ---------------------------- The best way to quickly learn how to use Xojo is to create a simple app. For this QuickStart, you will create a web app that accesses a web service that returns a picture of the flag of the country whose name you provide it. A Xojo app consists of a collection of objects, called classes. Nearly everything in Xojo is a class, including your web pages and the controls on the web page. In the Flag Search project, you will use the default WebPage class to create your web page and you will add controls (user interface classes) to the web page to create the design. Flag Search uses these controls: * **SearchField:** A field where you can enter the name of a country then press Return to search for its flag. * **ImageViewer:** An ImageViewer control is used to view images. In this project, it will display the flag whose name is entered in the Search Field. The next sections walk you through creating the user interface and adding the necessary code to make the app work. .. _/getting_started/quickstarts/web_quickstart/building_the_user_interface: Building the user interface --------------------------- WebPage1 is now open in the Layout Editor. It's time to start adding controls to the web page. You will add a SearchField and an ImageViewer to your webpage by dragging it from the Library on the right side of the Workspace and dropping them on your webpage. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/controls.png :scale: 50 % :align: center The finished layout will look like this: .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/layout.png :scale: 50 % :align: center Let's get started. 1. In the Library, click on the **SearchField** and drag it to the top-left corner of the web page in the Layout Editor. As you get close to the edges of the page, you will see alignment guides that help you position the control. #. Drag the **ImageViewer** control so that's underneath the SearchField. Use the alignment guides to help you determine where to drop it. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/weblayout.png :scale: 50 % :align: center Finally, let's resize the controls to best utilize the space. 4. Click on the **SearchField** to show the selection handles. 5. Click the **center-right handle** and drag it to the right until the alignment guides tell you it is close enough to the right edge of the webpage. 6. Click on the **ImageViewer** to select it. 7. Drag the **selection handle** in the control's lower-right corner and resize it to fill the remaining space. As you approach the bottom and right edges of the layout, alignment guides will appear letting you know to leave some space between the control and the edge of the page. .. _/getting_started/quickstarts/web_quickstart/editing_controls: Editing controls ---------------- There's more to creating a nice user interface than just dragging and dropping controls. You'll need to make a few changes to finish your interface. Next make the SearchField and ImageViewer controls automatically resize themselves based upon the size of the window in which the webpage is displayed. These changes are done using the Inspector. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/inspector.png :scale: 50 % :align: center Let's start with the Search Field: 1. Click on the **Search Field** on the page to select it. 2. If the Inspector is not visible, click the Inspector button in the toolbar to display it. .. note:: In the Inspector notice that the Locking section shows four locks. The top and left are already locked indicating that the Search Field's top and left sides are locked by default. 3. Click the **lock** on the right side to lock the right side of the button to the right side of the page. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/web/locking.png :scale: 50 % :align: center Now let's change the ImageViewer to use all the available space on the webpage. 4. Click on the **ImageViewer** to select it. .. note:: In the Inspector notice that the Locking section shows four locks. The top and left are already locked indicating that the ImageViewer's top and left sides are locked by default. 5. Since you'll want the ImageViewer to become wider or narrower with the window, click on the **lock** on the right hand side to lock the ImageViewer's right side to the right side of the page. #. Since you'll want the ImageViewer to become taller and shorter with the window, click on the **lock** on the bottom to lock the ImageViewer's bottom side to the bottom side of the page. #. Click on the **webpage's title bar** (where it says “Untitled”) to select the page itself. #. Grab the **selection handle** in the lower-right corner of the page and try resizing it a bit to see how the SearchField and ImageViewer behave with their locking properties changed. .. _/getting_started/quickstarts/web_quickstart/getting_ready_for_code: Getting ready for code ---------------------- When you start writing code you will sometimes need to refer to the webpage or controls on the webpage. Xojo gives them default names such as WebPage1, SearchField1 and ImageViewer1 but those names are not particularly descriptive. Giving webpages and controls sensible names will help make your code easier to read. Let's do it. 1. Click on the **webpage title** to select WebPage1. 2. In the Inspector, change the **Name** from ``WebPage1`` to ``FlagSearch``. 3. Change the **Title** property to ``Flag Search``. 4. Click on the **SearchField**. 5. In the Inspector, change the **Name** to ``CountryNameSearch``. 6. Change the **Hint** property to ``Enter the name of a country``. 7. Click on the **ImageViewer** to select it. 8. Change the **Name** property to ``FlagViewer``. .. _/getting_started/quickstarts/web_quickstart/adding_code: Adding code ----------- Your application is almost complete. Now it is time to add the code that will find the flag based upon the country name the user enters and display it in the FlagViewer control. Xojo uses an object-oriented programming language that is quite easy to learn. You need only one simple line of code to finish your project! The code you will write needs to: * Execute when the user presses the Return key or clicks the search button on the right side of the Search Field. * Because it will be utilizing a web service that uses dashes instead of spaces in country names, change spaces the user enters into dashes. * Combine the updated value the user entered with the URL of the web service to create a URL that is then assigned to the FlagViewer so that it can retrieve and display the appropriate flag. Let's get started. 1. Double-click on the **Search** Field to open the Add Event Handler dialog box. 2. Select the **Pressed** event. This event fires when the user presses the button. 3. Enter the following line of code: .. code:: xojo FlagViewer.URL = "https://flagsearch.xojo.com/API?search=" + Me.Text.Replace(" ", "-") .. note:: This line of code creates a URL to be used to perform the search. The first part (in quotes) is the URL that the flagsearch service expects to do a search. Added to that is Me.Text.Replace(" ", "-"). *Me* in this case refers to the control itself (the CountrySearch field). **Text** refers to its Text property and the *Replace* function is then replacing any spaces you entered with a dash because spaces are not allowed in URLs. This completed URL is then assigned to the URL property of the FlagViewer control which forces it to attempt to access the picture at that location. That's it. Your first web application is complete. .. _/getting_started/quickstarts/web_quickstart/saving_your_project: Saving your project ------------------- Before we run the app to test it, it's a good idea to save your project so should something go wrong you don't lose your work: 1. Choose **File > Save**. 2. When the Save As dialog box appears, enter ``Flag Search`` as the name of your project. 3. Press the **Save** button. .. _/getting_started/quickstarts/web_quickstart/running_your_app: Running your app ---------------- Now let's test your finished web application: 1. Press the **Run** button in the toolbar. Xojo will compile your application and as long as there are no errors, it will launch the application on your computer and then launch your browser to display the app. 2. When your browser appears, click in the **Search** field and type ``France``. 3. Press Return. The flag of the country of France should appear. If it doesn't make sure you spelled it correctly. Try searching for the flags of other countries as well. When you're done, close the tab or browser window and then return to Xojo. It may take a few minutes for Xojo to realize you have closed the browser so click the Stop sign button in the Xojo window to stop execution of your app. .. tip:: If no flags are appearing, make sure you are connected to the Internet. If you are, recheck the line of code you entered. Make sure you used *http* and not *https* for example. .. _/getting_started/quickstarts/web_quickstart/next_steps: Next steps ---------- The QuickStart has introduced you to Xojo. You've learned how to make your first app by designing a web page, adding controls and writing code. You should next work through the :doc:`Web Tutorial` and then explore the `Introduction to Xojo Programming `_ book if you are new to programming. Be sure to also read :doc:`What is Xojo?` and review the API section of the documentation to continue learning how to create great web apps using Xojo. ============== iOS QuickStart ============== This iOS QuickStart will give you an introduction to the Xojo development environment and lead you through the development of a working iOS app, a simple web browser. It should take you 15 minutes or less to complete this. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/iphone.png :scale: 25 % :align: center .. _/getting_started/quickstarts/ios_quickstart/ios_development_requirements: iOS development requirements ---------------------------- A Mac is required to develop iOS projects with Xojo. To run/test/debug an iOS project you create in Xojo requires the iOS Simulator which is provided with Apple's Xcode development tool. You can `download Xcode `_ for free from the Mac App Store. After you have downloaded and installed Xcode, run it one time to accept its License Agreement. After that, quit Xcode. If you have not yet downloaded and installed Xcode, do that now before continuing with the QuickStart. .. _/getting_started/quickstarts/ios_quickstart/shortcuts: Shortcuts --------- We've provided a `video `_ of the QuickStart, in case you'd rather watch than go through the steps yourself. You can `download `_ the finished Xojo project as well if you'd prefer. .. _/getting_started/quickstarts/ios_quickstart/getting_started: Getting started --------------- 1. Launch Xojo. After it finishes loading, the Project Chooser window appears. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/new_project_chooser.png :scale: 50 % :align: center 2. Click on iOS to select it. You see three fields that need values: * **Application Name** will be the filename of the actual app file that you create. * **Company Name** is the name of your company. You may choose to leave this blank. * **Application Identifier** is a unique identifier for this app. 3. Enter ``SimpleBrowser`` as the Application Name. 4. Click **Create** to open the Workspace, the main Xojo window, where you will begin designing your app. .. _/getting_started/quickstarts/ios_quickstart/workspace: Workspace --------- The Workspace opens with the default screen. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/ide_for_docs_ios.png :scale: 50 % :align: center .. _/getting_started/quickstarts/ios_quickstart/making_the_simple_browser_app: Making the simple browser app ----------------------------- .. _/getting_started/quickstarts/ios_quickstart/overview: Overview ******** A Xojo app consists of a collection of objects, called classes. Nearly everything in Xojo is a class, including screens and controls. In the SimpleBrowser project, you use the default Screen class to create your screen and you add controls (user interface classes) to the screen to create the design. The app uses three controls: * **Text Field**: A Text Field control is used to enter text. In this project, the URL to display is typed into a Text Field at the top of the screen. * **Button**: A Button is used to trigger an action. The user presses the button to load the web page at the URL into the HTML Viewer. * **HTML Viewer**: An HTML Viewer is used to display a web page. .. _/getting_started/quickstarts/ios_quickstart/building_the_user_interface: Building the user interface *************************** With Screen1 open in the Layout Editor, you are ready to start adding controls to the screen. 1. In the Library, click on the **Text Field** icon and drag it to the top-left corner of the screen in the Layout Editor. As you get close to the edges of the screen, you will see alignment indicators that help you position the control. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/drag_first_control_to_layout.png :scale: 50 % :align: center 2. In the Library, click on the **Button** icon and drag it to the top-right corner of the screen. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/drag_second_control_to_layout.png :scale: 50 % :align: center 1. Drag the **HTML Viewer** icon to the remaining empty area on the screen. Resize this control (using the selection handles so that it fills the screen below the Text Field and Button). .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/drag_third_control_to_layout.png :scale: 50 % :align: center 4. Resize the **Text Field** so that it is larger. Click on it to show the selection handles. Click the center-right handle and drag it to the right until the alignment guides tell you it is close enough to the Button. Your finished screen layout should look like this: .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/finished_layout.png :scale: 50 % :align: center .. _/getting_started/quickstarts/ios_quickstart/setting_the_properties: Setting the properties ********************** A property is a value of a class. Changing property values allows you to change the behavior of the class. .. _/getting_started/quickstarts/ios_quickstart/inspector: Inspector ^^^^^^^^^ The Inspector is used to change screen and control properties. It shares the same area on the right of the Workspace as the Library. .. _/getting_started/quickstarts/ios_quickstart/setting_the_properties_for_the_screen: Setting the properties for the screen ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. In order to show the Inspector, click the Inspector button on the toolbar or press :kbd:`⌘ I` (:kbd:`Ctrl I` on Windows and Linux). .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/inspector_browserscreen.png :scale: 50 % :align: center 2. In the Layout Editor, click on the **iPhone screen** (not on any control) to select it. The Inspector now shows the properties for the screen. 3. In the Name field (located in the ID group), change the **name** from ``Screen1`` to ``BrowserScreen``. Press :kbd:`RETURN` to see the name change in the Navigator. 4. In the Title field, change the **name** to ``SimpleBrowser``. .. _/getting_started/quickstarts/ios_quickstart/setting_the_properties_for_the_text_field: Setting the properties for the text field ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Text Field is where your user enters the URL they want to see in the browser. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/urlfield_inspector.png :scale: 50 % :align: center 1. In the Navigator, select the **TextField1** control on BrowserScreen. The Inspector changes to show the Text Field properties. #. In the Name field, change the **name** from ``TextField1`` to ``URLField``. #. In the InputType field, select **URL** from the popup menu. This displays the special URL keyboard on the iOS device when the user taps in the field. #. In the Text field, enter ``https://www.wikipedia.org``. .. _/getting_started/quickstarts/ios_quickstart/setting_the_properties_for_the_button: Setting the properties for the button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When running the app, pressing the button displays the web page. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/ios_quickstart_buttonios.png :scale: 50 % :align: center 1. On BrowserScreen, select the **Button1** control. The Inspector changes to show the Button properties. #. In the Name field, change the **name** from ``Button1`` to ``ShowButton``. #. Give your button a caption by changing the **Caption** field from ``Button`` to ``Show``. .. _/getting_started/quickstarts/ios_quickstart/setting_the_properties_for_the_html_viewer: Setting the properties for the HTML viewer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The last user interface change you need to make is for the HTML Viewer. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/inspector_webviewer.png :scale: 50 % :align: center 1. On BrowserScreen, select the **HTMLViewer1** control. The Inspector changes to show the HTML Viewer properties. #. In the Name field, change the **name** from ``HTMLViewer1`` to ``WebViewer``. .. _/getting_started/quickstarts/ios_quickstart/adding_code: Adding code *********** Your app is almost complete. Now it is time to add the code that will tell the HTML Viewer (called WebViewer) the web page to display. Follow these steps to add the code: 1. On BrowserScreen, double-click the **ShowButton** control. It's labelled "Show". .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/ios/add_event_handler_dialog.png :scale: 50 % :align: center 2. The Add Event Handler window appears. Event Handlers occur when the user initiates an action. In this case, when a user presses on a Button, your app runs any code in its Pressed event handler. Select **Pressed** from the Event Handler list and click **OK**. Notice the Navigator updates to show the Pressed event underneath the ShowButton control and the Code Editor displays. 3. Now you need to get the URL that the user typed. The value that a user types into a Text Field is stored in the Text property of the Text Field. Then you want to have the WebViewer display the web page. This is done by calling the LoadURL method of the HTML Viewer control and sending it the URL that the user typed. So, you need to add this code to the Code Editor. Start by clicking in **the white space below the Pressed event name** and then type this code (do type it rather than copy and pasting it): .. code:: xojo WebViewer.LoadURL(URLField.Text) That's it! Your first app is complete. .. _/getting_started/quickstarts/ios_quickstart/saving_your_work: Saving your work **************** Before you go any further, save your work: 1. Save the project by choosing **File > Save As**. #. Click **Save**. .. _/getting_started/quickstarts/ios_quickstart/running_your_project: Running your project ^^^^^^^^^^^^^^^^^^^^ 1. In order to run an iOS project, you first need to download and install Xcode in order to get the iOS Simulator app that is used to run iOS apps on a Mac. You can download Xcode for free from the Mac App Store. After you have downloaded and installed Xcode, **you need to run it one time to accept its License Agreement**. After doing this, you can quit Xcode as you will not need it. #. Click the **Run** button in Xojo to run the app in the iOS Simulator. By default the Simulator chooses the smallest device. You can choose other simulated devices (or even an actual device if you have it plugged in to your Mac) by choosing Project > Run On, then choosing a device from the menu. #. Type a (**secure, https**) URL of your choice (or use the default) and click the **Show** button. #. You will see the web page. #. When you are finished experimenting with the Simple Browser app, you can quit the iOS Simulator to return to Xojo. Making your app more robust *************************** If while the user is entering a new URL they accidentally type a space at the beginning or end of the URL then tap the Show button, the webpage won't load because that a URL can't begin or end with a space. To resolve this, you can add the Trim function to the code that loads the URL so that any spaces will be removed automatically. To do this: 1. Click on BrowserScreen in the Navigator (if it's not already displayed). 2. Double-click the Show button. 3. In the Code Editor, add `.Trim` after Text but before the closing parenthesis. Your updated code should look like this: .. code:: xojo WebViewer.LoadURL(URLField.Text.Trim) Try running your project again and test putting any number of spaces before or after the URL. They will be removed when the URL is sent to the HTMLViewer control. Problem solved! .. _/getting_started/quickstarts/ios_quickstart/what's_next: What's next ----------- This QuickStart has introduced you to Xojo and showed you how to make a simple app. Next, try this :doc:`iOS Tutorial` that walks you through building a Task Manager app. For more details, view the Topics and API sections of the documentation. ================== Android QuickStart ================== This Android QuickStart will give you an introduction to the Xojo development environment and lead you through the development of a working Android app, a simple web browser. It should take you 15 minutes or less to complete this. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/completed_app.png :scale: 25 % :align: center .. _/getting_started/quickstarts/android_quickstart/ios_development_requirements: Android development requirements -------------------------------- To run, test and debug an Android project you create in Xojo requires that Google's Android Studio development tool be installed but you will not have to use it. If you have not yet downloaded and installed Android Studio yet, :doc:`do that now` before continuing with the QuickStart. .. important:: Make sure you quit Android Studio after installing it and before running an Android project in Xojo. Otherwise, you won't be able to run your Android project. .. _/getting_started/quickstarts/android_quickstart/shortcuts: Shortcuts --------- We've provided a `video `_ of the QuickStart, in case you'd rather watch than go through the steps yourself. You can :download:`download` the finished Xojo project as well if you'd prefer. .. _/getting_started/quickstarts/android_quickstart/getting_started: Getting started --------------- 1. Launch Xojo. After it finishes loading, the Project Chooser window appears. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/new_project_chooser.png :scale: 50 % :align: center 2. Click on Android to select it. You see three fields that need values: * **Application Name** will be the filename of the actual app file that you create. * **Company Name** is the name of your company. You may choose to leave this blank. * **Application Identifier** is a unique identifier for this app. 3. Enter ``SimpleBrowser`` as the Application Name. 4. Click **Create** to open the Workspace, the main Xojo window, where you will begin designing your app. .. _/getting_started/quickstarts/android_quickstart/workspace: Workspace --------- The Workspace opens with the default screen. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/ide_for_docs_android.png :scale: 25 % :align: center .. _/getting_started/quickstarts/android_quickstart/making_the_simple_browser_app: Making the simple browser app ----------------------------- .. _/getting_started/quickstarts/android_quickstart/overview: Overview ******** A Xojo app consists of a collection of objects, called classes. Nearly everything in Xojo is a class, including screens and controls. In the SimpleBrowser project, you use the default Screen class to create your screen and you add controls (user interface classes) to the screen to create the design. The app uses three controls: * **TextField**: A TextField control is used to enter text. In this project, the URL to display is typed into a TextField at the top of the screen. * **Button**: A Button is used to trigger an action. The user presses the button to load the web page at the URL into the HTML Viewer. * **HTML Viewer**: An HTML Viewer is used to display a web page. .. _/getting_started/quickstarts/android_quickstart/building_the_user_interface: Building the user interface *************************** With Screen1 open in the Layout Editor, you are ready to start adding controls to the screen. 1. In the Library, click on the **TextField** icon and drag it to the top-left corner of the screen in the Layout Editor. As you get close to the edges of the screen, you will see alignment indicators that help you position the control. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/drag_first_control_to_layout.png :scale: 25 % :align: center 2. In the Library, click on the **Button** icon and drag it to the top-right corner of the screen. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/drag_second_control_to_layout.png :scale: 25 % :align: center 3. In the Library, click on the **HTML Viewer** icon and drag it to the middle of the screen. Resize the control (using the selection handles so that it fills the screen below the TextField and Button). .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/drag_third_control_to_layout.png :scale: 25 % :align: center 4. Resize the **TextField** so that it is larger. Click on it to show the selection handles. Click the center-right handle and drag it to the right until the alignment guides tell you it is close enough to the Button. Your finished screen layout should look like this: .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/finished_layout.png :scale: 25 % :align: center .. _/getting_started/quickstarts/android_quickstart/setting_the_properties: Setting the properties ********************** A property is a value of a class. Changing property values allows you to change the behavior of the class. .. _/getting_started/quickstarts/android_quickstart/inspector: Inspector ^^^^^^^^^ The Inspector is used to change screen and control properties. It shares the same area on the right of the Workspace as the Library. .. _/getting_started/quickstarts/android_quickstart/setting_the_properties_for_the_screen: Setting the properties for the screen ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. In order to show the Inspector, click the Inspector button on the toolbar or press :kbd:`⌘ I` (:kbd:`Ctrl I` on Windows and Linux). .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/inspector_browserscreen.png :scale: 50 % :align: center 2. In the Layout Editor, click on the **Screen1** (not on any control) to select it. The Inspector now shows the properties for the screen. 3. In the Name field (located in the ID group), change the **name** from ``Screen1`` to ``BrowserScreen``. Press :kbd:`RETURN` to see the name change in the Navigator. 4. In the Title field, change the **name** to ``SimpleBrowser``. .. _/getting_started/quickstarts/android_quickstart/setting_the_properties_for_the_text_field: Setting the properties for the TextField ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The TextField is where your user enters the URL they want to see in the browser. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/urlfield_inspector.png :scale: 50 % :align: center 1. In the Navigator, select the **TextField1** control on BrowserScreen. The Inspector changes to show the TextField properties. #. Change the **Name** property from ``TextField1`` to ``URLField``. #. Change the **InputType** property to **URL**. This displays the special URL keyboard on the Android device when the user taps in the field. #. Change the **Text** property to ``https://wikipedia.org``. .. _/getting_started/quickstarts/android_quickstart/setting_the_properties_for_the_button: Setting the properties for the button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When running the app, pressing the button displays the web page. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/button_inspector.png :scale: 50 % :align: center 1. On BrowserScreen, select the **Button1** control. The Inspector changes to show the Button properties. #. In the Name field, change the **name** from ``Button1`` to ``ShowButton``. #. Give your button a caption by changing the **Caption** field from ``Button`` to ``Show``. .. _/getting_started/quickstarts/android_quickstart/setting_the_properties_for_the_html_viewer: Setting the properties for the HTML viewer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The last user interface change you need to make is for the HTML Viewer. .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/webviewer_inspector.png :scale: 50 % :align: center 1. On BrowserScreen, select the **HTMLViewer1** control. The Inspector changes to show the HTML Viewer properties. #. In the Name field, change the **name** from ``HTMLViewer1`` to ``WebViewer``. .. _/getting_started/quickstarts/android_quickstart/adding_code: Adding code *********** Your app is almost complete. Now it is time to add the code that will tell the HTML Viewer (called WebViewer) the web page to display. Follow these steps to add the code: 1. On BrowserScreen, double-click the **ShowButton** control. It's labelled "Show". .. image:: https://documentation.xojo.com/getting_started/quickstarts/images/android/add_event_handler_dialog.png :scale: 25 % :align: center 2. The Add Event Handler window appears. Event Handlers occur when the user initiates an action. In this case, when a user presses on a Button, your app runs any code in its Pressed event handler. Select **Pressed** from the Event Handler list and click **OK**. Notice the Navigator updates to show the Pressed event underneath the ShowButton control and the Code Editor displays. 3. Now you need to get the URL that the user typed. The value that a user types into a TextField is stored in the Text property of the TextField. Then you want to have the WebViewer display the web page. This is done by calling the LoadURL method of the HTML Viewer control and sending it the URL that the user typed. So, you need to add this code to the Code Editor. Start by clicking in **the white space below the Pressed event name** and then type this code (do type it rather than copy and pasting it): .. code:: xojo WebViewer.LoadURL(URLField.Text) That's it! Your first app is complete. .. _/getting_started/quickstarts/android_quickstart/saving_your_work: Saving your work **************** Before you go any further, save your work: 1. Save the project by choosing **File > Save As**. #. Click **Save**. .. _/getting_started/quickstarts/android_quickstart/running_your_project: Running your project ^^^^^^^^^^^^^^^^^^^^ 1. Click the **Run** button in Xojo to run the app in the Android Simulator. It may take a minute for the Android emulator to load. #. Type a (**secure, https**) URL of your choice (or use the default) and click the **Show** button. #. You will see the web page. #. When you are finished experimenting with the Simple Browser app, you can quit the Android Simulator to return to Xojo. .. _/getting_started/quickstarts/android_quickstart/what's_next: What's next ----------- This QuickStart has introduced you to Xojo and showed you how to make a simple app. Next, try the :doc:`Android Tutorial` that walks you through building a Task Manager app. For more details, view the Topics and API sections of the documentation. Tutorials ========= .. toctree:: :maxdepth: 1 :name: sec-main Desktop Tutorial Web Tutorial iOS Tutorial Android Tutorial .. toctree:: :maxdepth: 1 :name: sec-databases DBKit Desktop Tutorial DBKit Web Tutorial .. toctree:: :maxdepth: 1 :name: sec-more Building a tip calculator to compare Xojo and Swift Recreating Facebook Messenger Connecting to a serial device ================ Desktop Tutorial ================ This Desktop Tutorial is an introduction to the Xojo desktop development environment and will lead you through the development of a real desktop app. It should take you about an hour to complete this tutorial. If you would like a shorter introduction to Xojo for desktop, take a look at the :doc:`Desktop QuickStart`. If you have experience with other programming languages, you'll also want to check out :doc:`Using the Xojo language` and the :doc:`API` sections of the Documentation. .. _/getting_started/tutorials/desktop_tutorial/getting_started: Getting started --------------- 1. If you haven't done so already, now is the time to start Xojo. After it finishes loading, the Project Chooser window appears. If you already had Xojo running, choose File > New Project and click on Desktop in the Project Chooser. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/desktop_tutorial/project_chooser.png :scale: 50 % :align: center Xojo lets you build several different types of apps (Desktop, Web, Console and iOS). For this Tutorial, you are building a desktop app, so click on Desktop. You should now see three fields that need values: Application Name, Company Name and Application Identifier. * **Application Name**: the name of your app. This will be the filename of the actual app file that gets created. * **Company Name**: the name of your company. * **Application Identifier**: a unique identifier for this app. It will automatically populate using what you enter for the Application and Company Names, but you can also change it to whatever you want. 2. Enter ``Task Manager`` as the Application Name. 3. You can leave Company Name as it is or change it. 4. Click **Create** to open the main Xojo window (called the Workspace), where you will begin designing your app. .. _/getting_started/tutorials/desktop_tutorial/the_workspace: The Workspace ------------- Xojo opens the Workspace with the default window for your app selected in the Navigator and displayed in the Layout Editor. There are three basic parts: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/desktop_tutorial/desktop_ide_parts.png :align: center * **Navigator**: The area on the top left shows you all the items in your project. By default you can see Window1 (which is selected), the App object and the MainMenuBar object. Use the Navigator to navigate within your project. * **Editor**: The center area is the Editor Area. Currently it's showing the Layout Editor. You use the Layout Editor to design the user interface for the windows in your app. It shows the window and previews how it looks when the app runs. In this image, the window is blank because you haven't yet added any user controls. The Editor Area however can also show other kinds of editors for editing code, icons and other things you might use in a project. * **Library**: The area on the right is the Library and shows the controls and interface elements that you can add to a window. You design the window by dragging controls from the Library to it. You can also add a control to the window by double-clicking it. .. note:: If your Library looks different than the one above, that's because it was set to show icons only. You can customize yours by clicking on the small gear icon near the top-left corner of the Library and then choosing a different setting. If the Library is not visible, click the Library button on the toolbar to show it. **Inspector**: Not shown in the above image is the Inspector, which allows you to see and change the various attributes (the height, width, name and more) of any control you've added to your webpage. This area of the window is shared with the Library. You can show the Inspector by clicking the Inspector button on the toolbar. The Inspector shows information about the selected item in the Navigator or Editor. The contents of the Inspector changes as you click on different items. .. _/getting_started/tutorials/desktop_tutorial/about_the_task_manager_app: About the app you're about to create ------------------------------------ In this tutorial you will create a Task Manager, an app to manage tasks. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/desktop_tutorial/completed_app.png :align: center .. _/getting_started/tutorials/desktop_tutorial/desktop_tutorial:_task_manager: Creating the Task Manager app ***************************** For the Task Manager app, you enter tasks in the text field and click Add to add them to the list. You can click on individual tasks in the list to delete them or mark them as complete. Task Manager uses three types of controls: * List Box: A control that is a scrollable list. It can show both single and multiple-column lists and scroll horizontally and vertically. * Text Field: A control for entering a single line of text. * Button: A standard button. It is most often used to initiate an action. In the next sections, you'll create this app step-by-step. .. _/getting_started/tutorials/desktop_tutorial/designing_the_user_interface: Designing the user interface ---------------------------- .. _/getting_started/tutorials/desktop_tutorial/task_list: Creating the task list ********************** You should have Xojo running and Window1 open in the Layout Editor. You are now going to add a Listbox to the window. The Listbox is used for storing the tasks. 1. In the Control Library, click on the **Listbox** and drag it to the top-left corner of the Layout Editor. As you get close to the edges of the window, you will see alignment indicators that help you position the control. Drop the Listbox when you are happy with its position. #. Click on the **Listbox** so that the resizing handles appear. Grab the handle in the bottom-right corner and drag it to enlarge the Listbox to fill the top 2/3 of the window. .. _/getting_started/tutorials/desktop_tutorial/buttons: Adding buttons ************** Now you will add the three buttons needed by Task Manager to the window. The Delete button removes tasks from the Listbox, the Add button adds tasks to the Listbox and the Complete button marks tasks in the Listbox as completed. 1. In the Library, click on the **Button control** |images/desktop_tutorial/button.png| and drag it to the window below the lower-right corner of the Listbox. This will be the Delete button. Use the alignment indicators to help you position the button so that it lines up with the right edge of the Listbox. .. |images/desktop_tutorial/button.png| image:: images/desktop_tutorial/button.png :scale: 25 % #. Drag another **Button** control it to the window near the bottom-left corner. This will be the Add button. Again, take advantage of the alignment indicators to help you position the button. #. Drag another **Button** control again and drag it to the window near the bottom-right corner. This will be the Complete button. .. _/getting_started/tutorials/desktop_tutorial/text_field: Adding a text field ******************* The Text Field is where the user types the Task to add to the list. 1. In the Library, click on **TextField** and drag it to the window so that it is between what will be the Delete and Add buttons. #. Resize the Task field by selecting the rightmost drag handle and drag-resize it until it's the same width as the Listbox. Use the alignment indicators as guides to help you line everything up correctly. After adding all the controls, your window layout should now look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/desktop_tutorial/initial_window_layout.png :align: center .. _/getting_started/tutorials/desktop_tutorial/properties: Understanding Properties ------------------------ .. _/getting_started/tutorials/desktop_tutorial/what_is_a_property?: What is a property? ******************* A property is a value of an object. Changing property values allows you to change the behavior of the object. For this project, you want to change various properties for the view and the controls you added. Some of the things you need to do are: * Rename all controls (and the window) so that they describe their behavior and are easy to refer to in code. * Add Captions to the Buttons. * Set Locking properties so that the controls resize properly when the window is resized. .. _/getting_started/tutorials/desktop_tutorial/inspector: The Inspector pane ****************** The Inspector is used to change view and control properties. It shares the same area on the right of the Workspace as the Library. In order to show the Inspector, click the Inspector button on the toolbar or press :kbd:`⌘ I` (:kbd:`Ctrl I` on Windows and Linux). .. _/getting_started/tutorials/desktop_tutorial/window_properties: Setting Window properties ************************* 1. If you haven't already, display the Inspector by clicking the Inspector button on the toolbar. You need to change the Name and Title properties of the window. 2. First, in the Layout Editor, click on the **window** to select it. The Inspector now shows the properties for the window. .. tip:: To select the window, click anywhere on the window where there is no control. When you select selection handles appear around the window, you'll know you have selected it. 3. In the Name field (located in the ID group), change the **name** from ``Window1`` to ``TaskManagerWindow``. #. Press :kbd:`Return` to see the name change in the Navigator. #. In the Title field (located in the Frame group), change the **name** from ``Untitled`` to ``Task Manager``. #. Press :kbd:`Return` to see the name change in the title bar of the window. .. _/getting_started/tutorials/desktop_tutorial/listbox_properties: Setting ListBox properties ************************** The Listbox is where the tasks that your user enters are displayed and stored. You need to change the following properties: Name, Column Count, Initial Value, Column Widths and Locking. 1. First, in the Layout Editor, click on the **Listbox** to select it. The Inspector now shows the properties for Listbox. #. Change the **Name property** from ``Listbox1`` to ``TaskList``. Press :kbd:`Return` to see the name change in the Navigator. #. The Listbox has two columns, one to show the completed status and another to show the name of the task. Change the **Column Count** property from ``1`` to ``2``. Press :kbd:`Return` to see the Listbox appear with two columns layout. #. You want to change the column headers to describe the data in the list. Find the **Initial Value** property and click the "pencil" icon to its right. This opens a small editor window where you can enter the column heading values. #. Type ``Completed``, press :kbd:`Tab`, and then type ``Task``. #. Click **OK**. You'll see the column headings for the ListBox change. #. Since the Completed column is only going to contain a simple checkmark when the task is marked as completed, it can be narrower. Set the **Column Widths** property to ``100,*``. Press :kbd:`Return` to see the column widths change. Using *100,** tells the Listbox that the first column should always be 100 pixels wide and that the rest of the columns share the available width. Lastly you need to make changes to the locking so that the Listbox gets larger or smaller as the window size changes. In the Locking group look at the image that shows the window with small locked icons for the top and left and small unlocked icons for bottom and right. 8. Click the **locks** so that top, left, bottom and right are all locked. .. _/getting_started/tutorials/desktop_tutorial/button_properties: Setting Button properties ************************* The three buttons are used to perform actions. You need to change the following properties for each button: Name, Caption and Locking. .. _/getting_started/tutorials/desktop_tutorial/configuring_the_delete_button: Configuring the Delete button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Delete button is used to remove tasks from the TaskList. 1. First, in the Layout Editor, click on the **Delete button** to select it (this is the button directly below the Listbox). The Inspector now shows the properties for Button. #. Change the **Name property** from ``Button1`` to ``DeleteButton``. Press :kbd:`Return` to see the name change in the Navigator. #. In the Caption field (located in the Appearance group), change the **name** from ``Button`` to ``Delete``. Press :kbd:`Return` to see the name change on the button in the window. #. Now you need to make changes to the locking so that the Delete button stays on the right side of the window when the window resizes. In the Locking group, click the **locks** so that right and bottom are locked and left and top are unlocked. .. _/getting_started/tutorials/desktop_tutorial/configuring_the_add_button: Configuring the Add button ^^^^^^^^^^^^^^^^^^^^^^^^^^ The Add button is used to add the task entered in the TextField to the Task List. 1. In the Layout Editor, click on the **Add button** to select it (this is the button on the far left of the window below the TextField). The Inspector now shows the properties for Button. #. Change the **Name property** from ``Button2`` to ``AddButton``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Caption property** from ``Button`` to ``Add``. Press :kbd:`Return` to see the name change on the button in the window. #. Now you need to change the locking so that the Add button stays on the bottom of the window when the window resizes. In the Locking group, click the **locks** so that left and bottom are locked and top and right are unlocked. .. _/getting_started/tutorials/desktop_tutorial/configuring_the_complete_button: Configuring the Complete button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Complete button is used to mark a task as completed. 1. In the Layout Editor, click on the **Complete button** to select it (this is the button directly below the TextField on the right). The Inspector now shows the properties for the Button. #. Change the **Name property** from ``Button3`` to ``CompleteButton``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Caption property** from ``Button`` to ``Complete``. Press :kbd:`Return` to see the name change on the button in the window. #. Now you need to make changes to the locking so that the Complete button stays on the right side of the window when the window resizes. In the Locking group, click the **locks** so that right and bottom are locked and left and top are unlocked. In the Project List, the newly renamed controls show under the Controls for TaskManagerWindow. .. _/getting_started/tutorials/desktop_tutorial/text_field_properties: Setting TextField properties **************************** The TextField is where your user will type the task to add to the list. You need to change the following properties: Name and Locking. 1. In the Layout Editor, click on the TextField to select it. The Inspector now shows the properties for the TextField. #. Change the **Name property** from ``TextField1`` to ``TaskField``. Press :kbd:`Return` to see the name change in the Navigator. #. Now you need to make changes to the locking so that the TextField gets larger or smaller when the window resizes. In the Locking group, click the **locks** so that left, bottom and right are locked and top is unlocked. After adjusting all the properties for the window and its controls, your layout should look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/desktop_tutorial/final_window_layout.png :align: center .. _/getting_started/tutorials/desktop_tutorial/saving_your_work: Saving your work ---------------- Your user interface layout is now complete, so it's time to try it out. But before you go further, you should first save your work. 1. Save the project by choosing **File > Save**. #. Name the project ``“TutorialDesktop”`` and click **Save**. Now you can test your app. .. _/getting_started/tutorials/desktop_tutorial/running_your_app: Running your app ---------------- 1. Click the **Run** button in the toolbar to run the project. #. When Task Manager is displayed, you can interact with the buttons by clicking on them, you can type in the TextField and you can resize the window to see the buttons reposition themselves. #. Select **Exit** (on Linux/Windows) or **Quit** (on macOS) from the app's menu to return to the Layout Editor. Of course, Task Manager doesn't do anything yet! For that you need to add some code, which is what you'll do next. .. _/getting_started/tutorials/desktop_tutorial/adding_code: Adding code to your app ----------------------- .. _/getting_started/tutorials/desktop_tutorial/adding_code_to_the_add_button: Adding code to the Add button ***************************** The Add button adds tasks to the list. The code you add to the button needs to take what was typed in TaskField and add it as a new row to the list. Follow these steps to add the code: 1. In the Navigator, click on **TaskManagerWindow** to display it in the Layout Editor. 2. On the window, double-click the **AddButton control**, labeled *Add*. The Add Event Handler window appears. When a button is pressed, the Pressed event handler is called. This means you want to add your code to the Pressed event handler. 3. Make sure the Pressed event is selected in the Event Handler list and click **OK**. The Code Editor is displayed. .. note:: Notice the Navigator updates to show the Pressed event underneath the AddButton control. Now you need to get the task that was typed into the Task field. You might think you could get the task just by referring to the name of the field, TaskField. That is close, but not quite what you want. What you instead need is a property of TaskField. When you need to refer to a property of an object, you use the name of the object, followed by a dot, followed by the name of the property. In other words, you use this syntax: ObjectName.PropertyName. This is something called *dot* notation and is commonly used in object-oriented programming. In this case the object is TaskField and the property you want is **Text** (See the :doc:`DesktopTextField` to find out about all the properties available to TextFields). The syntax looks like this: .. code:: xojo TaskField.Text To actually add a row to a Listbox, you use the *AddRow* method. You just learned how to get the text in the Task field. As you have seen before, objects can have properties. And as you now see with TaskList, objects can also have methods. *AddRow* is one of many methods available to Listboxes and it adds values to the two columns in TaskList. The first column contains the completed status, so it is initially set to blank. The second column contains the name of the task. 4. Enter this code: .. code:: xojo TaskList.AddRow("", TaskField.Text) 5. Save the project by choosing **File > Save**. 6. Run the app to test it out. Type tasks in the task field and click the Add button to see them appear in the task list. 7. Select **Exit** (on Linux/Windows) or **Quit** (on macOS) from the app's menu to return to the Layout Editor. .. note:: If you get an error message when you run your project, double-check the names you have given to the various controls. They need to match the names you are using in your code. .. _/getting_started/tutorials/desktop_tutorial/adding_code_to_the_complete_button: Adding code to the Complete button ********************************** When the user presses the Complete button, the selected task in the Listbox should be marked as completed. This is indicated by showing a checkmark (✓) in the Completed column. Follow these steps to add the code: 1. On the window, double-click the **CompleteButton control**, labelled *Complete*. The Add Event Handler window appears. 2. Make sure the **Pressed** event is selected in the Event Handler list and click **OK**. To change a row, you first need to know what row is selected. In a Listbox, the currently selected row is contained in the SelectedRowIndex property. To set the value in a particular cell of a Listbox, you use the *Cell* property, specifying the row and column. 3. This code puts the checkmark character in column 0 (the completed column) of the currently selected row (just select and copy the checkmark from this tutorial): .. code:: xojo TaskList.CellTextAt(TaskList.SelectedRowIndex, 0) = "✓" 4. Run the app and add a few sample tasks. 5. Click on a task and click the **Complete button**. A checkmark appears in the Completed column. 6. Select **Exit** (on Linux/Windows) or **Quit** (on macOS) from the menu to return to the Code Editor. .. _/getting_started/tutorials/desktop_tutorial/adding_code_to_the_delete_button: Adding code to the Delete button ******************************** The Delete button is used to remove tasks from the list. The code you add to the button needs to determine the selected row in the list and remove it from the list. Follow these steps to add the code: 1. In the Navigator, click on **TaskManagerWindow** to display it in the Layout Editor. 2. On the window, double-click the **DeleteButton control**, labeled *Delete*. The Add Event Handler window appears. 3. Make sure the **Pressed event** is selected in the Event Handler list and click **OK**. Since the selected row will be deleted, you again want to use the *SelectedRowIndex* property. #. Use the Listbox method *RemoveRowAt* to remove a row from the Listbox. You pass **RemoveRowAt** the row number to remove as a parameter. So your code looks like this: .. code:: xojo TaskList.RemoveRowAt(TaskList.SelectedRowIndex) 1. Save the project by choosing **File > Save**. #. Run the app and add a few sample tasks. Now click on a task in the Task List and click the **Delete button**. The task is removed from the list. .. _/getting_started/tutorials/desktop_tutorial/debugging: Finding and fixing bugs *********************** Finding bugs is part of creating apps. A bug is when the app you've created doesn't behave as you intended. Although your Task Manager app works just fine, there are a couple of lingering bugs that need addressing. Have you figured out what the problem is? Here's a hint: What happens if you click on the Complete or Delete buttons but have not selected a task? Try it. 1. With the app still running and no row selected, click on the **Complete button** without doing anything else. Your app will switch to the Debugger with a line of code highlighted. Your code generated an *OutOfBoundsException* and you are now in the debugger. The error occurred because you attempted to remove (or complete) a row that does not exist. When no row is selected in the Listbox, the *SelectedRowIndex* property returns -1. Since this is not a valid row in the Listbox, when it's passed to the *CellTextAt* method, *CellTextAt* cannot use it. As a result, Xojo creates something called an *Exception*. This means that something unusual or *exceptional* has happened. In this case, because the value is out of bounds (given that -1 isn't a valid row number) an *OutOfBoundsException* occurs. #. Click the **Resume button** in the debugger toolbar, to see the actual error message. #. Click the **button** in the dialog to quit the app to return to the Editor. Nobody wants buggy code. Luckily it is easy to prevent this bug from occurring. Essentially, you want to make sure a row is selected before you attempt to Delete or Complete a task. 1. The code to do this uses what you have already learned. Update the code for the **Pressed event handler** of the DeleteButton: .. code:: xojo If TaskList.SelectedRowIndex >= 0 Then TaskList.RemoveRowAt(TaskList.SelectedRowIndex) End If 2. The code for the Complete button is similar. Update the code for the **Pressed event handler** of the Complete button: .. code:: xojo If TaskList.SelectedRowIndex >= 0 Then TaskList.CellTextAt(TaskList.SelectedRowIndex, 0) = "✓" End If 3. In both cases, the code verifies that a row is selected by checking the *SelectedRowIndex* property to ensure that it contains a valid row before the actual method is called. #. Save the project by choosing **File > Save**. #. Run the project again and click the Complete button without selecting a row in the task list. Because the code now only removes the row if a valid row is selected, no OutOfBoundsException occurs. #. When you're done testing, select **Exit** (on Linux/Windows) or **Quit** (on macOS) from the menu to return to the Code Editor. .. _/getting_started/tutorials/desktop_tutorial/finishing_the_app: Finishing your app ------------------ .. _/getting_started/tutorials/desktop_tutorial/testing: Testing your app **************** Just because your app seems to work, doesn't mean you are finished with it. A good developer always thoroughly tests their apps to look for possible problems. You already found and fixed two problems (pressing the Complete and Delete buttons when no row is selected). Do you think there are other problems to fix? Run the app and play around with it a bit. Make a note of things you want to change. In the next section, you will add some improvements to Task Manager. .. _/getting_started/tutorials/desktop_tutorial/improvements: Making improvements ******************* Did you notice that there are times when the buttons in Task Manager probably should not do their action? For example, the Complete button should not try to mark a task as completed if one is not selected. Right now you can click it, but nothing happens. Also, you are not going to want to add a task to the list if nothing has been entered in the task field. There are several ways to accomplish this, but one way is to disable the buttons when they should not be used. Follow these steps to add this improvement: 1. In the Navigator, click on **TaskManagerWindow** to display it in the Layout Editor. 2. On the window, select **CompleteButton**, labeled *Complete*. In the Inspector, turn the **Enabled property** (in the Appearance group) to **Off**. #. Select **AddButton**, labeled *Add*. In the Inspector, turn the **Enabled property** (in the Appearance group) to **Off**. #. Select **DeleteButton**, labeled *Delete*. In the Inspector, turn the **Enabled property** (in the Appearance group) to **Off**. Now you will add code to enable the Add button when there is text in the Task Field. 5. On the window, double-click the **TaskField control**. The Add Event Handler window appears. Here you are seeing yet another list of event handlers. Every control type has its own specific list of event handlers. In this case, we want to disable AddButton when there is no text in the task field and enable it when there is text. The TextChanged event is called whenever the text in the task field is changed, either by the user typing or by your code changing the Text property. 6. Make sure the **TextChanged event** is selected in the Event Handler list and click **OK**. .. note:: Notice the Navigator on the left updates to show the TextChanged event underneath the TaskField control and the code editor displays. You now need code that checks the Text property of the TextField (Me.Text) to see if anything is there. If there is text there, then the AddButton is enabled by setting its Enabled property to :doc:`True`. If there is no text, then it is disabled by setting its Enabled property to :doc:`False`. 7. Enter this code: .. code:: xojo If Me.Text.IsEmpty Then AddButton.Enabled = False Else AddButton.Enabled = True End If You already added code earlier to prevent the Delete and Complete buttons from doing anything if no row is selected in the Task List. Now you can also make those buttons enable when a row is selected and disable when no rows are selected. This is accomplished with the *SelectedRowIndex* property of the Listbox. 8. In the Navigator, click on **TaskManagerWindow** to display it in the Layout Editor. 9. Double-click the **TaskList control**. The Add Event Handler window appears. Here you are seeing the list of event handlers for ListBox. The SelectionChanged event is called whenever the selection in the TaskList control changes. 10. Make sure the **SelectionChanged event** is selected in the Event Handler list and click **OK**. 11. Add this code: .. code:: xojo If Me.SelectedRowIndex >= 0 Then DeleteButton.Enabled = True CompleteButton.Enabled = True Else DeleteButton.Enabled = False CompleteButton.Enabled = False End If 12. Save the project by choosing **File > Save**. 13. Run the app to test it out. 14. When you're done testing, select **Exit** (on Linux/Windows) or **Quit** (on macOS) from the menu to return to the Code Editor. Notice that the Add button is initially disabled. But try typing some text in the Task field. The Add button immediately becomes enabled. And if you remove the text from the Task field, the buttons again become disabled. Similarly, when you click on a row in the Task List, the Delete and Complete buttons become enabled. Let's make the Add button remove the text from the TaskField after it creates a task out of it: 1. Select the **AddButton** in the Navigator to both display the TextManagerWindow in the Layout Editor and select the AddButton at the same time. 2. Next we need to update the Add button's **Pressed** event handler so double-click it to get to the Pressed event. 3. Now you should be staring at the first line of code you wrote. Click at the end of the line and press :kbd:`Return` to create new line. 4. On that brand new line, enter this code to set the **Text** property of the TaskField to an empty text value. .. code:: xojo TaskField.Text = "" 5. Go ahead and save your project. 6. Run your app to test out this newest version. .. _/getting_started/tutorials/desktop_tutorial/deploying_/_building: Building a standalone app ************************* Now that you have created this fine app, you probably want to share it with the world. To do so, you need to build a standalone app. Xojo lets you create desktop apps for macOS, Windows, and Linux (including the Raspberry Pi). The first thing you want to do is to decide which platforms you wish to build. You do this using the BUILD section of the Navigator. First, check the box next to the platforms you want to build. By default, *This Computer* is checked so that you will at least create a build for the platform you are currently using. To see settings specific to each platform, click on the platform name. The *Shared Build Settings* contain the version information and other settings. You can click around on these various build settings to look at them, but you do not need to change any in order to build this app. .. note:: To build a standalone app, you must have already purchased a Xojo license. 1. To build the app, click the **Build button** in the toolbar (or choose **Project > Build Application** from the menu). Xojo creates a standalone app for each selected platform. In the folder containing your project, you will see a folder called *Builds - TutorialDesktop* and inside this folder will be folders for the builds for each platform. Navigate to the build folder for your current platform and double-click the app file to run it. .. _/getting_started/tutorials/desktop_tutorial/next_steps: Next steps ********** Congratulations, you have successfully completed the Desktop Tutorial and now have a fully functional app. To continue on your journey of learning Xojo, you should next read the :doc:`What is Xojo?` topic, which covers Xojo in its entirety. You will also want to refer to the :doc:`API` section of the documentation, which covers the specifics of language elements, classes and other details of Xojo. :download:`Download ` the completed TaskManager project. .. _/getting_started/tutorials/desktop_tutorial/feedback: Feedback -------- What did you think of this tutorial? We'd appreciate your feedback, `write to us `_. ============ Web Tutorial ============ .. _/getting_started/tutorials/web_tutorial/learn_the_basics: Learn the basics ================ Xojo is a cross-platform integrated development environment (IDE) that is made up of a rich set of graphical user interface objects, the modern object-oriented Xojo programming language, an integrated debugger, and a multi-platform compiler. With the IDE, you can build your app's interface by dragging and dropping interface objects onto the app's web pages and dialogs. Once you have completed the `Web Quickstart `_, this tutorial will walk you through creating a more intermediate web app, a Task Manager. This is what the completed app will look like: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/finished_web_tutorial_app.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/task_manager_tutorial: Task Manager tutorial ===================== .. _/getting_started/tutorials/web_tutorial/getting_started: Getting started --------------- 1. Launch Xojo. After it loads, the Project Chooser window appears. If Xojo was already running, choose File > New Project. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/project_chooser_web.png :scale: 50% :align: center Xojo lets you build several different types of apps (Desktop, Web, Console, iOS and Android). For this Tutorial, you are building a web app, so click on Web. You should now see three fields that need values: Application Name, Company Name and Application Identifier. * **Application Name**: the name of your app. This will be the filename of the actual app file that gets created. * **Company Name**: the name of your company. * **Application Identifier**: a unique identifier for this app. It will automatically populate using what you enter for the Application and Company Names, but you can also change it to whatever you want. 2. Enter ``Task Manager`` as the Application Name. 3. You can leave Company Name as it is or change it. 4. Click **Create** to open the main Xojo window (called the Workspace), where you will begin designing your app. .. _/getting_started/tutorials/web_tutorial/the_xojo_project_window: The Xojo Project Window ----------------------- This window has three basic parts: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/web_layout_editor.png :align: center * **Navigator**: The area on the top left shows you all the items in your project. By default you can see WebPage1 (which is selected), the App object and the Session object. Use the Navigator to navigate within your project. * **Editor**: The center area is the Editor Area. Currently it's showing the Layout Editor. You use the Layout Editor to design the user interface for the web pages in your app. It shows the web page and previews how it looks when the app runs in a web browser. In this image, the web page is blank because you haven't yet added any user controls. The Editor Area however can also show other kinds of editors for editing code, icons and other things you might use in a project. * **Library**: The area on the right is the Library and shows the controls and interface elements that you can add to a web page. You design the web page by dragging controls from the Library to it. You can also add a control to the web page by double-clicking it. .. note:: If your Library looks different than the one above, that's because it was set to show icons only. You can customize yours by clicking on the small gear icon near the top-left corner of the Library and then choosing a different setting. If the Library is not visible, click the Library button on the toolbar to show it. **Inspector**: Not shown in the above image is the Inspector, which allows you to see and change the various attributes (the height, width, name and more) of any control you've added to your webpage. This area of the window is shared with the Library. You can show the Inspector by clicking the Inspector button on the toolbar. The Inspector shows information about the selected item in the Navigator or Editor. The contents of the Inspector changes as you click on different items. .. _/getting_started/tutorials/web_tutorial/about_the_app_you_are_about_to_create: About the app you are about to create ------------------------------------- In this tutorial you will create an app to track tasks, called *Task Manager*. When finished, the Task Manager app will let you enter tasks in a field then click the Add button to add them to the Task list. You can click on individual tasks in the list to delete them or mark them as complete. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/finished_web_tutorial_app.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/designing_the_user_interface: Designing the user interface ---------------------------- .. _/getting_started/tutorials/web_tutorial/creating_the_task_list: Creating the task list ********************** WebPage1 is open in the Layout Editor. First, you need to add a TaskList (ListBox) to the web page. 1. In the Library, click on the **ListBox** |listbox_library_icon| and drag it to the top-left corner of the Layout Editor. As you get close to the edges of the web page, you will see alignment guides that help you position the control to leave the right amount of space between the edge of the browser window and the control. .. |listbox_library_icon| image:: images/web_tutorial/listbox_library_icon.png .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/listbox_alignment_guides.png :scale: 50% :align: center 1. When the alignment guides appear, drop the ListBox. 2. Click on the **Listbox** to select it and then use the handles to resize it. Leave about 1/4th of the webpage blank for the other controls you're about to add. Your project should now look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/resized_listbox.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/adding_the_buttons_and_textfield: Adding the buttons and text field ********************************* Now add the Delete and Add buttons. 1. In the Library, click on the **Button** control |button_control_icon| and drag it to the lower-left corner of the web page. .. |button_control_icon| image:: images/web_tutorial/button_control_icon.png 3. Now drag a **Default Button** control |default_button_control_icon| to the lower-right corner of the web page. This button is labeled, "OK" by default but we will change it to "Add" later. .. |default_button_control_icon| image:: images/web_tutorial/default_button_control_icon.png 3. Last but not least, drag a **TextField** control |textfield_control_icon| from the Library and drop it next to the Delete button. .. |textfield_control_icon| image:: images/web_tutorial/textfield_control_icon.png 4. Resize the **Textfield** so that its right side is aligned with the left side of the OK button. After adding all the controls, your web page in the Layout Editor should look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/layout.png :scale: 50% :align: center .. note:: If on your computer the browser frame of the web page looks different from what you see in this tutorial, that's because Xojo draws a frame that is similar to the default browser of the OS upon which you are running Xojo. .. _/getting_started/tutorials/web_tutorial/customizing_controls_with_properties: Customizing controls with properties ------------------------------------ Controls have various values associated with them such as their caption, height, and width. Because they help describe the control, they are called Properties. Changing property values allows you to change the look and behavior of the control. .. _/getting_started/tutorials/web_tutorial/inspector: Changing properties with the Inspector ************************************** The Inspector is used to change web page and control properties. It shares the same area as the Library on the right side of the window. .. _/getting_started/tutorials/web_tutorial/web_page_properties: Changing Webpage properties **************************** You need to change the Name and Title properties of the web page: 1. Click the **Inspector** button on the toolbar. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/inspector_toolbar_button.png 2. In the Layout Editor, click anywhere on the web page where there is no control to select the page itself. The Inspector pane now shows the properties for the web page. #. Change the **Name property** from ``WebPage1`` to ``TaskManagerPage``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Title property** from ``Untitled`` to ``Task Manager``. Press :kbd:`Return` to see the name change in the title bar of the web page. .. note:: The labels the user sees such as the title of a page or the caption of a button, can have spaces. The names of controls, however, are used to refer to them in code and cannot have spaces in their names. If you add a space, Xojo will get confused because controls prefer to have single names like `Elvis `_, `Sting `_ and `Usher `_. .. _/getting_started/tutorials/web_tutorial/listbox_properties: Changing ListBox properties *************************** So the ListBox can properly display Tasks, we need to customize the properties. 1. In the Layout Editor, click on the **ListBox** to select it. The Inspector pane now shows the properties for ListBox. 2. Change the **Name property** from ``ListBox 1`` to ``TaskList``. 3. Since the ListBox needs two columns (Completed and Task), change the **Column Count property** from ``1`` to ``2`` and press :kbd:`Return`. 4. You want to change the column headers to *Completed* and *Task*. In the Inspector, click the **pencil** button to the right of the Initial Value property. This will bring up the Initial Value Editor. 5. In the Initial Value editor type ``Completed``, press :kbd:`Tab` and then type ``Task``. Click **OK** when you're finished editing. 6. Since the Completed column is only going to contain a simple checkmark when the task is marked as completed, it can be narrow. In the **ColumnWidths** field, enter ``150,*`` and press :kbd:`Return`. Using *150,** tells the ListBox that the first column should always be 150 pixels wide and that the rest of the columns share the remaining width. 7. Next you will lock the edges of the ListBox to the edges of the webpage so the ListBox gets larger or smaller as the web page size changes. Click the **locks** so that top, left, bottom and right are all locked. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/listbox_locking_properties.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/changing_buttons: Changing the properties of the buttons ************************************** Next let's add properties to the Delete and Add buttons. .. _/getting_started/tutorials/web_tutorial/the_delete_button: Changing the Delete button properties ************************************* The button in the lower-left corner will become the Delete button. 1. Click on the **button** in the lower-left corner to select it. The Inspector now shows its properties. 2. Change the **Name property** from ``Button1`` to ``DeleteButton`` and press :kbd:`Return`. 3. Change the **Caption property** from ``Button`` to ``Delete``. Now you need to make changes to the locking properties so that the Delete button stays in the lower-left corner of the web page when the web page resizes. 4. In the Locking group, click the **Top lock** to unlock it and the **Bottom lock** to lock it. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/delete_button_locks.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/the_add_button: Changing the Add button properties ********************************** The button in the lower-right corner will become the Add button. It's a default button which means that pressing the Return key will active it. 1. In the Layout Editor, click the **button** in the lower-right corner to select it. 2. Change the **Name property** from ``Button2`` to ``AddButton`` and press :kbd:`Return`. 3. Change the **Caption property** from ``OK`` to ``Add`` and press :kbd:`Return`. Now you need to check the locking properties so the Add button stays in the bottom-corner of the web page when the web page resizes. 4. Click the **locks** so that the right and bottom are locked and top and left are unlocked. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/add_button_locking.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/textfield_properties: Changing the TextField properties ********************************* The TextField is where your user will type the task to add to the list. 1. In the Layout Editor, click on the **TextField** to select it. #. Change the **Name property** from ``TextField1`` to ``TaskField`` and press :kbd:`Return`. #. To allow the **TaskField** to get wider/narrower as the webpage is resized, click the **locks** so that left, bottom and right are locked and top is unlocked. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/textfield_locking.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/final_layout: The final layout **************** After adjusting all the properties for the web page and its controls, your layout should look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/final_layout.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/saving_the_project: Saving the project ------------------ Before we run the app to test it, it's a good idea to save your project so should something go wrong you don't lose your work: 1. Choose **File > Save**. .. note:: This means select **Save** from the **File** menu and is the standard way choosing menu items as notated in the Xojo documentation. 2. Name the project ``Task Manager`` and click **Save**. .. _/getting_started/tutorials/web_tutorial/running_the_app: Running the app --------------- Your user interface layout is now complete, so it's time to try it out. Remember, you haven't added any code yet, so the app doesn't yet do much. 1. Click the **Run** button in the toolbar to run the project. Because this is a web project, it runs in your browser. If your browser wasn't already running, Xojo launches it for you. #. When the Task Manager is displayed, you can interact with the buttons by clicking on them, you can type in the TextField and you can resize the web page to see the buttons and TextField reposition themselves. #. Close the browser tab or window to return to the Layout Editor. Xojo won't know that you closed the browser window or tab for several minutes. #. To stop the app, click the **Stop** button in the Debugger (which we will talk about more later). .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/debugger_stop_button.png :scale: 50% :align: center .. _/getting_started/tutorials/web_tutorial/adding_functionality_with_code: Adding functionality with code ------------------------------ .. _/getting_started/tutorials/web_tutorial/add_button: Adding code to the Add button ***************************** The Add button adds tasks to the list. The code you add to the button needs to take what was typed in TaskField and add it as a new row to the TaskList ListBox. Follow these steps to add the code: 1. In the Layout Editor, double-click the **AddButton** control. The Add Event Handler window appears. When a user presses a button, the Pressed event handler is called. 2. The Pressed Event Handler is already selected so just click **OK** to accept it. This displays the Code Editor. Now you need to get the task that was typed into the TaskField. You might think you could get the task just by using "TaskField", the name of the field. That is close, but not quite enough. The name of the control represents the entire control with all of its properties. What you want is just the text in the TaskField. To do that you type the name of the control, followed by a dot, followed by the name of the property. In other words, you use this syntax: ControlName.PropertyName. This is something called “dot” notation and is commonly used in object-oriented programming. In this case the control is named TaskField and the property you want is Text (use the :doc:`WebTextField` to find out about all the properties available to TextFields). The syntax looks like this: TaskField.Text The ListBox control has a built-in function for adding a row to itself. That function is appropriately called AddRow. Functions like this in Xojo are more generally referred to as methods. The AddRow method can take multiple values allowing you to fill in as many columns as you want with a single call. The first column contains the completed status of each task, so it is initially set to blank. The second column contains the name of the task. 3. Add this code to the **Pressed** event: .. code:: xojo TaskList.AddRow("", TaskField.Text) 4. Run the app to test it out. 5. Type tasks in the TaskField and click the Add button to see them appear in the TaskList ListBox. 6. Close the browser tab or window. 7. The app is still running in Xojo, so click the **Stop** button to return to the Code Editor. .. _/getting_started/tutorials/web_tutorial/marking_tasks_as_complete: Marking tasks as complete ************************* When the user clicks in the Completed column of the TaskList, the selected task should toggle between being displaying a checkmark (✓) and displaying nothing. Let's add code to the TaskList in the same way we added code for the Add button: 1. Click on **TaskManagerPage** in the Navigator. #. Double-click the **TaskList** control on your layout. #. In the Add Event Handler dialog box, select the **Pressed** event and click **OK**. Notice that the name of the event (Pressed) appears at the top of the Code Editor. Along with it are the names of two values (row and column) that are automatically populated when the Pressed event is called. It also indicates they are both integers. Values that are passed into a method or event are called parameters. Like AddRow, the ListBox control has another method that lets you get and set the value in a cell. This method is called CellTextAt and it expects you to tell it which row and column to access. In this case, the Pressed event's row parameter provides us with the row number. Columns in a ListBox are numbered starting at 0 so that's the number of your Completed column in this project. Enter this code for the TaskList's Pressed event. And then I will explain what's going on here. .. code:: xojo If column = 0 Then If Me.CellTextAt(Me.SelectedRowIndex, 0) = "" Then Me.CellTextAt(Me.SelectedRowIndex, 0) = "✓" Else Me.CellTextAt(Me.SelectedRowIndex, 0) = "" End If end If This code uses IF (a conditional operator) to make a decision. On the first line, it checks to see IF the column that was pressed was the first column (column 0 AKA the "Completed" column). If it is, then it continues to the next line that checks to see if column 0 of the selected row is blank. Let's dissect this line a bit further: Me.CellTextAt(Me.SelectedRowIndex, 0) = "" Remember that to call a method, you need to start with the name of the control. A shortcut for this is a function appropriately titled, *Me*. It means the control from which Me was called. This is convenient as well because if you change the name of a control you won't have to update any code in the control's events that refers to itself. Next is CellTextAt, the method already discussed. It takes two parameters: a row and a column. For the column, we are once again using Me and then calling a property of the ListBox called SelectedRowIndex. As you may have already deduced, SelectedRowIndex returns the number of the selected row. That value is then being passed as the first parameter to CellTextAt. The second parameter is 0 because the only column we care about is column 0 (the Completed column). Finally we compare the value from the cell at the intersection of Me.SelectedRowIndex and 0 to "" (which means an empty text value). If it's empty, we use nearly the same code to assign it a checkmark. If it's not, we go to the ELSE part of the code and set it to empty (""). Now if you've never written a line of code before, that may seem like a long explanation for some code that toggles a cell. I can only say that computers can't think for themselves and need a lot of details to get the job done. The good news is that the more code you write, the easier it will get. 1. Since you've already entered the code, run the app and add a few sample tasks. #. Now try toggling the **Completed** column by clicking in that column for one of your tasks. #. When you're done, close the browser tab or window. #. The app is still running in Xojo. Press **Stop** in Xojo. .. _/getting_started/tutorials/web_tutorial/delete_button: Adding code to the Delete button ******************************** The code you add to the Delete button needs to determine which row is selected in the list and remove it. Add code to the Delete button in the same way you added code for the Add button: 1. In the Navigator, click the the **TaskManagerPage** to select it and show the Layout Editor. 2. In the Layout Editor, double click on your **Delete** button to add the Pressed event handler. Once again we will be using a method of the ListBox. This one is appropriately named, *RemoveRowAt*. Since it's removing an entire row, it only needs to know which row is getting the axe. Once again, we will be using the *SelectedRowIndex* method. This time, however, we can't use *Me* because we are calling the method from an event of the Delete button not the ListBox: 3. Add the following code to the **Pressed** event: .. code:: xojo TaskList.RemoveRowAt(TaskList.SelectedRowIndex) 4. Save the project. 5. Run the app and add and delete some tasks to test it out. .. _/getting_started/tutorials/web_tutorial/finding_and_fixing_bugs:_the_fine_art_of_debugging: Finding and fixing bugs: the fine art of debugging -------------------------------------------------- No matter how experienced you get at writing code, you're going to create bugs. .. note:: Remember, computers can only do exactly what we tell them to do. If you tell them to do something, they will do it without complaint. Some bugs are obvious and easy to find, others are are sneaky and love to hide in the dark corners of your projects so you can spend your time looking for them instead of creating new ones. Although your app works just fine, believe it or not there is a bug that needs addressing. Do you know what it is? Here's a hint: What happens if you you click the Delete buttons but have not selected a task? Let's find out! 1. With the app still running, click on the **Delete** button without selecting a task, observe what happens. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/outofbounds_exception.png :scale: 50% :align: center Xojo has switched to its Debugger and has highlighted a line of code where an error occurred. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/debugger_error.png :scale: 50% :align: center You can tell where this error occurred but you can also tell by looking at the Stack pane of the Debugger. Notice that it indicates it's in the DeleteButton's Pressed event: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/debugger_stack.png :scale: 50% :align: center The error occurred because you attempted to remove a row that does not exist. When no row is selected in the ListBox, the *SelectedRowIndex* property returns -1. Since this is not a valid row number, the *CellTextAt* method caused an error. Specifically, the value passed was out of bounds and thus the error is called an *OutOfBoundsException*. It's called an Exception because it's something exceptional that happened. You can tell it's *OutOfBoundsException* by looking at the Debugger's Variables pane: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/web_tutorial/debugger_variables.png :scale: 50% :align: center 2. Switch back to your browser and close the browser tab or window. 3. Switch back to Xojo and click the **Stop** button in the toolbar to return to the Code Editor. Luckily, it is easy to prevent this bug from occurring. We are just going to prevent the user from even pressing the Delete button if they don't have a row selected. That way *SelectedRowIndex* can never be out of bounds. 4. Click the **TaskManagerPage** in the Navigator. 5. Select the **Delete** button and then in the Inspector, turn off the **Enabled** property. Now the Delete button is disabled by default. 6. Now select the **TaskList** to add another event. This time, click the **Add** button |addbutton| in the Layout Editor's toolbar and select Event Handler from its menu. You have to add the new event this way because you've already added the Pressed event so double-clicking on the TaskList would take you there instead of adding a new event. .. |addbutton| image:: images/web_tutorial/layout_editor_add_button.png :scale: 50 % 7. Add the **SelectionChanged** event. 8. In that event, add the following code and then I'll explain, though perhaps you have figured it out on your own: .. code:: xojo If Me.SelectedRowIndex = -1 Then DeleteButton.Enabled = False Else DeleteButton.Enabled = True End If When the user changes which row is selected (which includes selecting no row at all), the *SelectionChanged* event is called. This code checks to see if the *SelectedRowIndex* is -1 which means no row is selected. If that's the case, we set the Enabled property of the button to False or otherwise to :doc:`True`. Your code can't fail if it can't be called in a situation where it could fail. 9. Run your app again and you'll observe that this bug is no longer an issue. 10. When you're done, close the browser tab or window then return to Xojo and click the **Stop** button. .. _/getting_started/tutorials/web_tutorial/finishing_the_app: Finishing the app ----------------- .. _/getting_started/tutorials/web_tutorial/improvements: Making improvements ------------------- Did you notice that the Add button is enabled even when you haven't entered a task? The user probably doesn't want to accidentally enter an empty task. And after you click it, the task you just entered is still in the TaskField. So first, we need to disable the Add button when there's no text in the TaskField and second, unless you're the type to do the same task over and over, we will need to remove the last one from the TaskField after we add it to the TaskList. 1. Click **TaskManagerPage** in the Navigator to get back to the Layout Editor. 2. Since the TaskField is empty by default, Click on the **AddButton** to select it and in the Inspector, turn off the **Enabled** property. 3. Double click on the **TaskField** to add the TextChanged event. 4. Now enter the following code: .. code:: xojo If Me.Text.IsEmpty Then AddButton.Enabled = False Else AddButton.Enabled = True End If Let's make the Add button remove the text from the TaskField after it creates a task out of it: 1. Select the **AddButton** in the Navigator to both display the TextManagerPage in the Layout Editor and select the AddButton at the same time. 2. Next we need to update the Add button's **Pressed** event handler so double-click it to get to the Pressed event. 3. Now you should be staring at the first line of code you wrote. Click at the end of the line and press :kbd:`Return` to create new line. 4. On that brand new line, enter this code to set the **Text** property of the TaskField to an empty text value. .. code:: xojo TaskField.Text = "" 5. Go ahead and save your project. 6. Run your app to test out this newest version. .. note:: Since the TaskField will be empty after the user clicks the Add button, if would make sense for it to be disabled once clicked. You can do this easily by setting its AllowAutoDisable property to True in the Inspector. .. _/getting_started/tutorials/web_tutorial/deploying_the_app: Deploying the app ----------------- There are several ways to deploy a Xojo web app. The easiest way is to use `Xojo Cloud `_ so that you can just click "Deploy" on the main toolbar to send your app to a web server where it can be used. You can also deploy your app yourself as a *standalone application* that is a self-contained app and web server which can be run on any computer or server. Users can access the app by using the computer's URL and port number. To learn more about these options, refer to :doc:`Web App Deployment Overview` in the Topics section. .. _/getting_started/tutorials/web_tutorial/next_steps: Next steps ---------- Congratulations, you have successfully completed the Web Tutorial and now have a fully functional web app. Watch `this video tutorial `_ to learn how to connect this app to a SQLite database. To continue on your journey of learning Xojo, you should next move on to the Topics and API sections of the documention, which covers Xojo in its entirety. Go to the documentation when you run into trouble, check out the example projects included with Xojo and hop on over to the `Xojo Community Forum `_ and start asking questions. There are lots of experienced Xojo users eager to help. :download:`Download ` the completed TaskManager project. .. _/getting_started/tutorials/web_tutorial/feedback: Feedback -------- What did you think of this tutorial? We'd appreciate your feedback, `write to us `_. ============ iOS Tutorial ============ This iOS Tutorial is an introduction to the Xojo development environment and will lead you through the development of a real iOS app. It should take you 30 to 45 minutes to complete this tutorial. We'll walk you through building a Task Manager app from start to finish. If you are new to programming and to Xojo, we recommend you first complete the :doc:`iOS QuickStart`. .. _/getting_started/tutorials/ios_tutorial/ios_development_requirements: iOS development requirements ---------------------------- A Mac is required to develop iOS projects with Xojo. To run/test/debug an iOS project you create in Xojo requires the iOS Simulator which is provided with Apple's Xcode development tool. You can `download Xcode `_ for free from the Mac App Store. After you have downloaded and installed Xcode, you need to run it one time to accept its License Agreement. After doing that, you can quit Xcode and will never have to use it again as Xojo will automatically launch the iOS Simulator for you. If you have not yet downloaded and installed Xcode, do that now before continuing with the tutorial. .. _/getting_started/tutorials/ios_tutorial/getting_started: Getting started --------------- 1. Launch Xojo. After it finishes loading, the Project Chooser window appears. If Xojo is already running, choose File > New Project. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/project_chooser.png :scale: 50% :align: center Xojo lets you build several different types of apps (Desktop, Web, Console and iOS). 2. For this Tutorial, you are building an iOS app, so click on **iOS**. You should now see three fields that need values: Application Name, Company Name and Application Identifier. * **Application Name**: the name of your app. This will be the filename of the actual app file that gets created. * **Company Name**: the name of your company. * **Application Identifier**: a unique identifier for this app. It will automatically populate using what you enter for the Application and Company Names, but you can also change it to whatever you want. 3. Enter ``TaskManager`` as the Application Name. 4. Click **Create** to open the main Xojo window (called the Workspace) where you will begin designing your app. .. _/getting_started/tutorials/ios_tutorial/workspace: The Workspace ------------- Xojo opens the Workspace with the default View for your app selected in the Navigator and displayed in the Layout Editor. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/workspace.png :align: center * **Navigator**: The area on the top left shows you all the items in your project. By default you can see Screen1 (which is selected), the App object, the iPhone and iPad Layout objects and items for the App Icon and optional Launch Screen. You use the Navigator to navigate within your project. * **Layout Editor**: The center area is the Layout Editor. You use the Layout Editor to design the user interface for the Screens in your app. It shows the Screen and previews how it looks when the app runs on an iOS device. In this image, the Screen is blank because you haven't yet added any user interface controls from the Library. * **Library**: The area on the right is the Library and shows the controls and interface elements that you can add to a Screen or to the project. You design the Screen by dragging controls from the Library to the Screen layout. You can also add a control to the Screen by double-clicking it. You can change how the controls display in the Library by clicking the small gear icon and choosing a different setting. If the Library is not visible, click the Library button on the toolbar to show it. **Inspector**: Not shown in the above image is the Inspector, which allows you to see and change the properties for the selected control. This area of the Workspace window is shared with the Library. You can show the Inspector by clicking the Inspector button on the toolbar. The Inspector shows information about the selected item in the Navigator or Editor. The contents of the Inspector changes as you click on different items. You change an Inspector value by entering a new value in the field to the right of the field label. .. _/getting_started/tutorials/ios_tutorial/about_the_app: About the app ------------- In this tutorial you will create an app to manage tasks. Not surprisingly it is called Task Manager. When finished, the app will let you add tasks, mark them as completed (and unmark them if it turns out they are not) and delete them. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/task_manager.png :scale: 50% :align: center .. _/getting_started/tutorials/ios_tutorial/task_manager: Task manager ************ For the Task manager app, you enter tasks in the text field and tap Add to add them to the table. You can tap on individual tasks in the table to delete them or mark them as complete. Task Manager uses these controls: .. csv-table:: :header: "Icon", "Name", "Description" :widths: 10, 10, 50 .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/textfield_icon.png,"TextField","A TextField control is used to enter text. In this project, the task to add is entered into a TextField at the top of the Screen." .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/button_icon.png,"Button","A Button is used to trigger an action. This project uses a Button to add a task to the table." .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/table_icon.png,"Table","A Table is used to display a list of data. In this project, it is what displays the tasks entered in the TextField." .. _/getting_started/tutorials/ios_tutorial/designing_the_user_interface: Designing the user interface ---------------------------- Xojo is displaying Screen1 open in the Layout Editor. You are now going to add the controls to the Screen and position them. .. _/getting_started/tutorials/ios_tutorial/button: Adding a button *************** First add the Button that is used to add a task to the table. 1. In the Library, click on **Button** and drag it to the Screen so that it is at the top right just below the Navigation Bar. #. Use the alignment guides to position it with the appropriate amount of space between the header line and the right edge of the Screen. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/button_with_alignment_guides.png :scale: 50% :align: center .. _/getting_started/tutorials/ios_tutorial/textfield: Adding a text field ******************* The TextField is where the user types the Task to add to the list. 1. In the Library, click on **TextField** and drag it to the Screen so that it is at the top left just below the Navigation Bar. As you drag you will notice alignment guides that will help you align the TextField with the Button as pictured below. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/textfield_with_alignment_guides.png :scale: 50% :align: center 2. Grab the right handle of the **TextField** and drag it to make the control wider until you reach the button's left alignment guide. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/textfield_resizing.png :scale: 50% :align: center .. _/getting_started/tutorials/ios_tutorial/table: Adding a table ************** The Table displays the tasks. 1. In the Library, click on the **Table** and drag it to the center of the Layout Editor below the Button and TextField. As you drag the Table onto the Screen, you will see alignment indicators that help you position the control. Drop the Table when you are happy with its position on the Screen. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/table_with_alignment_guides.png :scale: 50% :align: center 2. Grab the bottom handle of the **Table** and drag it downward to make it taller. Stop dragging when the bottom edge is right up against the bottom of the Screen: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/table_aligned_bottom.png :scale: 50% :align: center .. _/getting_started/tutorials/ios_tutorial/all_controls_positioned: The screen layout at this point ******************************* After adding all the controls, your Screen should now look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/initial_layout.png :scale: 50% :align: center .. _/getting_started/tutorials/ios_tutorial/properties: Understanding properties ------------------------ .. _/getting_started/tutorials/ios_tutorial/what_is_a_property?: What is a property? ******************* A property is a value of a class. Changing property values allows you to change the behavior of an object such as a screen or control. For this project, you want to change various properties for the Screen and the controls you added. Some of the things you need to do are: * Rename all controls (and the Screen) so that they describe their behavior and are easy to refer to in code. * Add a Caption to the Button. * Add a hint to the TextField to help the user know what to enter. .. _/getting_started/tutorials/ios_tutorial/inspector: Using the inspector ******************* The Inspector is used to change screen and control properties. It shares the same area on the right of the Workspace as the Library. 1. In order to show the Inspector, click the **Inspector button** on the toolbar or press :kbd:`⌘-I`. .. _/getting_started/tutorials/ios_tutorial/screen_properties: Changing screen properties ************************** You need to change the Name and Title properties of Screen1: 1. In the Layout Editor click anywhere on the background (or phone UI) to select the Screen (rather than a control). The Inspector now shows the properties of the Screen. #. Change the **Name property** from ``Screen1`` to ``TaskManagerScreen``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Title property** from ``Untitled`` to ``Task Manager``. Press :kbd:`Return` to see the name change in the Navigation Bar of the screen. .. _/getting_started/tutorials/ios_tutorial/button_properties: Changing button properties ************************** For the button you'll change the Name and Caption properties. 1. Click the **Button** to select it. The Inspector now shows its properties. #. In the Inspector, change the **Name property** from ``Button1`` to ``AddButton``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Caption property** from ``Button`` to ``Add``. .. _/getting_started/tutorials/ios_tutorial/textfield_properties: Changing textfield properties ***************************** The TextField is where the user types the task to add to the Table. You'll be changing the Name, Text and Hint properties. 1. Click on the **TextField** to select it so that its properties are displayed in the Inspector. #. Change the **Name property** from ``TextField1`` to ``TaskField``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Hint property** to ``Enter a task``. #. To make it easier for the user to type, switch on the **Allow Spell Checking** and **Allow Auto Correction** properties. .. _/getting_started/tutorials/ios_tutorial/table_properties: Changing table properties ************************* The table displays the tasks and will also show a check mark next to ones marked as completed. Let's change the Name property to something that is more descriptive. 1. Click on the **table** to select it and display its properties in the Inspector. #. Change the **Name property** from ``Table1`` to ``TaskTable``. Press :kbd:`Return` to see the name change in the Navigator. .. note:: In the Auto-Layout section of the Inspector, look at the first row. The Edge column has the value *Bottom*. The Rule column should have the value *BottomLayoutGuide.Bottom*. If it doesn't, resize the Table control using its bottom handle until that value changes to BottomLayoutGuide.Bottom. This will ensure that the bottom of the table is locked to the bottom of the layout. .. _/getting_started/tutorials/ios_tutorial/final_layout: The final layout **************** This is what your layout should now look like after adjusting all the properties: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/ios_tutorial/final_layout.png :scale: 50% :align: center .. _/getting_started/tutorials/ios_tutorial/running_the_app: Running the app --------------- Your user interface layout is now complete, so it's time to try it out. But before you go further, you should first save your work. 1. Save the project by selecting **File > Save**. .. note:: This means select **Save** from the **File** menu and is the standard way choosing menu items as notated in the Xojo documentation. 1. Name the project ``iOSTaskManager`` and click **Save**. To test the app you use the iOS Simulator. To get the iOS Simulator from Apple you'll have to first download, install and run Xcode once so it installs its components. You do not need to keep Xcode running or otherwise use it when developing with Xojo. To learn more about Xcode and the iOS Simulator, refer to the :doc:`Installing Xcode and Apple Certificates` and iOS Simulator topics. .. note:: If the iOS Simulator window is quite small the first time you run your app, in the iOS Simulator choose Physical from the Window menu. That will permanently resolve the issue. Now you can test your app: 1. Click the **Run** button in the toolbar to run the project. This builds your app and runs it in the iOS Simulator. #. When your app appears in the iOS Simulator, you can interact with the buttons by clicking on them or you can type in the TextField. #. When you are finished, switch back to Xojo and click the **Stop** button. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/stop_button.png :scale: 50% :align: center Of course, Task Manager doesn't do anything yet! For that you need to add some code, which is what you'll do next. .. note:: If you want to change the type of iOS device that the iOS Simulator runs your project in, choose Project > Run On then choose another iOS device from the list. You can even run directly on your iPhone or iPad by plugging it into your Mac. .. _/getting_started/tutorials/ios_tutorial/adding_code: Adding code ----------- To complete the Task Manager, you will need to: * Make the Add Button add a task. * Make the Add Button only be enabled after the user has entered text into the TaskField. * Allow the user to swipe left to mark a task as completed. * Allow the user to swipe left to delete a row. .. _/getting_started/tutorials/ios_tutorial/coding_the_add_button: Coding the Add button ********************* First, you're going to need to create something in which to store the tasks. Normally you could just add tasks as rows directly to the table. However, because you will want the user to be able to swipe left to mark and delete tasks, you will need to store the data elsewhere. You can do this by adding a property to the Screen. This property will be an array which is a list. Since you need to store both the name of the task and whether or not it's been completed, the array will be an array of Pairs. A :doc:`Pair` in Xojo is two values that go together. The left side of the pair will store the task and the right side will store :doc:`True` (if the task has been completed) and :doc:`False` if it has not. To help you visualize the array, consider the list below with three tasks, the first one which is completed: .. csv-table:: :header: "Left", "Right" :widths: 25, 25 "Wash the dog","True" "Mow the lawn","False" "Clean the garage","False" Let's create the array to store the tasks: 1. To add a new property, select **Insert > Property**. A new property is added to the TaskManagerScreen. #. In the Inspector, enter ``Tasks()`` as the name. The () makes this an array. #. In the Inspector, enter ``Pair`` for the Type. If you've already done the QuickStart then you know that Events are where you put code that needs to respond to an action such as a user pressing a button. Let's now make the Add button add the task to the Tasks array: 1. In the Navigator, click on the **AddButton** to select it. #. In the Layout Editor, double-click the **Add** button. #. When the Add Event Handler dialog appears, the Pressed event will already be selected which is the one you want so click **OK** to add it. You are now in the Code Editor editing the AddButton's Pressed event. When the user presses the button, the button should add a row to the Tasks array using the text the user entered in the TaskField, store [False] to indicate the task is not yet completed, and clear the contents of the TaskField so they can enter another task. 4. In the Code Editor, enter the following three lines of code: .. code:: xojo Tasks.Add(TaskField.Text : False) TaskField.Text = "" TaskTable.ReloadDataSource .. note:: To save yourself having to do a lot of extra typing, remember that when you see an ellipsis (...) in the Code Editor, pressing the Tab key on your keyboard will auto-complete the item for you. Arrays have functions built into them, one of which is for adding items. The first line adds the Text from the TaskField and the value :doc:`False` as a pair to the Tasks array. It's the : that makes them a pair. The second line clears the contents of the TaskField so the user can enter another task. The third and final line tells the TaskTable to reload all of its data so the user will see the new task they just entered. The last step to making the Add button work is to connect the Tasks array to the TaskTable control. You will do this by making the TaskManagerScreen the source of the data for the TaskTable so that its Tasks array can provide that data. You will want to do this as soon as the TaskManagerScreen opens. Thus you will use its Opening event: 1. In the Navigator, click on the **TaskManagerScreen** to select it. #. Select **Insert > Event Handler**. The Add Event Handler dialog appears. Since the TaskManagerScreen is selected, the events that appear are for it. #. The Opening event is selected by default so go ahead and press **OK** to add it. #. Enter the following line of code to make the TaskManagerScreen the TaskTable's datasource: .. code:: xojo TaskTable.DataSource = Self The Self function is a generic way of referring to the object item whose method is being called. Because this is the TaskManagerScreen's Opening event, Self refers to the TaskManagerScreen. Next you need to add some *methods* that will connect the Tasks array to the TaskTable's datasource. This will make it possible for tasks you add to appear in the table. Methods are groups of one or more lines of code that perform a function. If you've written code in other languages, you may have called these subroutines. Normally you can name methods whatever makes sense to you. In this particular case, the datasource has specific method names it will be looking for. A group of methods that allow one thing to interface with another thing is appropriately called an *Interface*. You will be adding the iOSMobileTableDataSource interface which will create methods that will allow the app to get data from the Tasks array: 1. In the Navigator, click on the **TaskManagerScreen** to select it. #. In the Inspector, click on the **Choose...** button next to Interfaces. 'The Interfaces dialog box appears. #. Click the checkbox next to **iOSMobileTableDataSource**. #. Click **OK** to add the interface. Notice that a new list titled "Methods" has appeared in the Navigator. It has four methods: RowCount, RowData, SectionCount and SectionTitle. The TaskTable data source knows these method names and will call them when it needs data from the Tasks array. Your next task (no pun intended) is to enter the code into 3 of these methods that will make them do their jobs: 1. In the Navigator, click on the **RowCount** method. When the TaskTable needs to know how many rows there are in the array, it will call this method. #. The line of text that appears is a comment. Click at the end of it then press :kbd:`Return` to create a new line. #. Enter the following code: .. code:: xojo Return Tasks.Count The Return command sends the data right back to whatever called the method. In addition to the built-in Add method you used earlier, there is also a Count function that, as you have already guessed, returns a count of the number of items in the array. Next, you'll add code to the RowData method to return the task from the array the user selected in the table: 1. In the Navigator, click on the **RowData method** to select it. #. It too has a comment so click at the end of the comment then press :kbd:`Return` to create a new line. #. Enter the following code: .. code:: xojo Var cell As MobileTableCellData cell = table.CreateCell(Tasks(row).Left) Return cell The first line creates a new variable titled "cell" of type MobileTableCellData. A cell in a table will hold one task. The second uses the *table* variable that was passed from the TaskTable to the RowData method. You can see this at the top of the Code Editor just above the comment. The Table variable represents the TaskTable in this case. It has a built-in method called CreateCell, which as the name implies, creates a cell. It's expecting the text you want in the cell. Remember, the text of the task is stored in the left property of each :doc:`Pair` in the Tasks array. Fortunately, the RowData method also is passed the row number the user tapped on in the *row* variable. It too can be seen at the top of the Code Editor. This is then passed to the Tasks array so that it can get the corresponding row from the array. Next use the Left property of the pair to get the name of the task which ultimately is what will be passed to the CreateCell method so that it can create a cell with that task in it. Last but not least, you again use the Return command to return the cell you created to the TaskTable. The last step before you can add tasks is a really easy one. Because Tables can have multiple sections, the TaskTable will want to know how many there are. Since our TaskTable will only have one, you can return 1: 1. In the Navigator, click on the **SectionCount method**. #. Click to the right of the comment then press :kbd:`Return` to to create a new line. #. Enter the following code: .. code:: xojo Return 1 Great. Let's give it a try. 1. Click the **Run button** in the main toolbar. .. note:: If you mistyped something, instead of the iOS Simulator running, an errors pane will appear in the lower half of the Xojo Workspace window. Should this happen, look at the lines the error messages are indicating and compare them to the code in the instructions above. You probably just mistyped something. If you can't figure it out, go to the Xojo `forum `_ or contact us via `email `_. Your app should now be running again in the iOS Simulator. 2. Type a task in the TaskField and press the **Add button** to add it to the table. Enter a few tasks if you'd like. 3. When you're done, return to Xojo and press the **Stop button** you used last time. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/stop_button.png :scale: 50% :align: center .. _/getting_started/tutorials/ios_tutorial/enabling/disabling_the_add_button: Enabling/disabling the Add button ********************************* You wouldn't want the user to accidentally add a blank task. To avoid this you'll need to disable the Add button then enable it when the user has entered text in the TaskField, disable it if they clear the TaskField and disable it after the user presses the Add button since this clears the TaskField as well. 1. In the Navigator, click on the **TaskManagerScreen** to select it. #. Click on the **AddButton** button on the layout. #. In the Inspector, turn off the AddButton's **Enabled** property. Notice the button now appears disabled. #. In the Layout Editor, double-click the **TaskField** and add the TextChanged event handler. #. Add the following code to the **TextChanged** event: .. code:: xojo If Me.Text.IsEmpty Then AddButton.Enabled = False Else AddButton.Enabled = True End If This code checks the Text property of the TaskField to see if it's blank. *Me* in this case refers to the TaskField since this event is part of that control. If the Text property is blank, the AddButton's Enabled property is set to :doc:`False`. If it's not blank, it's set to :doc:`True`. Since the Add button clears the TaskField, you need to add a line of code to its Pressed event to disable it after the user presses it: 1. In the Navigator, click on the **TaskManagerScreen** to select it. #. In the layout Editor, double-click on the **AddButton** to go to its Pressed event handler. #. In the **Pressed** event, add the following line of code to disable the AddButton: .. code:: xojo Me.Enabled = False The last step is to make it possible for the user to swipe left to mark a task as complete or incomplete and delete it if they wish. Doing this will require: * Adding another interface to allow row editing * Adding the Complete/Incomplete and Delete buttons * Making the buttons perform their functions Let's do it! .. _/getting_started/tutorials/ios_tutorial/allowing_row_editing: Allowing row editing ******************** 1. In the Navigator, click on the **TaskManagerScreen** to select it. #. In the Inspector, click the **Choose...** button next to Interfaces. #. In the Interfaces dialog box, click the checkbox for **iOSMobileTableDataSourceEditing**. #. Click **OK** to add the interface to the TaskManagerScreen. #. In the AllowRowEditing method that was just added, add the following code: .. code:: xojo Return True Returning :doc:`True` in this method tells the TaskTable that the user can edit rows. .. _/getting_started/tutorials/ios_tutorial/adding_buttons_to_a_table_row: Adding buttons to a table row ***************************** Now let's add the Complete/Incomplete and Delete buttons. To do that you need to add the ApplyActionsForRow event to the TaskTable: 1. Click on the **TaskManagerScreen** to select it. #. Double-click on the **TaskTable** in the Layout Editor to add an event. #. In the Add Event Dialog Box, choose the **ApplyActionsForRow** event handler. #. Click the **OK** button to add it to the TaskTable. #. Add the following code to the event: .. code:: xojo Var actions(1) As iOSMobileTableRowAction If Tasks(row).Right Then actions(0) = New iOSMobileTableRowAction(iOSMobileTableRowAction.Styles.Normal, "Incomplete", "Incomplete") Else actions(0) = New iOSMobileTableRowAction(iOSMobileTableRowAction.Styles.Normal, "Completed", "Completed") End If actions(1) = New iOSMobileTableRowAction(iOSMobileTableRowAction.Styles.Destructive, "Delete", "Delete") Return actions So what's going on in this code? The first line creates an array. The size is 1 which creates two items because array numbering begins at 0 rather than 1. The type of each item in the array is iOSMobileTableRowAction. This is an object that represents an action the user can take on a row such as pressing a button. The second line passes the *row* parameter (which you can see at the top of the Code Editor was passed in to this event) which holds the row number upon which the user tapped. That gets passed in to the Tasks array to get the selected task. You then check the Right property of the Pair. If it's :doc:`True`, that means the task is marked as completed so you want to add the Incomplete button on line 3 allowing the user to remove the checkmark. If it's :doc:`False`, the task is incomplete so you go to line 5 and add the Completed button so the user can tap that to mark it completed. The next line adds the Delete button and finally return the actions array to the TaskTable so it can add them to the row the user swiped left on. Each of these buttons is created using the :doc:`iOSMobileTableRowAction` class. With it you indicate the style of button (*Normal* provides a gray background while *Destructive* provides a red one to remind the user that this action will result in data loss). You also pass in the label you want on the button and the action tag you want passed when the user taps the button so you know which action they wish to take. You may be wondering why you need both a button name and action tag when they are the same. Your app might one day be localized into another language in which case the button name would be localized because it's seen by the user but the action tag would not be because only your code uses that. If you'd like, run your app now and you'll see that you can swipe left to make the Complete and Delete buttons appear. To swipe in the iOS Simulator, press and drag left with the mouse. The buttons don't do anything yet. That's the next step. When you're done, return to Xojo, click the Stop button and get ready for some more coding! .. _/getting_started/tutorials/ios_tutorial/making_the_buttons_work: Making the buttons work *********************** Now you need to add the RowActionSelected event to the TaskTable. This event will be called when the user taps on a button after swiping left on a row: 1. In the Navigator, click on the **TaskTable** to select it. #. Select **Insert > Event Handler**. The Add Event Handler dialog box appears. #. Select the **RowActionSelected** event. #. Press **OK** to add the event to the TaskTable. #. Add the following code to the **RowActionSelected** event handler: .. code:: xojo Select Case actionTag Case "Delete" Tasks.RemoveAt(row) Me.ReloadDataSource Case "Completed" Tasks(row) = Tasks(row).Left : True TaskTable.ReloadRow(section, row) Case "Incomplete" Tasks(row) = Tasks(row).Left : False TaskTable.ReloadRow(section, row) End Select When this event is called, the action tag of the action the user chose will be passed in via the actionTag parameter. This code uses a Select Case statement to deal with each possibility. If the user tapped the *Delete* button, RemoveAt (yet another built-in array method) is called and passed the row number the user tapped on so that it can delete the row from the Tasks array. Then ReloadDataSource is called to cause the TaskTable to update itself with the data from the Tasks array. ReloadDataSource is a built-in method of the table control. If the user tapped the *Completed* button, the current row of the Tasks array is replaced with the task name it already has and the value :doc:`True` to indicate the task was completed. ReloadRow (another built-in method of the table control) is called to tell the TaskTable to update that row from the Tasks array. If the user tapped the Incomplete button, the exact same thing happens as happened if they clicked the *Completed* button with the important difference that the value set for the task is :doc:`False` instead of :doc:`True`. In these last two cases, because ReloadRow is called, the RowData method will be called to provide the row data to the TaskTable. That means you have one thing left to do: make the checkmark appear or disappear when the task is completed or marked incomplete. 1. In the Navigator, click on the **RowData** method to select it. You may need to expand the Methods list to see it. #. In the Code Editor, click at the end of the line that calls **CreateCell** and press :kbd:`Return` to create an empty line **between** that line and the Return Cell line. #. Insert the following lines of code between those lines: .. code:: xojo If Tasks(row).Right Then cell.AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark Else cell.AccessoryType = MobileTableCellData.AccessoryTypes.None End If Your updated version of the RowData method should now look like this: .. code:: xojo Var Cell As MobileTableCellData cell = table.CreateCell(Tasks(row).Left) If Tasks(row).Right Then cell.AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark Else cell.AccessoryType = MobileTableCellData.AccessoryTypes.None End If Return cell As you have already guessed, *row* is passed to this event from the table and is the row the user tapped on. If the Right value of the task Pair is :doc:`True`, the cell's accessory type (one of several widgets that can appear on the right side of a row) will be set to a checkmark. If it's :doc:`False`, it will be set to nothing. Believe it or not, that's it. Your app is done. 4. Run it in the iOS Simulator and try it out. .. note:: Xojo also supports test running and debugging your iOS apps directly on your iPhone or iPad. .. _/getting_started/tutorials/ios_tutorial/deploying: Deploying your app ****************** There are several ways to deploy a Xojo iOS app. You can build your app with Xojo and manually copy it to an iOS device using Xcode. Or you can build your app with Xojo and submit it to the App Store. Both of these options require you to create certificates, IDs and distributions profiles with Apple and use Xcode to install them. You can find more information here: * :doc:`On-Device Testing During Development` * :doc:`iOS App Store` .. _/getting_started/tutorials/ios_tutorial/next_steps: Next steps ---------- Congratulations, you have successfully completed the iOS Tutorial and now have a fully functional app. You learned a lot in this tutorial. You may be wondering how you'll remember it all. The good news is that you don't have to remember it. Xojo comes with documentation that will help you every step of the way. The Topics section will give you a general overview of the different parts of Xojo and of all the various controls and things you can use in your projects. The API section will give you the all the details for specific controls. After you feel you've reviewed enough of the documentation, choose a small project to build that will benefit you or others. The best way to learn is to have a reason to learn. A small project will provide that. When you need extra help, contact us at hello@xojo.com or go to our `helpful community forum `_. :download:`Download ` the completed TaskManager project. .. _/getting_started/tutorials/ios_tutorial/feedback: Feedback -------- What did you think of this tutorial? We'd appreciate your feedback, `write to us `_. ================ Android Tutorial ================ This Android Tutorial is an introduction to the Xojo development environment and will lead you through the development of a real Android app. It should take you 30 to 45 minutes to complete this tutorial. We'll walk you through building a Task Manager app from start to finish. If you are new to programming and to Xojo, we recommend you first complete the :doc:`Android QuickStart`. We have a `video walk-through `_ available as well. .. _/getting_started/tutorials/android_tutorial/ios_development_requirements: Android development requirements ---------------------------- To run, test and debug an Android project, Xojo needs access to the Android emulator that is part of Android Studio. If you have not yet downloaded and installed Android Studio yet, :doc:`do that now` before continuing with the tutorial. .. _/getting_started/tutorials/android_tutorial/getting_started: Getting started --------------- 1. Launch Xojo. After it finishes loading, the Project Chooser window appears. If Xojo is already running, choose File > New Project. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/project_chooser.png :scale: 25% :align: center Xojo lets you build several different types of apps (Desktop, Web, Console, iOS and Android). 2. For this Tutorial, you are building an Android app, so click on **Android**. You should now see three fields that need values: Application Name, Company Name and Application Identifier. * **Application Name**: the name of your app. This will be the filename of the actual app file that gets created. * **Company Name**: the name of your company. * **Application Identifier**: a unique identifier for this app. It will automatically populate using what you enter for the Application and Company Names, but you can also change it to whatever you want. 3. Enter ``TaskManager`` as the Application Name. 4. Click **Create** to open the main Xojo window (called the Workspace) where you will begin designing your app. .. _/getting_started/tutorials/android_tutorial/workspace: The Workspace ------------- Xojo opens the Workspace with the default View for your app selected in the Navigator and displayed in the Layout Editor. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/workspace.png :scale: 35% :align: center * **Navigator**: The area on the top left shows you all the items in your project. By default you can see Screen1 (which is selected), the App object, and the App Icon. You use the Navigator to navigate within your project. * **Layout Editor**: The center area is the Layout Editor. You use the Layout Editor to design the user interface for the Screens in your app. It shows the Screen and previews how it looks when the app runs on an Android device. In this image, the Screen is blank because you haven't yet added any user interface controls from the Library. * **Library**: The area on the right is the Library and shows the controls and interface elements that you can add to a Screen or to the project. You design the Screen by dragging controls from the Library to the Screen layout. You can also add a control to the Screen by double-clicking it. You can change how the controls display in the Library by clicking the small gear icon and choosing a different setting. If the Library is not visible, click the Library button on the toolbar to show it. **Inspector**: Not shown in the above image is the Inspector, which allows you to see and change the properties for the selected control. This area of the Workspace window is shared with the Library. You can show the Inspector by clicking the Inspector button on the toolbar. The Inspector shows information about the selected item in the Navigator or Editor. The contents of the Inspector change as you click on different items. You change an Inspector value by entering a new value in the field to the right of the field label. .. _/getting_started/tutorials/android_tutorial/about_the_app: About the app ------------- In this tutorial you will create an app to manage tasks. Not surprisingly it is called Task Manager. When finished, the app will let you add tasks, edit them, mark them as completed (and unmark them if it turns out they are not) and delete them. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/task_manager.png :scale: 30% :align: center .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/task_manager_taskeditor.png :scale: 30% :align: center .. _/getting_started/tutorials/android_tutorial/task_manager: Task manager ************ For the Task manager app, you enter tasks in the text field and tap Add to add them to the table. You can tap on individual tasks in the table to delete them or mark them as complete. Task Manager uses these controls: .. csv-table:: :header: "Icon", "Name", "Description" :widths: 10, 10, 50 .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/textfield_icon.png,"TextField","A TextField control is used to enter text. In this project, the task to add is entered into a TextField at the top of the Screen." .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/button_icon.png,"Button","A Button is used to trigger an action. This project uses a Button to add a task to the table." .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/table_icon.png,"Table","A Table is used to display a list of data. In this project, it is what displays the tasks entered in the TextField." .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/label_icon.png, "Label", "A Label is used to identify a user interface element or provide more information. In this project, it will identify the control you use to mark a task as completed." .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/switch_icon.png, "Switch", "A Switch is used to indicate a value in one of two states such as on and off. In this project, it will indicate if the task is completed or not." .. _/getting_started/tutorials/android_tutorial/designing_the_user_interface: Designing the user interface ---------------------------- Xojo is displaying Screen1 open in the Layout Editor. You are now going to add the controls to the Screen and position them. .. _/getting_started/tutorials/android_tutorial/button: Adding a button *************** First add the Button that is used to add a task to the table. 1. In the Library, click on **Button** and drag it to the Screen so that it is at the top right just below the Navigation Bar. #. Use the alignment guides to position it with the appropriate amount of space between the two blue guide lines. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/button_with_alignment_guides.png :scale: 50% :align: center .. _/getting_started/tutorials/android_tutorial/textfield: Adding a text field ******************* The TextField is where the user types the Task to add to the list. 1. In the Library, click on **TextField** and drag it to the Screen so that it is at the top left just below the Navigation Bar. As you drag you will notice alignment guides that will help you align the TextField with the Button as pictured below. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/textfield_with_alignment_guides.png :scale: 50% :align: center 2. Grab the right handle of the **TextField** and drag it to make the control wider until you reach the button's left alignment guide. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/textfield_resizing.png :scale: 50% :align: center .. _/getting_started/tutorials/android_tutorial/table: Adding a table ************** The Table displays the tasks. 1. In the Library, click on the **Table** and drag it to the center of the Layout Editor below the Button and TextField. As you drag the Table onto the Screen, you will see alignment indicators that help you position the control. Drop the Table when you are happy with its position on the Screen. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/table_with_alignment_guides.png :scale: 50% :align: center 2. Grab the bottom handle of the **Table** and drag it downward to make it taller. Stop dragging when the bottom edge is right up against the bottom of the Screen: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/table_aligned_bottom.png :scale: 50% :align: center .. _/getting_started/tutorials/android_tutorial/the_screen_layout_at_this_point: The screen layout at this point ******************************* After adding all the controls, your Screen should now look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/initial_layout.png :scale: 30% :align: center .. _/getting_started/tutorials/android_tutorial/handling_different_screen_sizes: Handling different screen sizes ******************************* Android devices come in a variety of different sizes. You will want your user interface to look good on all of them. Additionally, the user may rotate their device from portrait to landscape orientation. When they do, you will want your user interface to resize itself properly. You can see this by switching the layout from portrait to landscape mode using the Landscape/Portrait buttons in the Layout Editor's toolbar: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/orientation_buttons.png :scale: 50% :align: center If you don't see the Portrait/Landscape buttons in the Layout Editor's toolbar, it's likely that your window is too small to show them. You can either make your window wider or click on the More Items |moreitems| button on the right side of the Layout Editor's toolbar. .. |moreitems| image:: images/shared/more_items_button.png :scale: 25% 1. Click the **Landscape** button. Notice that the controls do not resize to fit the landscape orientation. 2. Click the **Portrait** button to switch the Layout Editor back to portrait mode. Fortunately, this is easy to do by locking controls to edges of the layout. For this, you will need to change some control *properties*. Understanding properties ------------------------ .. _/getting_started/tutorials/android_tutorial/what_is_a_property?: What is a property? ******************* A property is a specific attribute of an object. For example, a button has a width and thus has a Width property. Changing property values allows you to change the behavior of the object. In this case, you will need to change various locking properties of the controls. .. _/getting_started/tutorials/android_tutorial/using_the_inspector: Using the inspector ******************* The Inspector is used to change screen and control properties. It shares the same area on the right of the Workspace as the Library. 1. Click the **Inspector button** in the upper-right corner of the window (or press :kbd:`⌘ I` on macOS or :kbd:`Ctrl I` on Windows and Linux) to display the Inspector. 2. On your layout, click on the **button** to select it. 3. In the Locking section of the Inspector, click on the **lock** on the right side to lock the right side of the button to the right side of the screen. 4. In that same section, click on the **Lock** on the left side to unlock the button from the left side of the screen. This will keep it locked only to the right side. Your locking properties should now look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/locking.png :scale: 50% :align: center 5. On your layout, click on the **TextField** to select it. 6. In Inspector, click on the **lock** on the right side to lock the right side of the Textfield to the right side of the screen. 7. On your layout, click on the **Table** control to select it. 8. This time, we want to lock the control to all sides of the screen. In the Inspector, click the **lock** on the bottom side. 9. Try clicking the **Landscape** and **Portrait** buttons again to see that your controls now automatically resize and reposition themselves properly when the screen dimensions change. .. _/getting_started/tutorials/android_tutorial/screen_properties: Changing more properties ************************ There are some additional properties that you need to change. You need to: * Rename all controls (and the Screen) so that they describe their behavior and are easy to refer to in code. * Add a Caption to the Button. * Add a hint to the TextField to help the user know what to enter. You need to change the Name and Title properties of Screen1: 1. In the Layout Editor click anywhere on the background to select the Screen (rather than a control). The Inspector now shows the properties of the Screen. #. Change the **Name property** from ``Screen1`` to ``TaskManagerScreen``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Title property** from ``Untitled`` to ``Task Manager``. Press :kbd:`Return` to see the name change in the Navigation Bar of the screen. .. _/getting_started/tutorials/android_tutorial/button_properties: Changing button properties ************************** For the button you'll change the Name and Caption properties. 1. Click the **Button** to select it. The Inspector now shows its properties. #. In the Inspector, change the **Name property** from ``Button1`` to ``AddButton``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Caption property** from ``Button`` to ``Add``. .. _/getting_started/tutorials/android_tutorial/textfield_properties: Changing textfield properties ***************************** The TextField is where the user types the task to add to the Table. You'll be changing the Name, Text and Hint properties. 1. Click on the **TextField** to select it so that its properties are displayed in the Inspector. #. Change the **Name property** from ``TextField1`` to ``TaskField``. Press :kbd:`Return` to see the name change in the Navigator. #. Change the **Hint property** to ``Enter a task``. .. _/getting_started/tutorials/android_tutorial/table_properties: Changing table properties ************************* The table displays the tasks and will also show a check mark next to ones marked as completed. Let's change the Name property to something that is more descriptive. 1. Click on the **table** to select it and display its properties in the Inspector. #. Change the **Name property** from ``Table1`` to ``TaskTable``. Press :kbd:`Return` to see the name change in the Navigator. .. _/getting_started/tutorials/android_tutorial/final_taskmanager_layout: The final TaskManager layout **************************** This is what your layout should now look like after adjusting all the properties: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/final_layout.png :scale: 30% :align: center .. _/getting_started/tutorials/android_tutorial/running_the_app: Adding an editing screen ************************ Eventually we want the user to be able to change the text of a task, mark it as complete or even delete it. To do these things, you will need to create another screen that appears when the user taps on a task in the TaskTable. To do this, you will use the Layout Editor, Library and Inspector just as you did when you created the TaskManagerScreen: 1. Choose **Insert > Screen**. 2. Click on the **Library button** in the toolbar to switch to the Library pane. 3. From the Library, drag a **TextField** to the top of the screen. Resize it to fill the width and lock it to the right side of the screen. 4. Drag a **Label** control below the TextField. 5. Click the **Pencil icon** on your label to edit the text of the Label. 6. Enter ``Completed`` as the text for the Label. 7. Drag a **Switch** control to the right of the Completed Label. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/edit_task_screen_progress.png :scale: 30% :align: center 8. Drag a **Button** to the bottom of the layout and resize it to fill the width. This will be the Delete button. 9. Click on the **Inspector button** in the toolbar to display the Inspector. 10. In the Inspector, in the Locking section, turn off the **top lock** and turn on the **bottom lock** to lock the button to the bottom of the screen. 11. In the Navigator, click on **Screen1** to select it. 12. In the Inspector, change the **Name** of the screen to ``TaskEditorScreen``. 13. Change the **Title** of the Screen to ``Edit Task``. 14. Click on the **TextField** to select it. 15. In the Inspector, change the **Name** for the TextField to **TaskField**. 16. Click on the **Switch control** you added to select it. 17. Change the **name** of the Switch control to ``Completed``. 18. Click on the **Button** you added to select it. 19. Change the **Name** of the Button to ``DeleteButton``. 20. Change the **Caption** to ``Delete``. This is what your TaskEditor screen should look like when you are done: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/taskeditor_layout.png :scale: 30% :align: center Running the app --------------- Your user interface layout is now complete, so it's time to try it out. But before you go further, you should first save your work. 1. Save the project by selecting **File > Save**. .. note:: This means select **Save** from the **File** menu and is the standard way choosing menu items as notated in the Xojo documentation. 1. Name the project ``AndroidTaskManager`` and click **Save**. Now you can test your app: 1. Click the **Run** button in the toolbar to run the project. This builds your app and runs it in the Android Emulator. If this the first time you are running the Android Emulator, it will take a minute or so to start up. #. When your app appears in the Android Emulator, you can interact with the buttons by clicking on them or you can type in the TextField. #. When you are finished, switch back to Xojo and click the **Stop** button. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/stop_button.png :scale: 50% :align: center Of course, Task Manager doesn't do anything yet! You can't even get to the TaskEditor screen you created. For that you need to add some code, which is what you'll do next. .. _/getting_started/tutorials/android_tutorial/adding_code: Adding code ----------- To complete the Task Manager, you will need to: * Make the Add Button add a task. * Make the Add Button only be enabled after the user has entered text into the TaskField. * Allow the user to tap on a row to edit, mark completed and delete a task using the TaskEditor screen you created. .. _/getting_started/tutorials/android_tutorial/coding_the_add_button: Coding the Add button ********************* The Add button should create a new row in the TaskTable from the text the user enters in the TaskField: 1. In the Navigator, click on the **TaskManagerScreen** to select it. 2. Double-click on the **AddButton**. The Add Event Handler dialog box appears. 3. The Pressed event occurs when the user presses the button. It's already selected so just click the **OK** button. The Code Editor is displayed. 4. You need to write a line of code that adds a new row to the TaskTable and assigns it the text from the TaskField. Enter the following code: .. code:: xojo TaskTable.AddRow(TaskField.Text) AddRow is a built-in method of the `AndroidMobileTable` control you added (and named "TaskTable"). It creates a new row from the text passed. The text passed in this case is the Text property of the TaskField. 5. To avoid the user accidentally creating duplicate tasks, the TaskField should be cleared after the user presses the Add button. Let's add another line of code to do that: .. code:: xojo TaskField.Text = "" This line sets the Text property to an empty string of text. Great. Let's give it a try. 1. Click the **Run button** in the main toolbar. .. note:: If you mistyped something, instead of the Android Emulator running, an errors pane will appear in the lower half of the Xojo Workspace window. Should this happen, look at the lines the error messages are indicating and compare them to the code in the instructions above. You probably just mistyped something. If you can't figure it out, go to the Xojo `forum `_ or contact us via `email `_. Your app should now be running again in the Android Emulator. 2. Type a task in the TaskField and press the **Add button** to add it to the table. Enter a few tasks if you'd like. 3. When you're done, return to Xojo and press the **Stop button** you used last time. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/shared/stop_button.png :scale: 50% :align: center .. _/getting_started/tutorials/android_tutorial/enabling/disabling_the_add_button: Enabling/disabling the Add button ********************************* You wouldn't want the user to accidentally add a blank task. To avoid this you'll need to disable the Add button then enable it when the user has entered text in the TaskField, disable it if they clear the TaskField and disable it after the user presses the Add button since this clears the TaskField as well. 1. In the Navigator, click on the **TaskManagerScreen** to select it. #. Click on the **Add** button on the layout. #. In the Inspector, turn off the AddButton's **Enabled** property. Notice the button now appears disabled. #. In the Layout Editor, double-click the **TaskField** and add the TextChanged event handler. #. Add the following code to the **TextChanged** event: .. code:: xojo If Me.Text.IsEmpty Then AddButton.Enabled = False Else AddButton.Enabled = True End If This code checks the Text property of the TaskField to see if it's blank. *Me* in this case refers to the TaskField since this event is part of that control. If the Text property is blank, the AddButton's Enabled property is set to :doc:`False`. If it's not blank, it's set to :doc:`True`. Since the Add button clears the TaskField, you need to add a line of code to its Pressed event to disable it after the user presses it: 1. In the Navigator, click on the **TaskManagerScreen** to select it. #. In the layout Editor, double-click on the **AddButton** to go to its Pressed event handler. #. In the **Pressed** event, add the following line of code to disable the AddButton: .. code:: xojo Me.Enabled = False Next, you will make it possible for the user to edit tasks using the TaskEditorScreen you created. Let's do it! .. _/getting_started/tutorials/android_tutorial/allowing_task_editing: Allowing task editing ********************* Now that you have created a screen that can be used to edit tasks, let's create a method that will be passed task information and display the TaskEditorScreen. When we are finished, the TaskManagerScreen will display a checkmark on the right side of the TaskTable for any tasks that have been completed. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/android_tutorial/completed_task.png :scale: 50% :align: center The checkmark is able to appear because the `AndroidMobileTable` allows for a picture to appear either on the left or right sides of a row. The code you will write will display a picture of a checkmark when the task is marked as completed and display no picture when it's not. Fortunately, Xojo provides a way to display any of the images that are built-in to Android itself, one of which is a checkmark. Let's add a new method to do this: 1. In the Navigator, click on the **TaskEditorScreen** to select it. 2. Choose **Insert > Method**. 3. In the Inspector, change the name of the method to ``UpdateControls``. 4. For the parameters, enter ``theTask As String, complete As Boolean``. 5. Make sure the **Scope** is set to Public. 6. In the Code Editor, add the following two lines: .. code:: xojo TaskField.Text = theTask Completed.Value = complete When you call this method, you will pass it the task and the completion status so that it can then set the TaskField and Completed controls. TaskEditorScreen.UpdateControls should be called when the user taps on a task in the TasksTable. Once you've passed the data to the TaskEditorScreen, the next step will be to show it. 1. In the Navigator, click on the **TaskManagerScreen** to select. 2. In the layout, double-click on the **TaskTable**. The Add Event Handler dialog box appears. 3. Click on the **SelectionChanged** event and then click **OK**. First, the code needs to determine if this task has been completed or not. Since the checkmark picture will appear in any row that is completed, its presence or absence tells us if the task is completed or not. Enter the following code to determine if the task is completed or not: .. code:: xojo If Me.SelectedRowIndex > -1 Then Var complete As Boolean If Me.RowPictureAt(Me.SelectedRowIndex, True) <> Nil Then complete = True End If This code first makes sure a row is selected by checking that SelectedRowIndex is > -1. It then creates a variable called complete of type Boolean which is :doc:`False` by default. It then calls the TaskTable's RowPictureAt method, passing it the row number of the selected row (Me.SelectedRowIndex) and :doc:`True` as the second parameter to indicate if the picture is on the left or right (True meaning *right*). If there is a picture on the right side in that row, then the picture is not :doc:`Nil` and the *complete* variable will be set to :doc:`True`. We can then pass this value, along with the task itself, to the UpdateControls method you wrote. Let's do it: 4. Press Return to create a new line and add the following code to call your UpdateControls method and then show the TaskEditorScreen: .. code:: xojo TaskEditorScreen.UpdateControls(Me.SelectedRowText, complete) TaskEditorScreen.Show End If This code calls the UpdateControls method, passing it the text in the row the user tapped on (Me.SelectedRowText) and the *complete* variable that indicates the status of the task. The second line shows the TaskEditorScreen. Last but not least, we need to make sure a row *is* selected before trying to access the SelectedRowIndex or SelectedRowText. This is easy to do by wrapping all of this code in an If statement that checks to see that SelectedRowIndex is > -1. Add Let's try it out. .. |backbutton| image:: images/android_tutorial/back_button_icon.png :scale: 25% 5. Click the **Run** button. 6. Add a task and then click on it. It appears in the TaskEditorScreen. You can even click on the Android's Back button |backbutton| (or the swipe gesture if you don't have a Back button) to go back to the TaskManagerScreen and click on the row again. What you can't do (yet) is save changes you make on this screen so let's add that ability: 7. In Xojo, click the **Stop** button. 8. In the Navigator, click on the **TaskManagerScreen** to select it. 9. Choose **Insert > Method** to create a new method. 10. Set the **Name** of the new method ``UpdateControls``. Like TaskEditorScreen.UpdateControls, this method will send the changes back from the TaskEditorScreen to the TaskManagerScreen. 11. Set the **parameters** to the same ones you used last time, ``theTask As String, complete As Boolean``. 12. Make sure the Scope is Public. 13. Add the following code: .. code:: xojo TaskTable.SelectedRowText = theTask If complete Then TaskTable.RowPictureAt(TaskTable.SelectedRowIndex, True) = Picture.SystemImage("check", 18) Else TaskTable.RowPictureAt(TaskTable.SelectedRowIndex, True) = Nil End If This code first makes sure there is a selected row. If the user deleted the row, there won't be. Next, it sets the text of the row the user tapped on to the text passed to it (in the *theTask* parameter). It then looks at the *complete* parameter passed. If it's True, it calls RowPictureAt again for the selected row (the one the user tapped on) to set the row picture to a checkmark. It then called Picture.SystemImage to get the checkmark picture from a large set of pictures provided by Xojo. One of them is called "check". The last part (18) indicates the desired size of the picture. If *complete* is :doc:`False`, then the row picture is set to :doc:`Nil`. Now we just need to call the new method to update the TaskTable. 1. In the Navigator, click on the **TaskEditorScreen** to select it. 2. Choose **Insert > Event Handler**. 3. Select the **Closing** event then click **OK**. 4. Enter the following code to call the new UpdateControls method: .. code:: xojo TaskManagerScreen.UpdateControls(TaskField.Text, Completed.Value) When the user taps the Back button to return to the list of tasks, the Closing event will be called because the TaskEditorScreen is closing. The code is calling UpdateControls and passing it the text from the TaskField and the Value of the Completed Switch control. Now you can edit the task and its completed status in the TaskEditorScreen. Let's try it out. 18. Click the Run button. 19. Enter a task then click on it. Make some changes and click the Back button. 20. When you're done testing, go back to Xojo and click the **Stop** button. .. _/getting_started/tutorials/android_tutorial/deleting_tasks: Deleting Tasks ************** The last step is to make that Delete button you added work. 1. In the Navigator, click on TaskManagerScreen to select it. 2. Add a new method called ``DeleteTask``. 3. Enter the following code: .. code:: xojo TaskTable.RemoveRowAt(TaskTable.SelectedRowIndex) TaskTable.SelectedRowIndex = -1 This method, when called, will remove the row the user tapped on by calling the table's RemoveRowAt method and passing it the table's SelectedRowIndex (the number of the row that the user tapped on). It then sets the SelectedRowIndex to -1 since there can be no row selected since the selected row was just removed. Last but not least, you need to call this method. 4. In the Navigator, click on the **TaskEditorScreen** to select it. 5. Double-click on the **Delete** button and add the **Pressed** event. 6. Add the following code: .. code:: xojo TaskManagerScreen.DeleteTask Close When the user presses the Delete button, the DeleteTask method will delete the row in the TaskTable. The Close method will then close the TaskEditorScreen. 1. Run your project and try deleting a task. You're going to see there's a bug you need to fix. .. note:: Xojo also supports test running and debugging your Android apps directly on your phone or tablet. Finding and Fixing Bugs *********************** Sometimes code doesn't work the way you planned. Bugs are a normal part of creating apps. So what happened here? Xojo's Debugger is indicating that there's a problem with the TaskManager.UpdateControls method. Remember that in the Closing event of the TaskEditorScreen, your code calls that method. The problem is on the first line. It's trying to set the row text of the row the user tapped on to the text from the TaskField on the TaskEditorScreen. The problem is that you just deleted that row. So there is no row to update. In the Variables pane of the Debugger, you can see that an Exception has occurred. Specifically, an OutOfBoundsException. An exception occurs when something unanticipated happened. In this case, it occurred because the code is trying to assign a value to a row that no longer exists. The row is out of bounds. Fortunately, there's an easy solution to this problem. You just need to have your code make sure there's a valid row before trying to change it: 1. In the Xojo Debugger, click the **Stop** button. 2. In the Navigator, click on the **TaskManager.UpdateControls** method to select it. 3. In the code, add an If statement around the code. When you're done, your UpdateControls method should look like this: .. code:: xojo If TaskTable.SelectedRowIndex > -1 Then TaskTable.SelectedRowText = theTask If complete Then TaskTable.RowPictureAt(TaskTable.SelectedRowIndex, True) = Picture.SystemImage("check", 18) Else TaskTable.RowPictureAt(TaskTable.SelectedRowIndex, True) = Nil End If End If The If statement first checks to see if the TaskTable's SelectedRowIndex (the number of the row the user tapped on), is greater than -1. The first row is 0. It will only be -1 if there are no rows. So as long as there is a selected row, your code will now attempt to update the TaskTable. 11. Run your project again, create a task and then delete it. No more error. Congratulations! .. _/getting_started/tutorials/android_tutorial/deploying: Deploying your app ****************** Android apps are deployed by :doc:`publishing them via the Google Play Store`. You can provide your app to the general public or to just those in your organization. .. _/getting_started/tutorials/android_tutorial/next_steps: Next steps ---------- Congratulations, you have successfully completed the Android Tutorial and now have a fully functional app. You learned a lot in this tutorial. You may be wondering how you'll remember it all. The good news is that you don't have to remember it. Xojo comes with documentation that will help you every step of the way. The Topics section will give you a general overview of the different parts of Xojo and of all the various controls and things you can use in your projects. The API section will give you the all the details for specific controls. After you feel you've reviewed enough of the documentation, choose a small project to build that will benefit you or others. The best way to learn is to have a reason to learn. A small project will provide that. When you need extra help, `contact us `_ or go to our `helpful community forum `_. :download:`Download ` the completed TaskManager project. .. _/getting_started/tutorials/android_tutorial/feedback: Feedback -------- What did you think of this tutorial? We'd appreciate your feedback, `contact us `_. ***************************************************************** Quickly building a database client app for the desktop with DBKit ***************************************************************** In this tutorial, you will recreate the single window example in the DBKit-Desktop example project. The tutorial takes about 20 minutes to complete. There is a :doc:`web tutorial` as well. Creating the project ******************** 1. Download the :download:`DBKitResources ` file. This contains the database and icons you'll need to complete the tutorial. 2. Uncompress the file which will create the DBKit Resources folder. 3. Create a **new desktop project**. #. **Save** the project. #. From the DBKit Resources folder you downloaded, drag the **Icons folder** onto the Navigator in your project. #. Open the DBKit-Desktop example project (New Project > Examples > Databases > DBKit) then copy the **DBKit module** from the example into your project. .. important:: Always copy DBKit from the **DBKit-Desktop** example project when using it with desktop projects. .. note:: The Icons folder includes icons you can use for toolbar buttons that DBKit will then manage for you. Next we need to copy the EddieElectronics.sqlite database file into your app when it is built. 1. In the Build Settings section of the Navigator, select the OS your are going to build for (Linux, macOS or Windows). 2. Add a **Copy Files Build Step** (Insert > Build Step > Copy Files). #. In the Inspector, change the **name** to ``CopyDB``. #. From the DBKit Resources folder you downloaded, drag the **EddieElectronics.sqlite database** file into the area in the middle of the IDE below where it says "Drag files into the list below for this step". #. In the Inspector, change the **Destination** to Resources Folder. #. If you're on a Mac, drag **CopyDB** so that it's immediately after the Build item as the app must be built before the database can be copied to it. Because the database is being copied each time you run your project, you will be able to edit it all you'd like without worrying that your changes are permanent. So add, edit and delete to your heart's content. Connecting to the database ************************** Now let's get connected to the database. 1. In the Navigator, click on the App object. 2. In the Inspector, set the **App.DefaultWindow** to None. #. In the **App object** in the Navigator, create a property: .. csv-table:: :header: "Item", "Value" :widths: auto Name, db Type, DBKit.TableConnection Scope, Public 3. In the **App.Opening** event, add the following code to connect to the SQLiteDatabase: .. code:: xojo db = New DBKit.TableConnection If db.Connected(SpecialFolder.Resources.child("EddiesElectronics.sqlite")) Then Var w As New Window1 Else System.Beep MessageBox("The database could not be reached due to an error.") End If .. note:: In your own apps, you should consider the Resources folder to be read-only. If you are providing the user with a database, even an empty one, your code should copy the database file from Resources to another location outside the application itself before writing to it. Adding the ability to search for customers ****************************************** Now let's add the ability to search for customers and display the results in a Listbox. 1. Expand DBKit in the Navigator then expand Classes and drag a **TableConnection** to the Window1. 2. In the Inspector, set the **Table property** to ``customers``. 3. From the DBKit module in the Navigator, drag a **SearchField** to Window1. Position it at the top and make it fill the entire width. 4. Rename the **SearchField** to ``SearchCustomers``. 5. Add a ListBox to Window1. Position it on the left side below the SearchField. 6. To tell DBKit that this ListBox will show search results, you'll need to make it a DBKit.QueryRowsListBox control. Let's do it a different way this time. Click on the **pencil icon** next to the Super property in the Inspector. 7. When the dialog appears, select **DBKit.QueryRowsListBox** then click the **Select button**. 8. Position the ListBox on the left side of the layout and stretch the bottom so that it fills the available space. 9. Set the Width property to ``230``. 10. Use the Inspector to configure the ListBox as follows: .. csv-table:: :header: "Item", "Value" :widths: auto Name, SearchResults ColumnCount, 2 Initial Value, First Name :kbd:`TAB` Last Name Requires Selection, True Columns, "FirstName, LastName" .. note:: :kbd:`TAB` means to press the Tab key on the keyboard rather than literally type . The Columns property indicates the names of the database columns from your table that you want displayed in the SearchResults Listbox. .. tip:: You might want to drag the bottom all the way to the bottom of the window and lock it so that it expands when the user expands the window. 11. Run your project and try searching for names by typing a few characters. Displaying the selected row *************************** Next we will add some controls that can display other database columns (address, city, etc.) when the user clicks on a customer in the SearchResults ListBox. By naming the controls to match the column names in the customers table of the database, DBKit will automatically populate them. The finished layout should look something like the one: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/dbkit_tutorials/desktop_finished_layout.png :scale: 50% :align: center Of course you can of course arrange it any way you want. 1. Drag a **TextField** to Window1. 2. Set the following properties in the Inspector. .. csv-table:: :header: "Item", "Value" :widths: auto Name, FirstName Hint, First name 3. To tell DBKit that this TextField will display data from the selected row in the SearchResults ListBox, click on the **pencil icon** next to the Super property in the Inspector. In the dialog box that appears, select **DBKit.TextField** then click the **Select** button. 4. Duplicate the **FirstName control** to create TextFields for LastName, Address, City, PostalCode and Email. Make sure to change the Name and Hint properties for each one. .. tip:: You might want to set the control locking for these controls so they user can resize the window and have the controls properly resize with it. 5. Drag out a **PopupMenu** to display the State/Region and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, StateOrRegion Super, DBKit.PopupMenu Selected Row Index, -1 6. In the DBKit section of the Inspector, click the **AutoPopulate** checkbox. 7. In that same section, set the Auto Populate Table property to ``States`` and the Auto Populate Column property to ``Abbreviation`` to populate the popup menu from the Abbreviation column of the States table in the database. .. tip:: Feel free to add a label for the PopupMenu. 8. Add a **DateTimePicker** control below the TextFields and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, LastContact Super, DBKit.DateTimePicker Display Mode, DateOnly .. important:: Make sure you set the DisplayMode of the DateTimePicker to DateOnly. If you miss this step, every time you switch records, DBKit will think you've edited the row because it will be comparing a date to a value that includes both the date and the time. 9. Add a **CheckBox** to the window and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, Taxable Super, DBKit.CheckBox Caption, Taxable 10. Add an **ImageViewer** to the layout and set the following properties:. .. csv-table:: :header: "Item", "Value" :widths: auto Name, Photo Super, DBKit.ImageViewer 11. Now it's time to see your work (and DBKit) in action. Run your project. If you have any errors, you probably didn't name your controls properly so double check those. If all went well, try searching again and clicking on rows in the SearchResults ListBox. You should see the customer information appear in the various controls. Adding more controls for other database columns in the future ************************************************************* Now that DBKit is set up and configured, if you needed to add more columns you'd only need to do the following 3 steps: 1. Drag out a **control**. #. **Name it** the same as the database column. #. Set the **Super** property in the Inspector to make it a DBKit control. .. tip:: If you prefer not to match the name of the control to the name of the column, you can instead indicate the column name using the Column property in the DBKit section of the Inspector. Adding an Edit button ******************** Now we need to add an Edit button so quit your app and head back to the Layout Editor. 1. Make some room at the bottom (if necessary) for some buttons. #. Add a **Button** to the lower-right corner of Window1: .. csv-table:: :header: "Item", "Value" :widths: auto Name, EditButton Super, DBKit.EditButton Caption, Edit .. tip:: Make sure to set the locking properties appropriately. 3. Run your project. Notice that clicking Edit button makes the entry controls editable and changes the Edit button caption to *Done*. Make some changes and test out the Edit/Done button. Notice that if you are in the middle of editing and try to switch to another row, DBKit automatically confirms you wish to continue as you will lose your edits if you do. Adding an Undo button ********************* Now let's add an Undo button the user can use to undo any changes they make. 1. Add a **Button control** to the left of the Edit button: .. csv-table:: :header: "Item", "Value" :widths: auto Name, UndoButton Super, DBKit.UndoButton Caption, Undo .. tip:: Set the locking properties so it moves as the window is resized. 2. Run your project. Try changing a customer record and then pressing the Undo button. Notice the button enables/disables automatically. Adding a Delete button ********************** Now let's add a Delete button so the user can delete rows. 1. Add a **button** to the layout. Align it with the other buttons but position it over next to the SearchResults ListBox: .. csv-table:: :header: "Item", "Value" :widths: auto Name, DeleteButton Super, DBKit.DeleteButton Caption, Delete 2. Run your project then test out the delete button. Notice that DBKit.TableConnection automatically confirms with the user that they wish to delete the row. Try using both the Cancel and Delete buttons in the confirmation dialog box. Adding a New button ******************* The user needs to be able to add new customers. Let's add a New button. 1. Add a **button** to the layout. Position it between the Delete button and the Undo button. You may need to make your window bigger to make room for it: .. csv-table:: :header: "Item", "Value" :widths: auto Name, NewButton Super, DBKit.NewButton Caption, New 2. Run the project and test out the New button. Separating the search results and entry controls ************************************************ In this tutorial you created a single window app. The SearchResults Listbox is on the same window as the various entry controls. DBKit also supports multi-window user interfaces where the SearchResults ListBox and entry controls are on separate windows. You can see this in the DBKit-Desktop example project. Run it and click the Separate button. What appears is a search window. After doing a search, double-click on a row to open the entry window. If you examine the SearchWindow layout, you will see a few important changes from the window you created in this tutorial: Since the SearchResults ListBox displays more columns, the SearchForCustomers method includes the additional columns. Since the user double-clicks on a row in the SearchResults ListBox, instead of using the SelectionChanged event, the DoublePressed is used and the code is different: .. code:: xojo If Me.SelectedRowIndex > -1 Then 'If there is a row selected Var w As New EntryWindow w.TableConnection1.BindListBoxControl(Me) w.Show End If This code: 1. Makes sure a row is selected then creates a new window. 2. Binds the DBKit.TableConnection on the new window to the listBox. 3. Shows the new window. You will also notice that the SearchWindow has a New button on it for creating new customers. It's Pressed event code looks like this: .. code:: xojo Try Var w As New EntryWindow w.TableConnection1.CreateNewRow = True 'Set to true to create a new row w.Show Catch error As DatabaseException System.Beep MessageBox("An error occurred while attempting create a customer.") End Try This code: 1. Creates a new EntryWindow instance. 2. Sets the CreateNewRow property to True so that the EntryWindow will know to set itself up for a new row. 3. Shows the new EntryWindow. The EntryWindow also has a DBKit.TableConnection class instance. Because it's only managing the entry controls, it's Opening event is just the code that was missing from the Opening event of the DBKit.TableConnection on the SearchWindow: .. code:: xojo 'Create a new row (if that property was set to true before the window opened) or load the selected one If Me.CreateNewRow Then Me.NewRow Else Me.LoadSelectedRow End If FirstName.SetFocus The extra code at the end checks to see if CreateNewRow was set to True and if so, calls the NewRow method to create a new customer. If it's False, then the EntryWindow loads the selected row. You will also notice that the EntryWindow uses a toolbar rather than individual buttons. This is just to show that DBKit can use Toolbar buttons as easily as regular buttons. Next Steps ********** 1. Explore the Separate window and Rows Only examples in the DBKit-Desktop example project. 2. Explore the DBKit-Web example project. 3. Review the :doc:`DBKit Introduction`. 4. Start using DBKit in your own projects. ************************************************************* Quickly building a database client app for the web with DBKit ************************************************************* In this tutorial, you will recreate the single page example in the DBKit-Web example project. The tutorial takes about 20 minutes to complete. There is a :doc:`desktop tutorial` as well. Creating the project ******************** 1. Download the :download:`DBKitResources ` file. This contains the database and icons you'll need to complete the tutorial. 2. Uncompress the file which will create the DBKit Resources folder. 3. Create a new **web project**. #. Save the project. #. From the DBKit Resources folder you downloaded, drag **the Icons folder** into the Navigator in your project. #. Open the DBKit-Web example project (New Project > Examples > Databases > DBKit-Web) then copy the **DBKit module** from the example into your project. .. important:: Always copy DBKit from the **DBKit-Web** example project when using it with web projects. .. note:: The Icons folder includes icons you can use for toolbar buttons that DBKit will then manage for you. Next we need to copy the EddieElectronics.sqlite database file into your app when it is built. 1. In the Build Settings section of the Navigator, select the OS your are going to build for (Linux, macOS or Windows). 2. Add a **Copy Files Build Step** (Insert > Build Step > Copy Files). #. In the Inspector, change the **name** to ``CopyDB``. #. From the DBKit Resources folder you downloaded, drag the **EddieElectronics.sqlite database** file into the area in the middle of the IDE below where it says "Drag files into the list below for this step". #. In the Inspector, change the **Destination** to Resources Folder. #. If you're using a Mac, drag **CopyDB** so that it's immediately after the Build item as the app has to be built before the database can be copied to it. Because the database is being copied each time you run your project, you will be able to edit it all you'd like without worrying that your changes are permanent. So add, edit and delete to your heart's content. Connecting to the database ************************** Now let's get connected to the database. 1. In the **Session object** in the Navigator, create a property: .. csv-table:: :header: "Item", "Value" :widths: auto Name, db Type, DBKit.TableConnection Scope, Public 2. In the **Session.Opening** event, add the following code to connect to the SQLiteDatabase: .. code:: xojo db = New DBKit.TableConnection If Not db.Connected(SpecialFolder.Resources.child("EddiesElectronics.sqlite")) Then MessageBox("The database could not be reached due to an error.") End If .. note:: In your own apps, you should consider the Resources folder to be read-only. If you are providing the user with a database, even an empty one, your code should copy the database file from Resources to another location outside the application itself before writing to it. Adding the ability to search for customers ****************************************** Now let's add the ability to search for customers and display the results in a Listbox. 1. Expand DBKit in the Navigator then expand Classes and drag a **TableConnection** to the WebPage1. 2. In the Inspector, set the **TableName** property to ``customers``. 3. From the DBKit module in the Navigator, drag out a **SearchField** to WebPage1. Position it at the top and make it fill the entire width. 4. Change the name of the **SearchField** to ``SearchCustomers``. 5. Add a **ListBox** to WebPage1. Position it on the left side below the SearchField. You may need to make WebPage1 larger in the Layout Editor. .. csv-table:: :header: "Item", "Value" :widths: auto Name, SearchResults ColumnCount, 2 Initial Value, First Name :kbd:`TAB` Last Name .. note:: :kbd:`TAB` means to press the Tab key on the keyboard rather than literally type . .. tip:: You might want to drag the bottom all the way to the bottom of the webpage and lock it so that it expands to fill the height of the browser window. 6. To tell DBKit that this ListBox will show search results, you'll need to make it a DBKit.QueryRowsListBox control. Since you didn't drag this from the DBKit Module in the Navigator, click on the **pencil icon** next to the Super property in the Inspector. 7. When the dialog appears, select **DBKit.QueryRowsListBox** then click the **Select** button. 8. In the Inspector, set the value of the Columns property for the ListBox to ``FirstName, LastName``. The Columns property indicates the names of the database columns from your table that you want displayed in the SearchResults Listbox. 9. Run your project and try searching for names by typing a few characters then pressing return. .. tip:: Pressing return when the SearchField is empty will display all the customers. Displaying the selected row *************************** Next we will add some controls that can display other database columns (address, city, etc.) when the user clicks on a customer in the SearchResults ListBox. By naming the controls to match the column names in the customers table of the database, DBKit will automatically populate them. The finished layout should look something like the one: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/dbkit_tutorials/web_finished_layout.png :scale: 50% :align: center Of course you can of course arrange it any way you want. 1. Drag a **TextField** to WebPage1 and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, FirstName Hint, First name 2. To tell DBKit that this TextField will display data from the selected row in the SearchResults ListBox, click on the **pencil icon** next to the Super property in the Inspector. In the dialog box that appears, select **DBKit.TextField** then click the **Select** button. 3. Duplicate the **FirstName** control to create TextFields for LastName, Address, City, PostalCode and Email. Make sure to change the Name and Hint properties for each one. .. tip:: You might want to set the control locking for these controls so the user can resize the browser window and have the controls properly resize with it. 4. Drag out a **PopupMenu** to display the State/Region and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, StateOrRegion Selected Row Index, -1 Super, DBKit.PopupMenu 5. In the Inspector, click the **AutoPopulate** checkbox to fill in the popup menu with all the unique values from the StateOrRegion database column. 6. In that same section, set the Auto Populate Table property to ``States`` and the Auto Populate Column property to ``Abbreviation`` to populate the popup menu from the Abbreviation column of the States table in the database. .. tip:: Feel free to add a label for the PopupMenu. 7. Add a **DatePicker** control below the TextFields and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, LastContact Super, DBKit.DatePicker 8. Add a **CheckBox** to the webpage and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, Taxable Caption, Taxable Super, DBKit.CheckBox 9. Add an **ImageViewer** to the layout and set the following properties: .. csv-table:: :header: "Item", "Value" :widths: auto Name, Photo Super, DBKit.ImageViewer 10. To allow the user to upload images on the ImageViewer, from the DBKit module in the Navigator, drag out an **ImageUploader** to the page and set the following properties:. .. csv-table:: :header: "Item", "Value" :widths: auto Name, ImageUploader1 Super, DBKit.ImageUploader Has File Name Field, False Caption, Select Photo... 11. In its **Opening** event, add the following code: .. code:: xojo Me.TargetImageViewer = Photo 12. Now it's time to see your work (and DBKit) in action. Run your project. If you have any errors, you probably didn't name your controls properly so double check those. If all went well, try searching again and clicking on rows in the SearchResults ListBox. You should see the customer information appear in the various controls. Adding more controls for other database columns in the future ************************************************************* Now that DBKit is set up and configured, if you needed to add more columns you'd only need to do the following 3 steps: 1. Drag out a control. #. Name it the same as the database column. #. Set the Super property in the Inspector to make it a DBKit control. Adding an Edit button ******************** Now we need to add an Edit button so quit your app and head back to the Layout Editor. 1. Make some room at the bottom (if necessary) for some buttons. #. Add a **Button** to the lower-right corner of WebPage1: .. csv-table:: :header: "Item", "Value" :widths: auto Name, TheEditButton Super, DBKit.EditButton Caption, Edit Indicator, Primary .. tip:: Make sure to set the locking properties appropriately. 3. Run your project. Notice that clicking Edit button makes the entry controls editable and changes the Edit button caption to *Done*. Make some changes and test out the Edit/Done button. Notice that if you are in the middle of editing and try to switch to another row, DBKit automatically confirms you wish to continue as you will lose your edits if you do. Adding an Undo button ********************* Now let's add an Undo button the user can use to undo any changes they make. 1. Add a **Button** control to the left of the Edit button: .. csv-table:: :header: "Item", "Value" :widths: auto Name, TheUndoButton Super, DBKit.UndoButton Caption, Undo Indicator, Warning .. tip:: Set the locking properties so it moves as the browser window is resized. 2. Run your project. Try changing and customer and then pressing the Undo button. Notice the button enables/disables automatically. Adding a Delete button ********************** Now let's add a Delete button so the user can delete rows. 1. Add a **button** to the layout. Align it with the other buttons but position it over next to the SearchResults ListBox: .. csv-table:: :header: "Item", "Value" :widths: auto Name, TheDeleteButton Super, DBKit.DeleteButton Caption, Undo Indicator, Danger 2. Run your project then test out the delete button. Notice that DBKit.TableConnection automatically confirms with the user that they wish to delete the row. Try using both the Cancel and Delete buttons in the confirmation dialog box. Adding a New button ******************* The user needs to be able to add new customers. Let's add a New button. 1. Add a **button** to the layout. Position it between the Delete button and the Undo button. You may need to make your layout bigger to make room for it: .. csv-table:: :header: "Item", "Value" :widths: auto Name, TheNewButton Super, DBKit.NewButton Caption, New Indicator, Success 2. Run the project and test out the New button. Separating the search results and entry controls ************************************************ In this tutorial you created a single page web app. The SearchResults Listbox is on the same page as the various entry controls. DBKit also supports multi-page user interfaces where the SearchResults ListBox and entry controls are on separate pages. You can see this in the DBKit-Web example project. Run it and click the Separate button. What appears is a search page. After doing a search, double-click on a row to open the entry page. If you examine the SearchPage layout, you will see a few important changes from the page you created in this tutorial: Since the SearchResults ListBox displays more columns, the SearchForCustomers method includes the additional columns. Since the user double-clicks on a row in the SearchResults ListBox, instead of using the SelectionChanged event, the DoublePressed event is used and the code is different: .. code:: xojo 'Make a new entry page Var p As New EntryPage 'Bind this listbox to its DBKit TableConnection so it knows where to get the rows from p.TableConnection1.BindListBoxControl(Me) 'Show the page p.Show This code: 1. Creates a new page. 2. Binds the DBKit.TableConnection on the new page to the ListBox. 3. Shows the new page. You will also notice that the SearchPage has a New button on it for creating new customers. It's Pressed event code looks like this: .. code:: xojo Try Var p As New EntryPage p.TableConnection1.CreateNewRow = True 'Set to true to create a new row p.Show Catch error As DatabaseException MessageBox("An error occurred while attempting create a customer.") End Try This code: 1. Creates a new EntryPage instance. 2. Sets the CreateNewRow property to True so that the EntryPage will know to set itself up for a new row. 3. Shows the new EntryPage. The EntryPage also has a DBKit.TableConnection class instance. Because it's only managing the entry controls, it's Opening event is just the code that was missing from the Opening event of the DBKit.TableConnection on the SearchPage: .. code:: xojo 'Create a new row (if this property was set before the web page opened) or load the selected row If Me.CreateNewRow Then Me.NewRow Else Me.LoadSelectedRow End If FirstName.SetFocus The extra code at the end checks to see if CreateNewRow was set to True and if so, calls the NewRow method to create a new customer. If it's False, then the EntryPage loads the selected row. You will also notice that the EntryPage uses a toolbar rather than individual buttons. This is just to show that DBKit can use Toolbar buttons as easily as regular buttons. If you want to use a toolbar in your web app, check out the toolbar on this page to see how to set it up and what methods to call to perform the delete, undo and save actions. Next Steps ********** 1. Explore the Separate page and No Rows examples in the DBKit-Web example project. 2. Explore the DBKit-Desktop example project. 3. Review the :doc:`DBKit Details`. 4. Start using DBKit in your own projects. ======================================== Tip Calcuator (Comparing Xojo and Swift) ======================================== If you are interested in iOS development then you've probably heard about Swift, Apple's latest programming language. Swift seems like a fine language, but using it is not quite as simple as it is made out to be. You'll likely be able to create iOS apps much faster using Xojo. To help demonstrate this, this tutorial takes `a Swift tutorial that creates a simple tip calculator `_ and shows you how to port it to Xojo. You compare the differences and see how easy it is to create a native iOS app using Xojo! .. _/getting_started/creating_more_apps/comparing_xojo_and_swift/getting_started: Getting started --------------- Before you start, you'll need to install Xojo and Xcode (if it's not already installed). Note that Xcode only needs to be installed so that the iOS Simulator gets installed. You will not need to actually use Xcode. 1. Launch Xojo and in the Project Chooser, select **iOS**. 2. Enter ``TipCalculator`` for the Application Name, a Company Name (this can be anything) and press :kbd:`OK`. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_project_chooser.png In one quick step, you're now looking at the Xojo Workspace. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_workspace.png On the left side is the Navigator. In the center is the Edit Area, which is currently showing the Layout Editor. In the center is also where you will see the Code Editor. On the right is the Library of controls. You can also see the property Inspector on the right by clicking Inspector in the toolbar at the top. 3. Click **Run** on the toolbar to see what Xojo has created for you. This starts the iOS Simulator and a blank screen appears for the app. Quit the Simulator to go back to Xojo. .. _/getting_started/creating_more_apps/comparing_xojo_and_swift/creating_the_class: Creating the class ------------------ Swift, Xcode and Cocoa generally want you to use the Model-View-Controller design pattern. This is a useful pattern, but it can be overkill for simple apps and can be a bit tricky to grasp for those unfamiliar with it. Xojo uses an event-driven programming model that is much easier to work with (although you can also use MVC if you want). Even though this simple app does not really need a separate class to do the actual tip calculation, you will make one to keep the steps aligning closely with the original Swift tutorial. 1. Click the **Insert** button on the toolbar and choose **Class** to add a new class to your project. 2. Change its name to ``TipCalculator`` using the Inspector on the right. To match the Swift code, you'll add the same properties and methods. 3. Now click the **Insert** button on the toolbar and choose **Property** to add a property to the class. Name it ``Total`` and set its type to ``Double``. You can just type ``Total As Double`` for the name to have the type set automatically. 4. Now add a second property, ``TaxPct As Double`` to the class in the same manner. 5. Next, a computed property is needed. Again, click the **Insert** button on the toolbar and choose **Computed Property** to add it to the class. Name it ``Subtotal As Double``. Like with Swift, a computed property does not actually store a value. Instead it is computed each time based on other values. This computed property will calculate the subtotal each time based on the Total and TaxPct properties. 6. Expand the **Subtotal** computed property (in the Navigator) and In the **Get** section of Subtotal, put this code: .. code:: xojo Return Total / (TaxPct + 1) Now it is time to add the methods. These were covered in part 1 of the Swift tutorial referred to earlier, so here's the code for them: 7. Add the first method by clicking **Insert** on the toolbar and selecting **Method**. Name this method ``CalcTipWithTipPct``, set its parameter to ``tipPct As Double`` and its Return Type to ``Double``. This is its code: .. code:: xojo Return SubTotal * tipPct 8. Add another method and name it ``ReturnPossibleTips`` and set its Return Type to ``Dictionary``. This is its code: .. code:: xojo Var possibleTips() As Double = Array(0.15, 0.18, 0.20) Var retVal As New Dictionary For Each possibleTip As Double In possibleTips Var intPct As Integer = Floor(possibleTip*100) retVal.Value(intPct) = CalcTipWithTipPct(possibleTip) Next Return retVal The last method to add is the Constructor, which is the equivalent of the init method on the Swift class. 9. Add a new method and set its name to ``Constructor`` and its parameters to ``total As Double, taxPct As Double``. This is the code: .. code:: xojo Self.Total = total Self.TaxPct = taxPct That is it for code for the class. Be sure to compare the Swift class to the Xojo class and notice how similar the two are. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_tipcalculator_class.png .. _/getting_started/creating_more_apps/comparing_xojo_and_swift/creating_the_layout: Creating the layout ------------------- In Xojo, mobile layouts are called Screens. In the case of the Tip Calculator, there are two inputs fields on the view: the total bill amount and the tax rate. As with the Swift tutorial, a Text Field is perfect for entering the bill amount and a Slider works well for the tax amount. In addition to those two controls, you'll want a couple labels (for the Text Field and Slider). And you'll also want a Navigation Bar to show the app's name, a button to do the tip calculation and a Text Area to show the results. Like with the Swift tutorial, you'll go through it one step at a time: 1. **Name.** The default screen is given the name Screen1. Click it in the Navigator and change its name to something better: ``TipCalculatorScreen``. 2. **Navigation Bar.** In the Inspector for TipCalculatorScreen, set the **Title** text to ``Tip Calculator``. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_navigation_bar.png 3. **Labels.** From the Library (click the Library button on the toolbar to show it if necessary), drag a **Label** control onto the Screen. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_library_label.png 4. Drag the **Label** to the top left of the Screen and use the layout guides that appear to help you position it with appropriate margins. 5. Drag it so that its width is about half of the layout. 6. With it selected, press :kbd:`RETURN` to set its text to ``Bill Total (Post-Tax):``. 7. Do the same thing for a second label (position it below the first, with the same width) and set its text to ``Tax Percentage (0%):``. You'll also want to set the name of this second label (in the Inspector) to ``TaxPctLabel`` so that it can be easily referred to in the code later. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_labels_added.png 8. **Text Field.** From the Library, drag a **TextField** onto the layout, to the right of the Bill Total label. Use the layout guides to position it properly and resize it to the right margin. 9. In the Inspector, change the name to ``BillTotalField``, the InputType to **Decimal**, and remove the default Text. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_textfield_added.png 10. **Slider.** From the Library, drag a **Slider** onto the layout, to the right of the Tax Percentage layout. Use the layout guides to position it properly and resize it to the right margin. 11. In the Inspector, change the name to ``TaxSlider``, set the **Maximum Value** to ``10`` and **Value** to ``6``. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_slider_added.png 12. **Button.** From the Library, drag a **Button** onto the layout, below the Tax Percentage and centered. Use the layout guides to help with positioning. 13. With it selected, press :kbd:`RETURN` and change its caption to ``Calculate``. In the Inspector, change its name to ``CalculateButton``. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_button_added.png 14. **Text Area.** From the Library, drag a **Text Area** onto the layout below the button. Use the layout guides to position it on the left, right and bottom margins. 15. In the Inspector, change its name to ``ResultsTextArea``, set **Read Only** to ON, and remove the default text of "Untitled". .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_textarea_added.png As you were dragging controls onto the layout and using the layout guides to help with positioning, Xojo was creating the necessary **Auto-Layout** constraints for you. You do not have to do anything else, but if you wanted to manually adjust the constraints, you can do so by selecting a control and changing its Auto-Layout settings in the Inspector. 16. You can now run the app to see how the layout looks in the iOS Simulator. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_basic_ui_simulator.png :scale: 50 % :align: center .. _/getting_started/creating_more_apps/comparing_xojo_and_swift/adding_events_and_code: Adding events and code ---------------------- Because Xojo uses an event-driven model, the controls are already all hooked up to receive events. You can simply add the events you need and populate them with the necessary code, which is what you'll do now. First, you'll add the ValueChanged event to the Slider. 1. Double-click the **slider** in the layout to display the Add Event Handler window and choose the **ValueChanged** event and press :kbd:`OK`. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_add_event_handler.png 2. Add this code to update the text for the label with the tax percent as the slider is moved: .. code:: xojo Var pct As Integer = Me.Value TaxPctLabel.Text = "Tax Percentage (" + pct.ToString + "%):" In the Opening event of the Screen, you can initialize the Tax Percentage label. 3. Double-click on the **Screen layout** (not on a control) and select the **Opening** event from the Add Event Handler window. Click **OK** and add this code: .. code:: xojo Var pct As Integer = TaxSlider.Value TaxPctLabel.Text = "Tax Percentage (" + pct.ToString + "%):" The above code is similar to the code in ValueChanged, so you could extract the code into a common method that is called by both events if you wish. The last code to add is for the button, which is used to calculate the tip amounts and display them. 4. Select the **button** in the layout and double-click it to display the Add Event Handler window and add its Pressed event. Use this code to calculate the tip amounts (using the TipCalculator class you created at the beginning) and display the results: .. code:: xojo ResultsTextArea.Text = "" Var tipCalc As New TipCalculator(Double.FromString(BillTotalField.Text), Floor(TaxSlider.Value) / 100) Var possibleTips As Dictionary = tipCalc.ReturnPossibleTips For Each entry As DictionaryEntry In possibleTips Var tipPct As Integer = entry.Key Var tipValue As Double = entry.Value ResultsTextArea.Text = ResultsTextArea.Text + tipPct.ToString + "%: " + tipValue.ToString + &ua Next Here is a line-by-line breakdown of the code: * Create an instance of the TipCalculator class and initialize it by passing in the bill amount entered by the user (converted it to a Double first) and the current slider value (rounding it to a whole number and converting it to a percentage). * Call the ReturnPossibleTips method on TipCalculator which returns a Dictionary of values. * Now the code iterates through the values in the Dictionary and creates the text to display in ResultsTextArea. * Get the tip percentage, which is the key for the current DictionaryEntry. * Get the tip value, which is the value for the DictionaryEntry. * Combine these into Text to display in ResultsTextArea. That's it. 5. Run the project, enter a bill amount, adjust the slider and click the Calculate button to see three common tip amounts. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/tip_calculator/comparing_xojo_and_swift_tipcalc_finished_app_simulator.png :scale: 50 % :align: center You've now made a native iOS app using Xojo in far less time than you would have using Swift. Give Xojo a try and see how swiftly you can create other apps! .. _/getting_started/creating_more_apps/comparing_xojo_and_swift/next_steps: Next steps ---------- You've now made a native iOS app using Xojo in far less time and with less code than you would have using Swift. `Download the completed Xojo TipCalculator project `_. You can learn more about Xojo and iOS by reviewing more of the Xojo documentation. In particular, the :doc:`Getting Started Welcome page` has links to lots of useful information to help you get started with Xojo. And be sure to check out the `200+ videos `_ and ask questions in the `friendly developer forum `_. =================================== Creating Facebook Messenger in Xojo =================================== Creating apps with Xojo is quick and easy! We recently saw a video on Gizmodo where someone built a Windows-only Facebook messenger app in 3 minutes. Watch how we use Xojo to build the same app for Windows, Mac and Linux in less than 60 seconds. Then learn how you can do it too! As a bonus, we'll also show you how to build the same app for iOS using Xojo in about a minute. .. _/getting_started/creating_more_apps/creating_facebook_messenger_in_xojo/making_the_app: Making the app -------------- Here's how to do it: 1. Launch Xojo (`download it for free `_). #. In the Project Chooser, select “Desktop” if it's not already selected. #. In the Application Name field, type “Messenger” and click the OK button. #. Since you don't have icons, skip the step of assigning an app icon. #. Move your mouse over the pane of controls on the right side of the window (this is called the control Library) and drag the HTMLViewer control to the window in the center of the screen. It's under "Viewers": #. Position the HTMLViewer control in the upper-left corner of the window. #. In the toolbar, click the Fill Width button to make the HTMLViewer as wide as the window. #. In the toolbar, click the Fill Height button to make the HTMLViewer as tall as the window. #. Click the Inspector button in the upper-right corner of Xojo's main toolbar to show the property Inspector. #. In the Locking section of the Inspector pane, click on the right lock and the bottom lock (the two unlocked icons) to lock the right and bottom edges of the HTMLViewer to the window. This will make it resize when the user resizes the window. #. Double-click on the HTMLViewer control in the window to add an event. #. In the Add Event Handler dialog box, select Opening in the list and then click the OK button. #. You are now in the Code Editor. Click in the Code Editor to put the cursor there. Enter the following code exactly as you see it (including the quotes): .. code:: xojo Me.LoadURL("https://messenger.com") 1. In the left-hand pane, click on Window1 to select it. #. In the Inspector pane on the right, find the Frame section and click in the Title field. #. Enter “Messenger” (without the quotes) as the title. #. Click the green Run button in Xojo's main toolbar to test out your application. #. Enter your Facebook login information and you can start to chat away with your friends. .. _/getting_started/creating_more_apps/creating_facebook_messenger_in_xojo/watch_the_video: Watch the video --------------- .. youtube:: WS2sAITeLQA ============================= Connecting to a serial device ============================= Using Xojo, you can control any hardware with a serial interface. This Tutorial will walk you through creating an application in Xojo that can communicate with a serial device. Watch the tutorial video or follow the steps below. This tutorial assumes you have gone through the Xojo :doc:`Desktop QuickStart` and :doc:`Desktop Tutorial` and thus have a basic understanding of how to navigate around a simple project, get to the Inspector, the Library, the Code Editor, etc. If you haven't gone through the Xojo Desktop QuickStart and Tutorial yet, you should do so first. .. _/getting_started/creating_more_apps/connecting_to_a_serial_device/about_the_barcode_scanner: About the barcode scanner ------------------------- It makes sense to first start with the serial device. For this tutorial, the serial device must be an RS-232 device. That means it has a connector like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/serial_device/connecting_to_a_serial_device_serialport-cable.jpg Computers no longer have RS-232 ports so you 'll also need an RS-232 to USB adapter like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/serial_device/connecting_to_a_serial_device_serialport-adapter.jpg This allows your serial device to connect to your computer through USB. There are some devices that combine these two so you don't need the adapter. What is important is that the device makes it clear that it's a serial device. This tutorial uses a `Motorola LS2208 `_ barcode scanner but you can connect to and communicate with any serial device. This particular barcode scanner is popular and available from Amazon for about $100 new or less than $40 used. .. image:: https://documentation.xojo.com/getting_started/tutorials/images/serial_device/connecting_to_a_serial_device_serialport-scanner.jpg Many bar code scanners pretend to be a keyboard. You scan a barcode and they convert that to a value then inserts the value into the keyboard buffer as if the user typed it. That's fine for some purposes but it means the user must have the cursor in the field where they want the barcode value to appear. You may not want to depend on that or you may be using a device that doesn't pretend to be something else and you must communicate with it directly. .. _/getting_started/creating_more_apps/connecting_to_a_serial_device/create_the_project: Create the project ------------------ 1. Launch Xojo (if you haven't already) and create a new Desktop project. Serial devices connected to a computer are identified numerically with 0 being the first device. If you know the number, you can just hard-code it. If you know the name the device uses, you can loop through all of the serial devices and look for the index that has the matching name. Unfortunately, operating systems don't provide an easy way for a typical end user to find out which devices are assigned which numbers. The best thing to do is add a Popup Menu to your app that lists the serial devices so the user can choose one. #. Drag a Label from the Library and title it "Serial Devices:". #. Drag a Popup Menu from the Library and set the Name property of the control DevicesPopupMenu. #. Your window should now look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/serial_device/connecting_to_a_serial_device_devicespopup.png This Popup Menu will automatically update to show devices that have been added or removed. When you plug in your device, you 'll want it to appear in this menu and disappear when you unplug it. To do that, you will need a Timer control that will periodically update the DevicesPopupMenu control. 1. Drag a Timer control from the Library and drop it anywhere in the Layout Editor. Because a Timer control has no user interface, it appears in the tray at the bottom of the Layout Editor in what is called the Shelf. #. Set the Timer's Name property to DeviceListUpdater. #. Set the DeviceListUpdater's Period property to 500. This will cause the DeviceListUpdater to fire over and over again every half second. #. Double-click the DeviceListUpdater, select the Action event and click OK. #. In the Action event copy and paste the following code: .. code:: xojo Var count As Integer = DevicesPopupMenu.RowCount If SerialDevice.Count <> count Then ' The number of serial devices has changed so update the menu DevicesPopupMenu.RemoveAllRows For i As Integer = 0 To SerialDevice.LastIndex DevicesPopupMenu.AddRow(SerialDevice.At(i).Name) Next If SerialDevice.Count < count Then ' a device has been removed DevicesPopupMenu.SelectedRowIndex = 0 Else ' one has been added so select the new device DevicesPopupMenu.SelectedRowIndex = DevicesPopupMenu.LastRowIndex End If End If This code first gets the count of the number of rows in the DevicesPopupMenu. It then compares that to the number of connected serial devices and if they are not the same, it knows that either a new device has been connected or an existing device has been disconnected. In either case, it removes all the rows from the popup menu then loops through the connected serial devices, adding the name of each device connected to the popup menu. Finally, it checks to see if the number of connected serial devices is less than the number of rows the DevicesPopupMenu previously had. If that's the case, a device was removed so the code selects the first row (row 0) from the list of serial devices. If not, then a device has been added and it makes sense the user would want that device selected so the code selects the very last row of the DevicesPopupMenu. 6. Click the Run button to run the project. If you get a syntax error, it's likely to be the result of one or more names of the controls not matching those in the code so check them over carefully and update your control names to match the code. On macOS with no new devices plugged in, the popup menu may list a few internal system devices. Windows, the Popup Menu will likely be empty. If you happen to be running Windows on macOS via VMWare or Parallels, you may have an item titled “COM1” in the popup menu. On Linux it varies. With the Mint distribution you may see a long list of numbered device locations all beginning with “/dev/ttyS”. With the app still running, plug in your serial device and you should see it appear in the popup menu and be selected. Depending on the OS, you will get different device names when you plug in this barcode scanner: * macOS: usbserial-FTHI8S8F * Linux Mint: /dev/ttyUSB * Windows: COM3 1. Experiment with unplugging the device and plugging it back in to see that the popup menu automatically updates. #. Now it is time to add the SerialConnection control. Quit the running app and return to the IDE. #. Drag a SerialConnection control from the Library and drop it anywhere in the Layout Editor. Because it's not a visible control, it will be added to the Shelf. #. Click Inspector in the toolbar to view it. You 'll see the default values for the SerialConnection control in the Behavior section. You 'll have to look at the manual for your device to determine how to set the baud rate, bits, parity, etc. For the Motorola barcode scanner, you only need to change the Baud property to 9600. It will connect at a higher speed, but it won't work properly. #. If you're using the same barcode scanner I 'm using, set the Baud property to 9600. Otherwise, set this property accordingly. Before you start programming the SerialConnection control, add a button the user presses to connect to the serial device. You could of course choose to make this occur when the user selects a device from the popup menu. However, to keep things nicely separated for the purposes of this tutorial, add a Connect button. #. Switch to the Library and drag a Default Button to layout, positioning it to the right of the popup menu. #. Change the button's Caption property to Connect. #. Change the button's Name property to ConnectButton. #. Because we don't want this button enabled until the user selects a device from the popup menu, set the ConnectButton's Enabled property to :doc:`False`. At this point, your layout should look like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/serial_device/connecting_to_a_serial_device_connect_button.png 1. Next, you need to make the ConnectButton enable if a device is selected and disable otherwise. Double-click on the DevicesPopupMenu and add the SelectionChanged event to it. #. In the SelectionChanged event, copy and paste the following code: .. code:: xojo If Me.SelectedRowIndex = -1 Then ConnectButton.Enabled = False Else ConnectButton.Enabled = True End If 12. Next we need to make the Connect button work. The button will change to “Disconnect” once a connection is made. When the user clicks the button, if the caption is Disconnect, it will: * Close the connection to the serial device * Change the caption back to “Connect” * Enable the popup menu * Tell the DeviceListUpdater timer to start firing again Otherwise, the user wants to connect so it will: * Set the serial device to the number of the row selected in the popup menu * Connect to the serial device * Set the caption of the button to “Disconnect” * Disable the popup menu * Turn off the DeviceListUpdater timer 1. Select Window1 in the Navigator on the left-hand side. #. Double-click on the Connect button and add the Pressed event handler. #. Copy and paste the following code into the Pressed event handler: .. code:: xojo If Me.Caption = "Disconnect" Then ' Disconnect from the serial device SerialConnection1.Close Me.Caption = "Connect" DevicesPopupMenu.Enabled = True DeviceListUpdater.RunMode = Timer.RunModes.Multiple 'turn it on Else ' Connect to the serial device ' Set the serial device to the index of the one chosen in the popup menu SerialConnection1.Device = SerialDevice.At(DevicesPopupMenu.SelectedRowIndex) Try SerialConnection1.Connect DevicesPopupMenu.Enabled = False DeviceListUpdater.RunMode = Timer.RunModes.Off Me.Caption = "Disconnect" Catch error As IOException System.Beep MessageBox("The selected serial device could not be opened.") End Try End If Before reading the data from the device, there needs to be a place to show it. 4. Click on Window1 in the Navigator to select it. #. Drag a TextArea control from the Library to the window. The Position and size the TextArea control so that your layout looks like this: .. image:: https://documentation.xojo.com/getting_started/tutorials/images/serial_device/connecting_to_a_serial_device_textarea.png 6. In the Inspector, locate the ReadOnly property in the Behavior section and set it to ON. #. Last but not least, you need to add the code that will actually read the data from the serial port (through the Serial1 control) and append it to TextArea1. #. Double-click on SerialConnection1 and add the DataReceived event. #. Copy and paste the following code into the DataReceived event: .. code:: xojo Var data As String data = Me.LookAhead(Encodings.ASCII) If data.IndexOf(EndOfLine.Windows) > -1 Then TextArea1.Text = TextArea1.Text + Me.ReadAll(Encodings.ASCII) End If By default, the Motorola LS2208 barcode scanner provides a value with no terminating character. That means you have to keep reading from the serial device until you no longer get any data. That complicates things. If your device works this way, you can add a timer that keeps checking the length of the incoming data and when it sees it hasn't changed, it assumes the full value has been received. Fortunately, the LS2208 can be programmed to append a carriage return and line feed to the end of the value. Conveniently, EndOfLine.Windows in Xojo provides just that. If you have a LS2208 to experiment with, take a look at the quick start guide that came with it. You will see a section titled, "Add an Enter Key (Carriage Return/Line Feed)" with three bar codes. Just scan those three in order and your scanner will be programming to append carriage return and line feed to the end of any value. 10. The code above calls the SerialConnection's LookAhead function to get the data from the serial device. It's calling the IndexOf function of the data variable to see if the value returned by EndOfLine.Windows can be found. If it can, you have the entire value so you call the SerialConnection's ReadAll method to read the data and append it to the TextArea. #. Lastly, add an error handler just in case something goes wrong. #. Click on the SerialConnection1 control to make sure it's selected. #. Click the Add button in the Layout Editor's toolbar and choose Event Handler. #. Add the Error event handler and add the following code to it: .. code:: xojo System.Beep MessageBox("An error occured while reading data from the device.") If you have a Motorola LS2208 bar code scanner and have programmed it as instructed, you can now run your project and give it a try. If you have another type of device that terminates the incoming data with other characters, you should be able to modify the code above accordingly. This tutorial went through a bunch of set up to make the UI display the connected serial devices, update automatically, enable and disable when it should, but most of that in a real world scenario could probably be avoided. The meat of this example is the SerialConnection control and its DataReceived event code above which is only 5 lines. .. _/getting_started/creating_more_apps/connecting_to_a_serial_device/next_steps: Next steps ---------- For more information on Serial devices, refer to these topics: * :doc:`SerialConnection` class Examples Projects: * Communication/Hardware/Serial/Line State Change Tester * Communication/Hardware/Serial/Serial Port Bar Code Reader Example * Communication/Hardware/Serial/Serial Line Indicator .. _/getting_started/creating_more_apps/connecting_to_a_serial_device/watch_the_video: Watch the video --------------- .. youtube:: xJUWdamf5g4 Using the IDE ============= .. toctree:: :maxdepth: 1 :name: sec-using_the_ide Introduction Code Editor Constant Editor Enumeration Editor File Type Group Editor Find, Errors, Messages Panels Image Set Editor Included Plugins Inspector Keyboard Shortcuts Layout Editor Library Menu Editor Navigator Picture Editor Project File Information Report Layout Editor Running the Project Settings Structure Editor Toolbar Editor ============ Introduction ============ Xojo is an Integrated Development Environment (IDE). This means that all its components (layout editor, code editor, compiler, debugger, etc.) are all integrated into one package. In traditional programming languages, these items would each be a separate application. .. _/getting_started/using_the_ide/introduction/getting_started: Getting started --------------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/introduction_project_chooser_2025r2.png The first thing you see when you start Xojo is the Project Chooser window. It displays each time you start Xojo and when you select File > New Project. With the Project Chooser window, you can choose to create a new project, work on a recent project, open any project file. * **New Projects** - The Project Chooser lets you quickly decide what project you want to work with. You can choose to create a new blank Desktop, Web, Console, iOS or Android project or open an existing project. For new projects, fill in the Application Name and Company Name to automatically create the Application Identifier (which is used by macOS, Android, iOS and web apps). You can also manually change the Application Identifier as necessary. You need to use Xojo on a Mac in order to create iOS projects. * **Recent Projects** - You can view the most recently used projects and choose to open one of them. If the recent project is no longer available, clicking on it will prompt you to remove it. To remove an individual recent project, select it and then press the Backspace or Delete key. You can also clear the entire list of recent projects when a project is open by selecting File > Open Recent > Clear Menu. * **Templates** - :ref:`Templates` are Xojo projects that are saved in the Templates folder alongside the Xojo application. If you have a set of commonly used modules, classes or anything else, you can put them all in a project and then save it to the Templates folder. When you open the template, you get a new project with all your project items already included. Xojo includes templates for creating Service Applications and Event-Driven Console Applications. You can read more about Templates and how to create your own in the Project Types section. * **Examples** - Select Examples to view the example projects included with Xojo. The examples demonstrate how to use specific features and functions of Xojo. When you choose an example, it is opened in a new project for you to run, edit or save where you like. Over 400 examples are included. * **Open an Existing File** - Click this button to get a file selector for you to choose an existing Xojo project to open. Once you have chosen the project to work with, the Xojo Workspace window is displayed. If you open a Xojo project that is read-only on disk, then it will be read-only in the IDE as well. .. _/getting_started/using_the_ide/introduction/workspace: Workspace --------- When you open Xojo and choose to open or create a project, a single window, called the Workspace displays. Within this window you can navigate among project items by clicking on their names. You can also choose to open project items in their own tabs for more flexibility. You have more than one project open at the same time; each will be shown in its own Workspace window. You can open multiple workspace windows for a single project by using File > New Workspace, which can be useful when working with multiple displays. The Workspace consists of these areas: * Top: `Toolbar` * Left: Navigator * Center: Editor Area, containing layout and code editors * Right: Library/Inspector * Bottom: Find/Errors/Messages Panels .. _/getting_started/using_the_ide/introduction/toolbar: `Toolbar` ********* The `toolbar` at the top of the main window is called the Workspace `toolbar`. It has these buttons: .. csv-table:: :header: "Button", "Description" :widths: auto "Insert","Adds an item to the project or to the selected project item." "Back","Moves to the previously used item in the tab." "Forward","Moves to the next used item in the tab." "Run","Runs your project using the debugger." "Build","Creates stand-alone applications that you can distribute to others." "Deploy","Builds and uploads your app to Xojo Cloud. This is only visible for web projects." "Help","Displays documentation for the selected item." "Feedback","Launches your browser and opens Issues, our bug reporting/feature request tracking system." "Library","Toggles the visibility of the Library." "Inspector","Toggles the visibility of the Inspector." You can hide the `toolbar` by selecting View > Hide `Toolbar` from the menu. .. _/getting_started/using_the_ide/introduction/full_screen_mode: Full screen mode **************** On macOS you can also click the “full screen” button in the window to put the Workspace into full screen mode. When in full screen mode, the window title bar and the main menu bar are hidden to give you more space and allow you to focus better. Full screen mode is especially useful on smaller laptop screens. To see the main menu bar when in full screen mode, move the mouse cursor to the top of the screen; the menu bar will slide down. To exit full screen mode, click the full screen icon in the menu bar. .. _/getting_started/using_the_ide/introduction/menu: Menu **** The main Xojo menu has these top-level menus: File, Edit, View, Insert, Project, Window and Help. .. _/getting_started/using_the_ide/introduction/file: File ^^^^ * New Project * New Workspace * :ref:`New Tab` * Open * Open Recent * :ref:`Close Tab` * Close Window * :ref:`Save` * :ref:`Save As` * Revert to Saved * :ref:`Import` * :ref:`Export` * :doc:`Export Localized Values` * :ref:`Collect Project Items` * :ref:`Print` * :ref:`Page Setup` .. _/getting_started/using_the_ide/introduction/edit: Edit ^^^^ * Undo, Redo * Cut, Copy, Paste, Delete, Duplicate * Select All, Deselect All * :ref:`Comment` * :ref:`Set Default Value of` * :ref:`Encrypt` * :doc:`Find` * :doc:`Find` * :doc:`Next Issue` * :doc:`Previous Issue` .. _/getting_started/using_the_ide/introduction/view: View ^^^^ * :ref:`Tab Order` * Go To :doc:`Layout`/:doc:`Code` * Zoom In, Zoom Out * Hide/Show `Toolbar` * Hide/Show `Toolbar` Captions * :ref:`Hide Tab Bar` * :doc:`Library` * :doc:`Inspector` * Toggle Palettes * :doc:`Hide Find` * Enter Full Screen .. _/getting_started/using_the_ide/introduction/insert: Insert ^^^^^^ * :doc:`Build Step` * :ref:`Copy Files` * :ref:`Script` * :ref:`External` * :ref:`Various Project Items` * :ref:`Event Handler` * [Project Members] .. _/getting_started/using_the_ide/introduction/project: Project ^^^^^^^ * :doc:`Run` * :doc:`Run Paused` * :doc:`Run Remotely` * :ref:`Setup` * :ref:`Pause` * :ref:`Stop Debugging` * :ref:`Step` * :ref:`Over` * :ref:`Into` * :ref:`Out` * :ref:`Breakpoint` * :ref:`Turn On` * :ref:`Clear All` * :ref:`Show All` * :ref:`Bookmarks` * :ref:`Turn On` * :ref:`Clear All` * :ref:`Show All` * :doc:`Break On Exceptions` * :doc:`Profile Code` * :ref:`Analyze Project` * :ref:`Analyze Item` * :ref:`Analysis Warnings` * Build Application * :doc:`Deploy Application` * :ref:`Go To Location` .. _/getting_started/using_the_ide/introduction/window: Window ^^^^^^ * :ref:`Next Tab` * :ref:`Previous Tab` * [Open Workspace Windows] .. _/getting_started/using_the_ide/introduction/help: Help ^^^^ * Documentation * Help for Selection * `Xojo on the Web `_ * `Xojo Support Forum `_ * :doc:`Xojo Feedback` =========== Code Editor =========== Your app is eventually going to need code, which you write using the Xojo programming language in the built-in Code Editor. Code can be attached to almost any item that is in your project, including windows, web pages, controls, classes, modules and more. To maximize typing speed in the Code Editor, you should close the Find/Errors/Messages panel when it is not in use. Typing in the Code Editor can force the panel to update so that it shows changed code and this can slow down typing in large projects. .. _/getting_started/using_the_ide/code_editor/editing_features: Editing features ---------------- You can think of the Code Editor as a text editor that is specially designed for writing Xojo code. It has many features that make it easy for you to program in Xojo. .. _/getting_started/using_the_ide/code_editor/syntax_highlighting: Syntax highlighting ******************* The Code Editor highlights your source code for you as you type. You can change the default colors for things such as keywords, string, integers, comments and more in Preferences. .. _/getting_started/using_the_ide/code_editor/auto_indentation: Auto indentation **************** .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_code_editor_-_auto_indention.png :scale: 50 % :alt: Code Editor showing indented code. The Code Editor automatically indents your code as you type it. Code such as ``If...Then``, ``While/Do`` loops, ``Select...Case`` and other commands that group code are indented for you automatically. Code that is indented is referred to as a code block. In the Code Editor, you can see that code blocks have a small “-” sign to their left. When you hover over the "-", the code block lines are highlighted to better identify the code block. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_code_editor_-_expanded_code.png :scale: 50 % :alt: Code Editor - Expanded Code .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_code_editor_-_collapsed_code.png :scale: 50 % :alt: Code Editor - Collapsed Code You can click the "-" to collapse the code block (changing it to a "+"), hiding all the code within it to make your other code easier to read. The code will still run and is part of your project; it is just not visible in the Code Editor. Click the "+" to expand a code block. Click on the vertical line connecting the code block to select all the code in the block. Expanding and collapsing code is a great way to hide code that you do not need to see when focusing on other code. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_code_editor_-_autocomplete_menu.png :scale: 50 % :alt: Code Editor - Autocomplete Menu .. _/getting_started/using_the_ide/code_editor/highlighting_the_current_line: Highlighting the current row **************************** To make it easier to find the line being edited, you can turn on the Highlight current row feature in on the Code Editor pane in the Settings window. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_highlight_current_row.png :scale: 50 % :alt: Code Editor - Highlight Current Row .. _/getting_started/using_the_ide/code_editor/highlighting_the_selected_variable: Highlighting the selected variable ********************************** To make it easier to find all instances of a variable, you can turn on the Display selection matches feature on the Code Editor pane in the Settings window. Selecting a variable will then highlight all instances of that variable in the current method. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_highlight_selected_variable.png :scale: 50 % :alt: Code Editor - Highlight Selected Variable .. _/getting_started/using_the_ide/code_editor/auto-complete: Auto-Complete ************* As you are typing in the Code Editor you will often see an ellipsis (three small dots) that appears after the text you are typing. This indicates that the Code Editor has some auto-complete suggestions for you. When you see the ellipsis, press the tab key to display a list of available commands that can be auto-completed. The auto-completion is smart and only tries to show you commands that are relevant based on the context of your code. When the auto-complete list is displayed, you can choose the item you want using the mouse or you can continue typing to automatically move the selection in the list. When you've reached the selection you want, just press return. Here, "d." was typed and when the tab key was pressed the auto-complete menu appeared showing the methods and properties of a Dictionary that are relevant: .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_code_editor_-_auto_complete_code.png :scale: 50 % :alt: Code Editor - Auto Complete Code Auto-complete may also show you the remaining part of the text you are typing (in gray). Press tab to have the text fill in automatically. This can be much faster than typing out the name manually, especially if you have long variable, class or method names. If you are working on a project you created in version 2019r1 or earlier, entering an API that has been replaced with an API 2.0 equivalent will result in the original API displayed in Auto-Complete along with the new replacement API. To insert the new API rather than old one, hold the Shift key while clicking on the item in the Auto-Complete list. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_screen-shot-2019-10-25-at-3.06.08-pm-2.png :scale: 50 % :alt: Auto-Complete in the Code Editor .. _/getting_started/using_the_ide/code_editor/syntax_help: Syntax help *********** There is a small area at the bottom of the code editor that is used to display syntax help. The Syntax Help Area displays the line number, column number, syntax information, method signatures, declarations and other information about the code that is under the mouse cursor. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_code_editor_-_breakpoint_and_bookmark.png :scale: 50 % :alt: Code Editor - Breakpoint and Bookmark .. _/getting_started/using_the_ide/code_editor/breakpoints_and_bookmarks: Breakpoints and bookmarks ************************* The gutter on the left side of the Code Editor is used to set :ref:`breakpoints` and bookmarks. A breakpoint is a line of code that will stop execution when your app is run in the debugger. A bookmark is a quick way to jump to specific code. Next to each line of code, you will see a dash ("-") in the gutter indicating that this is a line that can have a breakpoint or bookmark. Click on the dash to toggle a breakpoint on or off. A breakpoint is indicated by a red circle. :kbd:`⌥ Click` macOS (or :kbd:`Control Click` on Windows/Linux) to toggle a bookmark on or off. A bookmark is indicated by a bookmark icon. If you have both a breakpoint and a bookmark set for the line, the indicator appears as a red bookmark icon. The Project > Breakpoint menu has menu items to turn a breakpoint on or off, show all the breakpoints or remove all the breakpoints. Similarly, Project > Bookmarks has menu items to turn a bookmark on or off, show all the bookmarks or remove all the bookmarks. When you Show All bookmarks or breakpoints, they appear in the Find panel at the bottom of the Workspace. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/code_editor_code_editor_-_line_numbers.png :scale: 50 % :alt: Code Editor - Line Numbers .. _/getting_started/using_the_ide/code_editor/line_numbers: Line numbers ************ In addition to displaying breakpoints and bookmarks, the gutter can also show line numbers (as see in the image above) when the *Show line numbers in gutter* checkbox in the Coding pane of the :ref:`Settings` window is selected. Doing so will also result in the background of the current line being drawn using the assigned Row Highlight color in the :ref:`Settings` window. .. _/getting_started/using_the_ide/code_editor/go_to_location: Go To location -------------- To go to a specific line of code, select Project > Go To Location to display the Go To Location window. Enter the line number (with or without a # prefix) you want to jump to and press Return (or click Go). The Code Editor will then scroll that line into view. .. _/getting_started/using_the_ide/code_editor/contextual_menu: Contextual menu *************** You can right-click (or :kbd:`Control Click`) on a variable, method, property, class or any item in your project to display the contextual menu, which offers several useful features: .. csv-table:: :header: "Item", "Description" :widths: auto "Cut/Copy/Paste/Delete","Provides the usual cut, copy, paste and delete functionality." "Select All","Selects all the text in the code editor." "Add Method","If you are clicking in your code on the name of a class that has delegates or events defined within it, this menu item will appear allowing you to add methods to the current project item using the method signatures from the selected delegate or event." "Comment","Comments (or uncomments) the selected code using the comment character you have specified in :doc:`Coding settings`." "Insert Color","Opens the Color Selector window and allows you to choose a color, which is added to the code editor as a color literal." "Insert #Pragma Unused for All Parameters", "Inserts at the top of the current method a line with ``#pragma unused`` followed by a parameter name for each of the method's parameters." "Go To","Jumps to the definition of the selected item in the code editor. This works for variables, methods, classes and any other item you have created in your project. You can also quickly jump by double-clicking on a variable, method, class while holding down the command key (MacOS) or control key (Windows and Linux)." "Switch To","Switches to another event, method or property." "Standardize Format","Applies standard formatting rules to the selected text, which case-corrects keywords for you. This can be configured in :doc:`Coding settings`." "Wrap in If/End If","Wraps the selected code in an :doc:`If Then / End If` code block." "Wrap in Do/Loop","Wraps the selected code in a :doc:`Do / Loop` code block." "Wrap in While/Wend","Wraps the selected code in a :doc:`While / Wend` code block." "Wrap in For/Next","Wraps the selected code in a :doc:`For / Next` code block." "Wrap in #if false/#endif","Wraps the selected code in a :doc:`conditional compilation block` that omits the code from the build." "Convert to Method","Moves the selected code to its own method and switches you to the :doc:`method` so you can specify its name, parameters and return value." "Convert to Constant","Displays a dialog that lets you create a constant from the selected text." "Define Missing Method","Creates a new method using the selected method as the method name. If you have supplied parameters, their types are automatically added to the method definition. For method calls without parameters, you'll need to include (and select) blank parenthesis in order for this option to be enabled in the menu." "Clean invisible ASCII characters","When cutting and pasting code, sometimes invisible ASCII characters can confuse the code editor. Use this command to clean up the selected text." "Turn Break Point On/Off","Turns the Break Point for the current line on or off." "Help for / Open Documentation","Opens the Documentation window. If the command at the cursor is recognized as a Xojo command, then “Help for” appears, allowing you to jump directly to its entry in the documentation." .. _/getting_started/using_the_ide/code_editor/code_assistants: Code Assistants *************** In addition to built-in functions above, you can also add your own code assistants. This is done using :doc:`XojoScript`. If you haven't used XojoScript before, don't panic. It's the Xojo language but without access to framework functions. You write your code assistants using the :doc:`IDE Script editor`. Your Code Assistant must implement three functions: 1. Name - Returns the name you wish to appear in the Code Editor's contextual menu. 2. CanEdit - Indicates whether or not your Code Assistant is designed to edit the code selection that will be passed into it as a parameter. Return :doc:`True` if it can and :doc:`False` if it cannot. 3. Edit - This is where your Code Assistant does its work. It's passed the code selection as a string. Your assistant manipulates that string and returns a replacement. Here's an example of a Code Assistant that wraps the selected text in an ``If isDarkMode Then`` statement: .. code:: xojo Function Name() As String Return "Wrap In IsDarkMode" End Function Function CanEdit(selection As String) As Boolean Return True End Function Function Edit(selection As String) As String Var lines() As String lines.Add("If isDarkMode Then") lines.Add("") lines.Add("Else") lines.Add(selection) lines.Add("End If") Return String.FromArray(lines, EndOfLine) End Function Because a Code Assistant is a :doc:`XojoScript`, they only parts of the Xojo framework it can access are: * :doc:`Clipboard` class * :doc:`RegEx` class * :doc:`EncodeBase64` method * :doc:`EndOfLine` method * :doc:`DecodeBase64` method A Code Assistant also has the following built-in methods it can use to determine the type of project in which it's running: * ``ProjectAndroid`` method * ``ProjectConsole`` method * ``ProjectDesktop`` method * ``ProjectiOS`` method * ``ProjectMobile`` method * ``ProjectWeb`` method Unfortunately, the IDE Script Editor does not have access to the debugger making writing Code Assistants challenging. Fortunately there is a way to write your Code Assistants using the Xojo Code Editor and Debugger. Open the Code Assistant Tester example project which can be found by choosing File > New Project, then click on Examples > IDE > Code Assistants > Code Assistant Tester. The Tester window in this project has the three methods mentioned above along with a TextArea you can use to test your Code Assistant. Just implement the three methods and run the project to test and debug. When you're sure your Code Assistant is ready, create an IDE Script from the template included in the Code Assistants folder and copy your code from Code Assistant Tester project into your script. When your script is ready, save it with the file extension *xojo_code_assistant*. Next, copy it into the Scripts folder next to the Xojo application itself. The scripts are loaded when the IDE is launched. However, you can force the Code Editor's contextual menu to refresh by holding a modifier key (:kbd:`⌘` key on macOS, :kbd:`Control` key on Linux and Windows) while clicking to open it. .. tip:: If you instead create a Scripts folder at Documents > Xojo > IDE then save your scripts there, Xojo will load them regardless of which version of Xojo you use. .. _/getting_started/using_the_ide/code_editor/useful_keyboard_shortcuts: Useful keyboard shortcuts ************************* While working in the code editor, there are several keyboard shortcuts that will help make you more productive. :kbd:`Shift Return` or :kbd:`⇧ Return` (macOS): Completes the code block. For example, typing ``If True`` and pressing :kbd:`Shift Return` will automatically fill in the ``Then`` and the ``End If`` and place the cursor between them. This works for other code blocks such as ``Select``, ``While``, ``Do``, ``For``, ``#If``, etc. :kbd:`Ctrl Enter` or :kbd:`⌥ Enter` (macOS): Extends code to a new line by automatically adding the ``_`` character. :kbd:`Ctrl \\` or :kbd:`⇧ ⌥ ⌘ \\` (macOS): Toggles the breakpoint on the line on or off. :kbd:`Ctrl '` or :kbd:`⌘ '` (macOS): Comments or uncomments the selected code lines or the line the cursor is on if no code is selected. :kbd:`Alt` :kbd:`↑` / :kbd:`↓` or :kbd:`⇧ ⌥` :kbd:`↑` / :kbd:`↓` (macOS): Moves a line/block up or down. :kbd:`Shift Alt` :kbd:`↑` / :kbd:`↓` or :kbd:`⇧ ⌥` :kbd:`↑` / :kbd:`↓` (macOS): Duplicates the current line/block up or down. :kbd:`Ctrl-Double-Click` or :kbd:`⌘-Double-Click` (macOS): Moves the cursor to the declaration line of the local variable. Also refer to the :doc:`Full Keyboard Shortcuts`. .. _/getting_started/using_the_ide/code_editor/command_bar: Command bar ----------- The Code Editor has its own command bar with the following features, in order from left to right: .. _/getting_started/using_the_ide/code_editor/add: Add *** The Add button is used to add code-related items to the window or web page. This includes: * :ref:`Event Handler` * Menu Handler (desktop projects only) * :doc:`Method` * Code Editor * :doc:`Property` * :ref:`Computed Property` * :ref:`Constant` * :ref:`Delegate` * :doc:`Enumeration` * :ref:`Event Definition` * :ref:`Shared Computed Property` * :ref:`Shared Method` * :ref:`Shared Property` * :ref:`Structure` * :ref:`Using Clause` .. _/getting_started/using_the_ide/code_editor/view_layout: View layout *********** This button is grouped with View Code and is a toggle. When viewing code, you can click this button to quickly switch back to the :doc:`Layout Editor` to see the last item you were working on. .. _/getting_started/using_the_ide/code_editor/view_code: View code ********* This button is grouped with View Layout and is a toggle. When viewing code, this button is selected. When not viewing code, you can click this button to quickly switch back to the Code Editor to see the last item you were editing. .. _/getting_started/using_the_ide/code_editor/comment/uncomment: Comment/uncomment ***************** Use this button to comment out the selected code in the code editor. If no code is selected, then the current line is commented out. If the code is already commented, then this will uncomment the code. This uses the comment character you have specified in :doc:`Coding settings`. .. _/getting_started/using_the_ide/code_editor/standardize_format: Standardize format ****************** Use this button to automatically case-corrects keywords on the line which contains the cursor. .. _/getting_started/using_the_ide/code_editor/toggle_line_numbers: Toggle Line Numbers ******************* Use this button to toggle the display of line numbers in the gutter to the left of the code editor as shown above. .. _/getting_started/using_the_ide/code_editor/analyze_item: Analyze item ************ Use Analyze Item to validate the source code for the current project item. Analyze checks the project item for compile errors and warnings (such as unused variables). Refer to :ref:`Analyzing the Project` for more information. .. _/getting_started/using_the_ide/code_editor/notes_editor: Notes Editor ------------ The Notes Editor is a simple text editor where you can enter notes related to the project item. Uses for Notes: * Technical documentation * Long comments * Commonly used source code snippets * Design documentation =============== Constant Editor =============== The Constant Editor is used to add constants to project items such as Windows, Web Pages, Views, classes and modules. Choose Constant from the command bar for the project item to add a constant. In the Inspector you can specify the name of the constant, its default value, type and scope. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/constant_editor_constant_editor_2022r4.png In addition a String constant can be made "localizable" which is a localized string whose value can changed based on OS or language. .. _/getting_started/using_the_ide/constant_editor/see_also: .. seealso:: :ref:`Adding Constants`, :doc:`Localization` topics ================== Enumeration Editor ================== An enum or enumeration is a data type consisting of a set of named values, which are called elements. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/enumeration_editor_enumerations_editor.png You can set these properties for an enumeration: * **Name**: The name of the enumeration. This cannot be left blank. * **Type**: Enumerations are always an Integer type and default to “Integer”. You can change the type to other Integer types (such as UInt64) should you need to use larger values for the enumeration elements. * **Scope**: Scope indicates what parts of your code can access the enumeration. Choices are Public, Protected and Private. * Public enums can be called from anywhere in your code with no restrictions. * Protected when used in classes can be used only within the class itself or within a subclass. When used with modules, the enumeration can be used only in conjunction with the module name. * Private enums can only be called by the module or class that contains the enum. Use the “+” icon to add a new enumeration element to the set. Use the “-” button to remove an element from the set. When you add an element, you also give it a name. The name is used to refer to the value. You can edit the name by clicking on it once to select it and a second time to edit it. .. note:: Scope is indicated in the Navigator via the background color of the item. An item that is public will have no background color. An item that is Protected has a yellow background. An item that is Private will has a red background. .. _/getting_started/using_the_ide/enumeration_editor/see_also: .. seealso:: :doc:`Enumeration` data type; :doc:`Enumerations` topic ====================== File Type Group Editor ====================== Your desktop apps may need to prompt users to select files. Apps may also want to create their own files, with specific icons for files used by the app. You do all these things by creating File Type Groups with the File Type Group editor. There are many different file types. The type of a file defines a unique type of data stored in that file. For example, a text file stores text while a PNG file stores PNG images. Files have a file extension (or suffix) that defines the file type. For example, a Text file has the extension "txt". A file named "MyNotes.txt" is recognized as a Text file. Rather than writing code that deals directly with all of this, you create File Types Sets to abstract you from the details. A File Type Group contains one or more file types that represents a specific file type and contains information on how to identify the file. Each file type has a name that is used in your code when opening and creating files. This allows you to work with names you can choose and easily remember instead of cryptic codes. It also abstracts your code from the operating system, making it easier for you to create versions of your app for other operating systems. What you specify in your File Type Group is used to tell the OS how your apps deals with files. .. _/getting_started/using_the_ide/file_type_group_editor/adding_file_types: Adding File Types ----------------- To create a File Type Group, select Insert > File Type Group from the toolbar or main menu. Use the buttons in the File Type Group editor command bar to add a custom File Type or add a Common File Type (file types for standard file formats). .. _/getting_started/using_the_ide/file_type_group_editor/removing_file_types: Removing File Types ------------------- With the cursor in any field of the File Type, click the X button to remove it. .. _/getting_started/using_the_ide/file_type_group_editor/common_file_types: Common File Types ----------------- These are file types for standard file formats, such as mp3, text, png, etc. When you choose a common file type, it is added to the File Type Group with the correct properties filled in for you. This is what "text/plain" looks like: Generally you should not change any of the settings for a common file type. For an example on how to use common file types in your apps, refer to the Using File Type Group section. Common file types should always have their UTI type set to "Imported". .. _/getting_started/using_the_ide/file_type_group_editor/custom_file_types: Custom File Types ----------------- Custom file types are used for files that are owned (or created) by your app. These are files that your app creates and knows how to handle. For an example on how to use custom file types in your apps, refer to the Using File Type Groups section. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/file_type_group_editor_file_type_editor.png .. _/getting_started/using_the_ide/file_type_group_editor/general_file_type_properties: General File Type properties ---------------------------- When you create a custom file type, you'll have to specify the property values so that your file type is recognized by the OS. These are the properties that are used with a File Type Group: * **Name**. Required. The name of the file type. You will use this when referencing the specific file type in your code. * **Display Name**. Optional. This is displayed in file dialogs. * **Description**. Optional. A description for the file type to help you remember its purpose. * **Extensions**. Required. The file extensions for the file type (the "." prefix is optional). When you have multiple extensions, list them all separated by semi-colons. * **Mime Types**. Optional and only used for Mac apps. Mime types are standard media and file format types. The IANA has a list of all Mime types. For example, JPEG has this Mime type: image/jpeg. Your own custom file types are not likely to have a Mime Type since they are not a standard file format. A Mime type can be provided to help provide additional information to other apps that may use the file. .. _/getting_started/using_the_ide/file_type_group_editor/macos-only_file_type_properties: macOS-Only File Type properties ******************************* .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/file_type_group_editor_file_type_set_-_edit_icon.png * **Icon**. (Mac-only) Optional. The Image Set contains all the icon sizes (and masks) required for a file type. Drag the image you want to use as the icon for the file type on to the image area of the file type and it will be scaled to fit all the sizes. This image will be used as the file's icon in the Finder on macOS. After dragging an image to the file type, you can click on it to display the Edit Icon window which shows you all the different sizes along with the images and masks. You can drag alternate images into this editor, copy/paste between areas and delete individual images using the contexual menu or standard keyboard commands. Not all images are required, but any images that are added must also have a mask. The Image Set is only used for macOS apps. * **File type is unique to this app CheckBox**. Indicates that this file type is uniquely created by and/or read by this application. If this is checked, your application is assumed to both read and write this file type. If it's unchecked, this file type is one that your app does not own, but would like to use. An example would be if you want your app to be able to open PNG image files. This checkbox should be left unchecked for the Common File Types (see above). .. warning:: Do not declare a standard file type (such as PDF or PNG) as an exported type as this may alter standard system behavior. * **Identifier**. Required for Mac apps that want to use a file type. The Uniform Type Identifier is a unique identifier used by macOS to identify a file. Following the reverse-DNS format beginning with com.companyName.myAppName-DisplayName is a simple way to ensure uniqueness. An example of a UTI might be: com.example.appname-contactdata. All characters in an identifier must be lowercase. The following characters are allowed: a-z, 0-9, - and . * **Conforms To**. Required for Mac apps that want to use a file type. The UTIs to which the above UTI Identifier conforms. UTIs are hierarchical, so you need to provide a "parent" or "base type" for your file type's UTI. Apple maintains a complete list of System-Declared Uniform Type Identifiers that you can use. If you don't want to identify your file content to other apps, you can use the base type of "public.data". If you'd like other apps to be able to recognize your file content, you can use a more specific type. For example, if your data file contains any type of text you can use "public.text" to allow other apps that work with text to be able to open your file. You can list multiple UTIs by separating them with commas. Some useful UTIs include: * public.data: Base physical type for byte streams (flat files, pasteboard data, and so on). * public.database: Base functional type for databases. * public.text: Base type for all text, including text with markup information (HTML, RTF, and so on). * public.html: HTML text. * public.xml: XML text. * **macOS Types**. Optional. The four-character Mac ostype used by the file type. An example would be "JPEG". This is retained for backwards compatibility. This is used to indicate that your application reads this type of file but not that it writes it. * **Role**. The role indicates if your application is used only for viewing, editing and viewing or executing the file. * **Rank**. Enabled only if the Role is set to Edit, View or Execute. MacOS uses rank to determine which app to use to open a file. The order of precedence is Owner, Default, Alternate. Default means this app is an opener of files of this type; this value is also used if no rank is specified. This would be appropriate for files uniquely created by your application. Owner means this app is the primary creator of files of this type. Alternate means this app is a secondary viewer of files of this type. .. _/getting_started/using_the_ide/file_type_group_editor/mac_and_ios_information: Mac and iOS information ----------------------- For Mac and iOS apps, the File Type information specified above is used to create a UTI that is included in the Info.plist for the app. A properly set up file type and role enables these OS features: * File selection dialogs can limit the selection of files * Opening a file or dropping a file onto the app's Dock icon will automatically launch the app and pass the file to the App.OpenDocument event. * The File Type icon will appear in the Finder for files created by the app To learn more about how macOS and iOS use UTIs, refer to these Apple documentation topics: * :doc:`Uniform Type Identifiers` * `Uniform Type Identifier Concepts `_ * `Declaring New Uniform Type Identifiers `_ * `IANA.org MIME Types `_ .. _/getting_started/using_the_ide/file_type_group_editor/windows_information: Windows information ------------------- On Windows, only the extensions need to be specified in order for file dialogs to limit the selection of files. Your Windows app installer needs to create Registry entries to set an associated file icon and allow opening of documents to automatically launch the app. For more information on this refer to the sample Inno Setup Scripts for 32-bit and 64-bit apps. * :doc:`Creating an Installer with the Inno Setup Script (32-bit apps)` * :doc:`Creating an Installer with the Inno Setup Script (64-bit apps)` .. _/getting_started/using_the_ide/file_type_group_editor/see_also: .. seealso:: :doc:`Understanding File Types` topic ============================= Find, Errors, Messages panels ============================= There are three buttons at the bottom of the workspace that open panels for Find, Errors and Messages. The panels may also be opened automatically as`needed, such as when there are compiler errors or you want to show all bookmarks. You can quickly switch between the various panels using the keyboard shortcuts :kbd:`Ctrl Shift >` and :kbd:`Ctrl Shift <` on Windows/Linux and :kbd:`⇧ ⌘ >` and :kbd:`⇧ ⌘ <` on macOS. The Panel area is closed when the project is Run if the preference "Hide Bottom Pane on Run" is checked. To maximize typing speed in the Code Editor, you should close this panel when it is not in use. Typing in the Code Editor can force the panel to update so that it shows changed code and this can slow down typing in large projects. .. _/getting_started/using_the_ide/find,_errors,_messages_panels/find: Find ---- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/find-_errors-_messages_panels_find_panel.png The Find Panel is used to search (and optionally replace) text in your project. It searches instantly as you type text in the Find field. You can use the scope control to change the scope of the search and the “gear” button to change the matching criteria. The scope choices are: * Entire project: Searches the entire project (this is the default). * This item & subclasses: Searches just the currently selected project item in the Navigator and any of its subclasses. * This item: If the Code Editor is visible, this item is visible and searches just the text in the Code Editor. * Selected Text: If the Code Editor is visible and has selected text, then this option is displayed and used to search only within the selected text. By default your search text is matched as case-insensitive and will find partial matches. This means that searching for "List" will find things like "myList" or "NameListBox". You can change the matching criteria: * **Whole word** - Only searches for your text as a whole word. * **Match Case** - Does a case-sensitive search. * **Use RegEx** - Use a Regular Expression to search for matching text. RegEx searches apply to a single line at a time. Click once on a Find result to jump to where it is in your project. Find only searches the items displayed in the Navigator. If you have filtered the Navigator using either the :ref:`Filter` or :ref:`Jump Bar`, then Find only finds based on what is displayed. .. _/getting_started/using_the_ide/find,_errors,_messages_panels/bookmarks_and_breakpoints: Bookmarks and breakpoints ************************* You can also display a list of :ref:`breakpoints or bookmarks` in this area, by choosing the appropriate menu option: * Project > Bookmarks > Show All * Project > Breakpoints > Show All .. _/getting_started/using_the_ide/find,_errors,_messages_panels/errors: Errors ------ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/find-_errors-_messages_panels_the_errors_pane.jpg :alt: The Errors Pane The Errors Panel displays compiler errors and warnings. This panel appears automatically when you Run or Build if there are compiler errors. Click on the issue to jump to where it is in your project. Warnings are only displayed when you use the “Analyze Project” or “Analyze Current Item” commands. You can choose to display errors or warnings by type or by location using the selector at the top of the Error Panel. You can choose which warnings appear by using the Analysis Warnings window (Project > Analysis Warnings). Refer to Analyzing the Project for more information. .. tip:: If a warning about an unused method parameter is displayed, you can right-click on it then choose "Add pragma unused" to add ``#pragma unused`` followed by the parameter name to the method. .. _/getting_started/using_the_ide/find,_errors,_messages_panels/messages: Messages -------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/find-_errors-_messages_panels_messages_panel.png The Messages Panel is used when running your project using the debugger. When you run your project, messages for Application Launched and Application Ended are automatically created. Additional system messages may also be displayed here. In addition, the output from the :ref:`System.DebugLog` method appears in the Messages Panel allowing your applications to generate their own logging messages to aid your debugging and testing. You can search for messages using the Search field in this panel. The Next and Previous buttons allow you to navigate values that match what has been entered into the Search field. The Clear button clears all the messages in the list. The Messages panel shows the latest 1500 lines of output. If you need access to older messages, use the appropriate OS system log viewer. ================ Image Set Editor ================ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/image_set_editor_image_set_editor.png :scale: 25 % The Image Set Editor lets you manage pictures and images dragged into your iOS projects and your desktop and web projects when the Supports HiDPI setting in Shared Build Settings is set to ON (this is the default on MacOS and Windows). The Image Set Editor displays three sizes for the image. When you drag an image into your project it is automatically assigned to the 1x size. This size is used for standard (non-HiDPI) screen resolutions. You can add additional 2x and 3x resolutions for use by HiDPI screens. All images must have the same aspect ratio. For example if your 1x image is 64x64 @ 72DPI, then the 2x image must be the same ratio (1:1) and would typically be 128x128 @ 72DPI or 64x64 @ 144DPI. Make sure that that the tool you use to create or edit your images properly sets the image DPI (dots per inch). Some tools (such as Photoshop) do not properly set the DPI which can cause Xojo to interpret the image as the wrong size. These images are not copied to your project, but are referenced as external items. This means if you move the external files or copy the project, the images may not be found. It is often convenient to create a ProjectImages folder next to your project where you place your images files and then drag them from there. You can click on any image in the Image Set Editor to select it. Press the Delete key to delete the image. You can use the contextual menu to open the image in the system default image editor or to show the location of the image on the drive. In your source code you refer to the image by its name as shown in the Navigator. If there are multiple sizes, your app will automatically choose the most appropriate size to display at runtime based on the type of screen being used. If there is only a single size image then it will be scaled as appropriate for the screen resolution. .. code:: xojo ImageView1.Image = MyImage You can rename an image in your project (this does not affect the name of the images on the drive) by change the Name property in the Inspector. .. _/getting_started/using_the_ide/image_set_editor/related_hidpi_information: Related HiDPI information ------------------------- * :doc:`Desktop HiDPI` * :doc:`Web HiDPI` * `Video: HiDPI `_ ================== Included `plugins` ================== You can use optional `plugins` (also called external `plugins`) with your Xojo apps to add additional functionality in the form of classes or UI controls. These `plugins` are files that are copied to the `Plugins` folder and are then loaded the next time Xojo starts. By default, Xojo ships with these database `plugins` installed into the `Plugins` folder: * MySQLCommunityPlugin for :doc:`MySQLCommunityServer` * ODBCPlugin for :doc:`ODBCDatabase` * PostgreSQLPlugin for :doc:`PostgreSQLDatabase` You can remove any of the `plugins` you are not using in order to reduce start time and lower memory usage. To use the Microsoft Office Automation classes, you will have to copy the MSOfficeAutomation plugin to the Plugin folder. Many third-party developers also offer `plugins` for use with Xojo, which you can also copy to the `Plugins` folder to use with Xojo the next time it starts. To see what `plugins` have been loaded by Xojo, go to the Help menu, select About Xojo and then click the "Loaded `Plugins`" tab where you'll see a list of loaded `plugins`. Loading `plugins` takes additional startup time and uses additional memory even if you don't use them in your project, so it is best to only put the `plugins` you are actually using in the `Plugins` folder and not necessarily include every plugin you have. Apps can use a plugin that contain 32-bit parts, but it must also have 64-bit parts in order for the plugin to be recognized by Xojo. .. _/getting_started/using_the_ide/included_plugins/windows_plugin_placement: Windows `plugin` placement -------------------------- Since a number of items are implemented as internal `plugins`, you may well see a Libs folder even when you have not installed any external (or third party) `plugins`. For 32-bit apps on Windows: * GUI apps include XojoGUIFramework32.dll in the Libs folder * All plugin DLLs appear in the Libs folder. For 64-bit apps on Windows: * Seveal DLLs now appear in the same folder as the exe itself. For more information about this refer to 64-bit Guidelines. * GUI apps include XojoGUIFramework64.dll in the same folder as the exe itself. * Visual Studio runtime DLLs appear in the same folder as the exe itself. * CEF3 (Chromium Embedded Framework) DLLs (and related files) appear in the same folder as the exe itself if your project is using the :doc:`DesktopHTMLViewer`. * Other plugin DLLs appear in the Libs folder. .. _/getting_started/using_the_ide/included_plugins/linux_plugin_placement: Linux `plugin` placement ------------------------ All `plugins` are included in the Libs folder. Since a number of items are implemented as internal `plugins`, you may well see a Lib folder even when you have not installed any external (or third party) `plugins`. .. _/getting_started/using_the_ide/included_plugins/internal_plugins: Internal `plugins` ------------------ The items and classes listed in this section are currently implemented as internal `plugins` in Xojo. This is an implementation detail that is subject to change at any time. Some high-level classes internally use functions implemented as internal `plugins`. For example, HTTPSocket and POP3Socket both use the MD5 function for authentication purpose thus causing the MD5 plugin to be included in the Libs folder. * Appearance Pak * DesktopBevelButton * DesktopDisclosureTriangle * DesktopHTMLViewer * DesktopImageViewer * DesktopPopupArrow * DesktopProgressWheel * DesktopSegmentedButton * DesktopSeparator * DesktopUpDownArrows * Game Input * GameInputDevice * GameInputManager * GameInputElement * Internet Encodings * EncodeBase64 * DecodeURLComponent * DecodeBase64 * EncodeQuotedPrintable * DecodeQuotedPrintable * EncodeURLComponent * Crypto * MD5 * MD5Digest * Any Crypto-related features * XojoScript * `RegEx` * RegEx * RegExOptions * RegExException * RegExSearchPatternException * RegExMatch * Shell * Shell * ShellNotRunningException * ShellNotAvailableException * Sockets * SSLSocket * XML * XMLAttributeList * XMLNodeList * XMLContentModel * XMLReader * XMLDocument * XMLReaderException * XMLDOMException * XMLXsltHandler * XMLElement * XMLNamespaces * Anything else that starts with "XML". ========= Inspector ========= .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/inspector_inspector_2022r4.png :scale: 50 % :alt: Inspector showing properties of DesktopButton The Inspector displays information (properties) for the currently selected item. This could be properties of controls on a Layout Editor, project items in the Navigator, Build Settings or methods or properties when using the :doc:`Code Editor`. The Inspector shares its space with the :doc:`Library` on the right side of the workspace, but you can also set a preference to have it display as a floating palette. To show the Inspector, click the Inspector button on the toolbar, select View > Inspector from the menu or press the shortcut key (⌘-I on macOS, Ctrl-I on Windows and Linux). What you see in the Inspector depends on what you have selected. For controls in the :doc:`Layout Editor`, you will see all the properties available for selected control. These properties are grouped by topic to make it easy to find what you are looking for. The Inspector may have more than one tab at the top depending on what is being viewed. More common items are contained in the "ID" tab and less commonly used (or advanced items) are in the "Gear" tab. .. _/getting_started/using_the_ide/inspector/changing_properties: Changing properties ------------------- There are several different types of properties and values they can contain. .. _/getting_started/using_the_ide/inspector/numeric_properties: Numeric properties ****************** If the property you want to set is numeric -- such as the Left, Top, Width or Height properties -- you can either enter a number of an expression that evaluates to a number. To enter an expression, you can use the simple arithmetic operators: +, -, \*, /, \\ (integer division), % (mod) and ^ (exponentiation). You can also use parentheses to control the order of evaluation. Additionally you can use references to property values by name, allowing you to write expressions such as "Top*2" or "Width + Left". If an expression is invalid on its own, the current value is prepended; this allows you to (for example) enter "\*2" as a handy shortcut for doubling the current value when multiple objects are selected. You add a value to the current value, use "+ + value". For example, to add 10 use "+ + 10", since "+10" will be treated as just 10. These expressions are only evaluated once when the control first appears on the layout. They are not evaluated again (should you reference another property by name that changes, for example). .. _/getting_started/using_the_ide/inspector/boolean_properties: Boolean properties ****************** The values of Boolean properties are displayed as ON/OFF switches in the Inspector. A value of False is indicated by OFF and a value of True is indicated by ON. You change the value by clicking on the switch to toggle it. You can right-click on Boolean properties and choose Assign Constant from the contextual menu to assign a Boolean value using a constant from the project. The constant name should be prefixed with a "#". To edit or remove the constant, right-click on it and choose Assign Constant or Remove Constant from the contextual menu. .. _/getting_started/using_the_ide/inspector/text_properties: Text properties *************** Text properties are entered by typing into the Text Field in the Inspector. If the text you want to enter is long, you can click the Text icon to open a larger window with a Text Area. .. _/getting_started/using_the_ide/inspector/constants: Constants ********* You can use a constant as a value for a property. For example, you can create a constant that contains the caption for all the "Accept" buttons in your project. If you want to change the caption, you only need to change the value of the constant rather than edit the values for each button. This technique is also useful for :doc:`localization`. A good place to create constants for this purpose is in a :doc:`Module`. To use a constant for a property value, precede its name with "#" (pound sign). For example, if you want to use a global constant (in a module) named AcceptButtonText, you would refer to it in the Caption property of a button as "#AcceptButtonText". .. _/getting_started/using_the_ide/inspector/choosers: Choosers ******** Some properties require you to choose a value from a fixed list, typically displayed as Pop-up Menus. Choose the value from the menu. For controls that accept a picture as a property value, you can select the picture by choosing it from the chooser for the property (all pictures in the project are added to the list automatically). The last menu item of the list of pictures is "Browse", which allows you to select a picture from your drive to add to the project and use. .. _/getting_started/using_the_ide/inspector/color_properties: Color properties **************** Color properties display as the selected color. Click on the color to display a Color Picker window for choosing another color. .. _/getting_started/using_the_ide/inspector/multiple_selection: Multiple selection ****************** When more than one item is selected, the Inspector displays only those properties common to all of the selected items. When you change a common property, it is changed for all the selected items. .. _/getting_started/using_the_ide/inspector/palettes: Palettes -------- By default, the Library and Inspector display on the right side of the window and only one appears at a time. You can quickly toggle between the Library and Inspector using View > Toggle Palettes in the menu. If you would prefer to position the Library and Inspector anywhere or have them both on screen at the same time, you should use the preference to display them as separate Palettes, which you can then position however you want (even a second display). In the General area of Preferences (Options on Windows and Linux) change "Show Library and Inspector" from "In the project window" to "As floating palettes". .. _/getting_started/using_the_ide/inspector/see_also: .. seealso:: :doc:`Navigator`, :doc:`Layout Editor`, :doc:`Library`, :doc:`Code Editor`, :doc:`Modules`, :doc:`Localization` topics ================== Keyboard shortcuts ================== .. _/getting_started/using_the_ide/keyboard_shortcuts/general: General ------- Hold down Alt (Windows), Shift (on Linux) or Option (on Mac) to prevent loading the user interface state for the IDE. This prevents the loading of tabs and defaults Xojo to a standard window size. * :kbd:`⌥` = Option key * :kbd:`⌘` = Command key * :kbd:`Ctrl` = Control key * :kbd:`⇧` or :kbd:`Shift` = Shift key * :kbd:`Alt` = Alternate key .. _/getting_started/using_the_ide/keyboard_shortcuts/code_editor: Code Editor ----------- These are the keyboard shortcuts that work with the :doc:`Code Editor`. Add Shift to many of the these commands to also select the text. In general, the Code Editor on Mac uses `standard Cocoa text editor keyboard shortcuts `_, so there may be other shortcuts available that are not listed here. .. csv-table:: :header: "Mac", "Windows", "Linux", "Description" :widths: auto ":kbd:`⌘` + [Double-click on method]",":kbd:`Ctrl` + [Double-click on method]",":kbd:`Ctrl` + [Double-click on method]","Navigate to the method that was double-clicked." ":kbd:`⌘ '`",":kbd:`Ctrl '`",":kbd:`Ctrl '`","Comment or uncomment the selected block of code. If not code is selected, then it works on the current line." ":kbd:`⌥ Enter`/:kbd:`⌥ Return`",":kbd:`Ctrl m` :kbd:`Ctrl Enter` :kbd:`Ctrl Return`",":kbd:`Ctrl m` :kbd:`Ctrl Enter` :kbd:`Ctrl Return`","Adds the :doc:`Line Continuation` character (_) and moves you to a new line." ":kbd:`⌘ \\`",":kbd:`Ctrl \\`",":kbd:`Ctrl \\`","Toggles the breakpoint on/off for the current line of code." ":kbd:`⇧ Return`",":kbd:`Shift Return`",":kbd:`Shift Return`","This will auto-complete a code block. For example, if you type *If True* and then press Shift-Return, the code editor will auto-complete to look add the Then, a blank line and the *End If* then position the cursor on the blank line between the If and End If. This works with all code blocks, such as If...Then, While...Wend, For...Next, Select Case, etc." ":kbd:`⌘ ←`",":kbd:`Home`",":kbd:`Home`","Moves the cursor to the beginning of the line." ":kbd:`⌘ →`",":kbd:`End`",":kbd:`End`","Moves the cursor to the end of the line." ":kbd:`Ctrl t`","","","Transposes the characters at the cursor." ":kbd:`Ctrl k`","","","Deletes characters from the cursor to the end of the line." ":kbd:`⌥ Backspace`",":kbd:`Ctrl Backspace`",":kbd:`Ctrl Backspace`","Deletes the previous word." ":kbd:`⌘ Backspace`","","","Deletes characters from the cursor to the beginning of the line." ":kbd:`⌥ ←`",":kbd:`Ctrl →`",":kbd:`Ctrl →`","Moves forward one word at a time." ":kbd:`⌥ ←`",":kbd:`Ctrl ←`",":kbd:`Ctrl ←`","Moves backward one word at a time." ":kbd:`⌥ Delete`",":kbd:`Ctrl Delete`",":kbd:`Ctrl Delete`","Deletes the next word." ":kbd:`⌘ ↑` or :kbd:`Home`",":kbd:`Ctrl Home`",":kbd:`Ctrl Home`","Go to first line of code." ":kbd:`⌘ ↓` or :kbd:`End`",":kbd:`Ctrl End`",":kbd:`Ctrl End`","Go to last line of code." ":kbd:`⌥ ↑`",":kbd:`Alt ↑`",":kbd:`Alt ↑`","Move a line/block of code up." ":kbd:`⌥ ↓`",":kbd:`Alt ↓`",":kbd:`Alt ↓`","Move a line/block of code down." ":kbd:`⇧ ⌥ ↑`",":kbd:`⇧ Alt ↑`",":kbd:`⇧ Alt ↑`","Duplicate a line/block of code up." ":kbd:`⇧ ⌥ ↓`",":kbd:`⇧ Alt ↓`",":kbd:`⇧ Alt ↓`","Duplicate a line/block of code down." .. note:: Right-clicking/Control-clicking on a value in the Key column will allow the assignment of keys that cannot be typed such as Function keys, Backspace, etc. .. _/getting_started/using_the_ide/keyboard_shortcuts/adding_your_own_menu_shortcuts: Adding your own menu shortcuts ------------------------------ To add or change menu shortcuts, use the Menu shortcut editor in the :doc:`General settings`. In this editor there is an entry for each Xojo menu item. You can use this to change, add or remove a shortcut for any of the menu items. On Mac you can also add your own shortcuts to menu items using System Preferences > Keyboard > Shortcuts and then select "App Shortcuts" in the list. Add Xojo manually and then specify the exact menu for which you want a shortcut. For example, to create a shortcut for clearing all breakpoints, you would create an entry like this and specify the keyboard shortcut you want (use "->" to specify nested menus): .. code:: xojo Project->Breakpoint->Clear All ============= Layout Editor ============= The Layout Editor is the primary editor you use to design the user interface for your app, whether it is a desktop, web or iOS app. In desktop apps, you use the Layout Editor to design your Windows and Container Controls. In web apps, you use the Layout Editor to design Web Pages, Container Controls and Web Dialogs. In iOS apps, you use the Layout Editor to design Views. .. _/getting_started/using_the_ide/layout_editor/layout_area: Layout area ----------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_window_layout_editor.png The Layout Area displays as either a window, web page or view, depending on the type of project. Regardless, you add controls to the area by dragging them from the :doc:`Library` or the :doc:`Navigator` onto the Layout Area. You can drag controls around the layout editor to position them. Hold down Option before dragging a control creates a duplicate control at the new location (the original control is unchanged). You can drag some controls onto other controls in order to make one control the parent of a another control (control parenting). For example you may add a Tab Panel control to a layout and then drag a button onto it. The Tab Panel control now becomes the button's parent rather than the window itself. When you do this the parent control has a red outline around it, which you can control in the :doc:`Layout settings`. Other controls that make useful parents include the Group Box, Page Panel, Rectangle and the Canvas. .. _/getting_started/using_the_ide/layout_editor/command_bar: Command Bar ----------- The Layout Editor has its own command bar (aka toolbar) with the following features, in order from left to right: .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_layout_editor_command_bar.png .. _/getting_started/using_the_ide/layout_editor/add: Add *** The Add button is used to add code-related items to the layout. This includes: * Event Handler (refer to the Event Handlers topic below) * Menu Handler (desktop only) * Method * Note * Property * Computed Property * Constant * Delegate * Enumeration * Event Definition * External Method * Shared Computed Property * Shared Method * Shared Property * Structure * Using Clause .. _/getting_started/using_the_ide/layout_editor/view_layout: View Layout *********** This button is grouped with View Code and is a toggle. When viewing a Layout, this button is selected. When not viewing a layout, you can click this button to quickly switch back to the Layout Editor to see the layout for the last item you were working on. .. _/getting_started/using_the_ide/layout_editor/view_code: View Code ********* This button is grouped with View Layout and is a toggle. When viewing a Layout, this button can be used to switch back to see the :doc:`Code Editor` for the last item being edited. .. _/getting_started/using_the_ide/layout_editor/set_default_value: Set Default Value ***************** The Set Default Value button allows you to set the default value for various controls. Refer to the Default Values topic below. .. _/getting_started/using_the_ide/layout_editor/lock_position: Lock Position ************* The Lock Position button is used to lock controls so that they cannot be moved by the mouse while editing the layout. You can still move the control using the arrow keys, change properties and delete it as necessary. .. _/getting_started/using_the_ide/layout_editor/show_tab_order: Show Tab Order ************** .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_tab_order_editor.png Available for desktop and web projects only. Show Tab Order displays each control in the Tab Order Editor. The controls on the layout appear in the editor list in tab order. You can drag controls around to change their tab order on the layout. .. _/getting_started/using_the_ide/layout_editor/auto-adjust_tab_order: Auto-Adjust Tab Order ********************* .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_auto-adjust_tab_order.png This item displays a dialog that will automatically adjust the tab order for the controls on the layout, which is faster than doing it manually. The choices are "Left to Right Top to Bottom" and "Top to Bottom Left to Right" with the flow indicated by the graphic in the dialog. .. _/getting_started/using_the_ide/layout_editor/show_measurements: Show Measurements ***************** .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_show_measurements.png The Show Measurements button allows you to better visualize your layout. When you click enable Show Measurements view, you can move the mouse around your layout to see various measurements, such as how far a control is from the top of the layout or how far controls are from each other. Select multiple controls to see various measurements, including distances between the controls. .. _/getting_started/using_the_ide/layout_editor/ordering: Ordering ******** The Ordering buttons (Order Forward, Order Front, Order Backward and Order Back) are used to change the ordering of the controls on the layout. .. _/getting_started/using_the_ide/layout_editor/fill: Fill **** The Fill Width and Fill Height buttons expand the selected control to fill the remaining space in its container. .. _/getting_started/using_the_ide/layout_editor/alignment: Alignment ********* The Alignment buttons (Align Left, Align Right, Align Top and Align Bottom) are used to align controls on the layout with each other. Select all the controls you want to align and then choose one of the buttons to align them. .. _/getting_started/using_the_ide/layout_editor/spacing: Spacing ******* The Space Horizontally and Space Vertically buttons align the selected controls so that they are spaced equally apart. .. _/getting_started/using_the_ide/layout_editor/light/dark_mode_preview: Light/Dark Mode Preview *********************** Allows you to preview your window in light and dark modes. .. note:: On Linux, this requires that the libwebkit2gtk library be installed. .. _/getting_started/using_the_ide/layout_editor/shelf: Shelf ----- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_layout_editor_with_a_control_on_the_shelf.png :scale: 50 % Some controls that are added to the layout are not actually a control on the layout, such as a Timer. When you add these types of controls to the layout, a Shelf is automatically displayed at the bottom of the Layout Editor and the control is added to it. In web apps, Web Dialogs appear on the shelf. In desktop apps, a toolbar added to a Window appears on the shelf. .. _/getting_started/using_the_ide/layout_editor/default_values: Default values -------------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_default_value_for_pushbutton.png A control on the Layout Editor can have its default value specified by pressing Return while the control is selected, by clicking the Pencil rollover icon that appears when you move the mouse over a control, or by clicking the Set Default Value button on the Layout Editor Toolbar . This opens a popover window to enter the default value. For example, with a PushButton, you can specify the Caption. To close the pop-out window, click the small close icon, press Return, click outside the pop-out window or click the “Set Default Value” button on the Layout Editor toolbar. .. _/getting_started/using_the_ide/layout_editor/event_handlers: Event handlers -------------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_event_handlers_for_pushbutton.png To add an Event Handler to your control, you click the Add button and select Event Handler. This opens the Add Event Handler Dialog which displays the events that are available for the control (or the window, web pag` or view if that is what was selected). You can click on each event to view the description of the event. Select one or more events and click OK to create corresponding Event Handlers. You can also press :kbd:`⌘ A` (:kbd:`Ctrl A` on Windows and Linux) to select all the Event Handlers to add at once. You can add multiple events at once by selecting each of them and clicking OK. Event Handlers appear in the Navigator underneath the selected control. You can click on an Event Handler to see its code in the Code Editor. .. _/getting_started/using_the_ide/layout_editor/alignment_guides: Alignment Guides ---------------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/layout_editor_alignment_guides_for_pushbutton.png As you move controls around on a layout you will see alignment guides appear to help with positioning. The guides appear as you drag the control near the edges of the layout or other controls. They are a great way to make sure that controls are aligned to the same positions as other controls on the layout. To disable the display of alignment guides, hold down Command while dragging (or Control on Windows and Linux). .. _/getting_started/using_the_ide/layout_editor/user_controls_(control_subclasses): User controls (control subclasses) ---------------------------------- You can access user controls (subclasses of built-in controls) at the end of all the built-in controls in the Library. If you have turned on "Show Group Banners" for the Library, these controls appear in the Project Controls group of the Library. Another option is to drag the subclass from the Navigator onto the Layout Area. .. _/getting_started/using_the_ide/layout_editor/working_with_controls_on_the_layout: Working with controls on the layout ----------------------------------- You can right-click on any control on the layout to display the contextual menu. From the contextual menu you have these options: .. csv-table:: :header: "Item", "Description" :widths: auto "Add to","Use this command to select *Add Event Handler* from the submenu to add event handlers to the control." "Inspect","Displays the Inspector for the control." "Set Default Value","Displays the Set Default Value popover for the control." "Scope","Changes the scope of the control." "Cut/Copy","Use to cut or copy the control." "Paste","Pastes a control in the clipboard onto the layout." "Delete","Deletes the control. To put it in the clipboard, use Cut." "Duplicate","Creates a copy of the control on the layout. You can also create a copy of a control by clicking on it with the mouse button and then holding Command (Shift+Control on Windows and Linux) while dragging the mouse." "Find","Does a Find using the control name with the results shown in the Find panel." "Print","Prints all the source code for the control." "Extract String Constants","Displays a dialog where you can create constants for use by Text properties on the selected controls." "New Subclass","Creates a new class in the Navigator that uses the control as its superclass." "Select All","Selects all the controls in the layout." "Select","This submenu contains all the controls on the layout. Use it to find and select a control by name or to select a control that it not visible in the layout due to layering." "Lock/Unlock Position","Locks the control at its position on the layout preventing it from being accidentally moved while editing the layout. To move the control, unlock it first." "Edit Superclass","If the control has a superclass, then this command takes you directly to the superclass so you can edit it." .. _/getting_started/using_the_ide/layout_editor/working_with_controls_in_the_navigator: Working with controls in the Navigator -------------------------------------- You can right-clicking (Control-Click on Mac) on any control in the Navigator provides the same set of options as described above. However, if you right-click on a deprecated event of a control, an additional item in the menu is available ("Replace Event With...") which when selected will convert the event to its replacement. ======= Library ======= .. _/getting_started/using_the_ide/library/introduction: Introduction ------------ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/library_library.png :scale: 50 % The Library (also called the Control Library) is a list of all built-in controls that can be added to your layout for your project type. It also contains all the project items that you can add to your project. To show the Library, click the Library button on the toolbar, select View > Library from the menu or press the shortcut key (:kbd:`⌘ L` on macOS, :kbd:`Ctrl L` on Windows and Linux). The Library displays on the right side of the workspace by default, but you can also set a preference to have it display as a floating palette. The Library allows you to display controls in several ways. By default the controls are shown in Large size, but you can also change the size, add group headers and change the sorting. You can make these changes using the small “gear” icon at the top of the Library. Display settings include: * Large Icons * Large Icons and Labels * Small Icons and Labels * Large Icons and Descriptions * Show Group Banners * Sort Alphabetically Hovering the mouse over any control displays its description in the Description Area at the bottom of the Library. If you do not need to see the description, you can resize the area to hide it. Using the drop-down at the top of the Library you can choose to show "All Controls" (all the controls included with Xojo as well as any subclasses of controls you have added to your project), "All Built-In Controls" (only the controls included with Xojo) or you can choose to show only controls from a specific group. You can also use the Search field (also at the top) to quickly search for and show controls by name or type. For example, type “button” to see all the button controls. Lastly, if a control is selected in the Library, you can just start typing to jump to specific controls. There are several ways to use the controls in the Library. You can drag controls from the Library onto the :doc:`Layout Editor` if it supports the control you have selected. You can drag controls from the Library directly to the :doc:`Navigator` to immediately create a subclass of the control. You can also double-click the control to add it to either the Navigator (as a subclass) or the Layout Editor, depending on which one has the focus. Lastly, you can right-click on a control in the Library to display the "New Subclass" menu which creates a subclass of the control and adds it to your project. At the very bottom of the Library (in a group called Project Controls, if you have group headings displayed) is a list of control subclasses from your project. This is an easy way to drag your own custom controls onto a layout. You can exclude controls from appearing in this section by using the "HideFromLibrary" attribute or by choosing "All Built-In Controls" from the drop-down at the top of the Library. If you want to display a description for your own control in the Library, use the "LibraryDescription" attribute and its value text as a description in your class. .. _/getting_started/using_the_ide/library/palettes: Palettes -------- By default, the Library and Inspector display on the right side of the window and only one appears at a time. You can quickly toggle between the Library and Inspector using View > Toggle Palettes in the menu. If you would prefer to position the Library and Inspector anywhere or have them both on screen at the same time, you can display them as separate palettes (which you can then position however you want - even a second display) by choosing Xojo > Preferences on macOS or Edit > Options on Linux/Windows and choosing the *As floating palettes* option. .. _/getting_started/using_the_ide/library/see_also: .. seealso:: :doc:`Navigator`, :doc:`Layout Editor`, :doc:`Inspector` topics =========== Menu Editor =========== .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/menu_editor_menu_editor_-_editing_menu_item_2025r1.png :alt: Menu Editor - Editing Menu Item The Menu editor makes adding menu bars, menus, and menu items to your desktop projects easy. The standard Desktop app template includes a menu bar that is used as the default menu bar for the entire app (MainMenuBar). You can use the default or create additional menu bars (using Insert on the toolbar or menu) for use with specific windows by assigning the menu bar to the Menu Bar property using the Inspector for the Window. The Menu Editor can only be used with Desktop projects. The default menubar for a Desktop app, MainMenuBar, includes File, Edit, Window and Help menus and the standard File and Edit menu items. The File menu has one menu item, Exit (on Windows) or Quit (on macOS and Linux). The properties of the Quit/Exit menu item are supplied, so that the menu item works automatically. You don't need to modify or add to the menu item's properties in order to enable it. The Edit menu is populated with Undo, Cut, Copy, Paste, Delete, and Select All menu items. On macOS the Window menu is automatically populated at runtime with macOS-provided menu items that will make the front most window fill the screen, center the window in the screen, move and resize the window, allow you to move it to another connected device, keep track of all open windows and more. The name of this menu item is ``WindowMenu`` by default and it must be named this for the automatic menu features to work. On macOS the Help menu provides a functional search menu item. On Linux and Windows, the Window and Help menus have no built-in functionality. You can add your own menu items to them or delete them. .. tip:: If you wish to have the Window and Help menus only for the macOS version of your app and not for versions for Linux or Windows, you can remove them at runtime in the :ref:`App.Opening` event using the :ref:`DesktopMenuBar.MenuAt` and :ref:`DesktopMenuBar.RemoveMenuAt` methods. You can drag menus around to rearrange them or move them to entirely new menus. The toolbar has functions for adding new menus and menu items. You can also use Cut/Copy/Paste to move and copy menu items to other areas of the menu. Click on the menu in the Menu Editor and then use the keyboard commands to cut/copy/paste or delete menus. Refer to :doc:`Desktop Menus` for more information on how to create and design menus. .. _/getting_started/using_the_ide/menu_editor/command_bar: Command bar ----------- The command bar provides these commands: .. csv-table:: :header: "Item", "Description" :widths: auto "Platform Display","The platform icons on the toolbar allow you to see how the menu looks on Windows, MacOS and Linux." "Create New Top-Level Menu","Adds a top-level menu to the menu bar." "Create New Menu Item","Creates a new menu item under a selected top-level menu." "Create Separator","Creates a separator item below the selected menu item." "Create Submenu","Creates a submenu below the selected menu item." "Convert Select Menu to a Top-Level Menu","Use this command to convert the selected menu item to a top-level menu." .. _/getting_started/using_the_ide/menu_editor/inspector: Inspector --------- The Inspector displays the properties for the selected menu or menu item. ========= Navigator ========= The Navigator is the area on the left sidebar that allows you to move around (and organize) the items in your project. The Navigator consists of the Filter and Jump Bar, plus the Contents, Run, Profiles and Build Settings sections. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/navigator_navigator_ee_ios.png .. _/getting_started/using_the_ide/navigator/navigator_parts: Navigator parts --------------- * Filter: The Filter is used to only show specific items in the Navigator's Contents section. * Jump Bar: The Jump Bar is used to control the level of detail you see for an item in the project. * Contents: The Contents section contains the items in your project that were added using the Insert button or menu, such as windows, web pages, classes, menus and folders. You can click on project items in the Contents section to view them or edit them. When an item is selected, you can use the arrow keys to move the selection between items. * The Run section is only visible in the Debugger tab when you are running your project. * Profiles: The Profiles section only appears when you have enabled the Profiler and have run your apps in debug mode to get profiler data. * Build Settings: The Builds Settings section contains all build-related information, including the build targets (and their settings) and active build steps used by Build Automation. .. _/getting_started/using_the_ide/navigator/filter: Filter ------ The Navigator has a Filter field at the top that can be used to filter what is displayed in the Contents section. Use the Filter to quickly show specific project items based on your criteria. For example, if you know you have a method that is called “LoadCustomer”, but you don't recall what class or window it is on, you can just type “Custom” in the Filter field to have the Navigator display any project items that have something containing “Custom” in its name (e.g. a method, control name, constant or property). You can then click on the item to see it or edit it. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/navigator_navigator_filter.png This also allows you to type the "location" of a project item and member to quick jump to it, similar to what you can do with "Go To Location". For example: "MyClass.Save" would quickly go to the Save method on MyClass. .. _/getting_started/using_the_ide/navigator/advanced_filtering: Advanced filtering ****************** You can use the "%" character as a wildcard for filtering the Navigator. You can also include a "type" operator to limit the filter to only find things of a certain type, which you can also combine with the wildcard. Here are some examples: .. csv-table:: :header: "Filter Spec", "Result" :widths: auto "Draw%","Displays everything that starts with *Draw*." "CustomerName type:property","Displays only properties that contain *CustomerName*." "ID type:property,constant","Displays only properties and constants that contain *ID*." "Name type:%property,constant","Displays only properties, computed properties and constants that contain *Name*." "type:%property,constant","Displays all properties, computed properties and constants." "% type:window","Displays all Windows." .. _/getting_started/using_the_ide/navigator/jump_bar: Jump bar -------- The Navigator displays items using a hierarchical list. The scope of what is displayed is controlled by the Jump Bar. By default, your entire project is in scope, so the Jump Bar displays your project name. When you double-click on an item that is a parent, the Jump Bar changes to show the parent (this is referred to as “drilling into” the project item). Now only the items that are its children are displayed in the Navigator. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/navigator_navigator_jump_bar.png The “Double click opens item in new tab” setting in Preferences alters the above behavior. When that setting is checked, double-click opens the item in a new tab, so use :kbd:`Option ⌘` when double-clicking on a project item to drill into it (on Windows and Linux, use :kbd:`Shift Control`). Click the name in the Jump Bar to see the entire history and to jump quickly to a specific item in the history. The Jump Bar is incredibly powerful when used with tabs because it allows you to focus the tab on just the specific item you are working with. .. _/getting_started/using_the_ide/navigator/contents: Contents -------- The Contents section shows the items in your project. You can click on project items in the Contents section to view them or edit them. When an item is selected, you can use the arrow keys to move the selection between items. You can do the usual things such as deleting, copying or pasting project items. You can also drag project items to reorganize them or to move them between project items. For example, you can drag a method from one class to another class to move it. In addition, objects can be multi-selected. For example, you can select a Class and a Window, and drag them both into a Folder. .. note:: When an item in the Navigator has a red or yellow background, this indicates it has a :ref:`scope` set. .. _/getting_started/using_the_ide/navigator/run: Run --- The Run section contains your app name only appears in the Debugger tab when you :doc:`run your project`. .. _/getting_started/using_the_ide/navigator/profiles: Profiles -------- If you run your app with Profiling enabled, the Profiles section appears with the results when the app quits normally (not if it crashes or has an exception). This section has a separate entry for time you run with the Profiler enabled. Click an entry to review the methods that were called with their elapsed times. Refer to the :doc:`Code Profiler topic` for more information about the Profiler. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/navigator_navigator_profiles.png .. _/getting_started/using_the_ide/navigator/build_settings: Build Settings -------------- The Build Settings section shows the various build OS targets available to you and is used to view and change the information needed to build and run your project. For Desktop projects, the item “This Computer” is checked by default and contains the settings for the platform you are currently using. For Web projects, the item :doc:`Xojo Cloud` is the default web deployment option. For :doc:`Raspberry Pi` projects, choose Linux and then make sure to go to the inspector and change the Architecture property to "ARM 32-bit". Check the check box next to other targets to create a build for it the next time you build the project. Click on a build target name to change its settings using the Inspector. For more about build settings for the various project types, refer to the :doc:`App Structure` section. The Build section also is where you manage your Build Steps, which are described in the :doc:`Build Automation` section. .. list-table:: * - Desktop Build Settings - .. figure:: images/navigator_desktop_build_settings.png * - Web Build Settings - .. figure:: images/navigator_web_build_settings.png * - Android Build Settings - .. figure:: images/navigator_android_build_settings.png * - iOS Build Settings - .. figure:: images/navigator_ios_build_settings.png Signing your app **************** iOS and macOS projects support application signing. You can sign your app by clicking the disclosure triangle next to iOS or macOS in Build Settings and then clicking on the Sign item. For macOS, enter your `Apple Developer ID `_ to sign the app using that ID which then identifies to the end user that you are the app's developer. .. warning:: Your app must be signed with your Apple Developer ID (which also then requires you to have installed an appropriate `certificate `_ via Xcode) if you want your name on the digital signature instead of no-name, if you are going to notarize the app in a later step, or are going to submit it to the Mac App Store. If you do not provide your ID, the app will be signed with an Adhoc signature which does not include any identifying information and is not acceptable for submission to the Mac App Store. iOS apps are signed with :doc:`provisioning profiles`. .. _/getting_started/using_the_ide/navigator/go_to_location: Go To Location -------------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/navigator_go_to_location.png If you know its name, you can directly jump to a specific project item using the Go To Location feature. Select Project > Go To Location to display the Go To Location window. Enter the name of the project item you want to jump to and press Return (or click Go). The Navigator will select the item for you. .. _/getting_started/using_the_ide/navigator/using_tabs: Using tabs ---------- A tab is another view into your project. When you create a new tab, you get new Navigator and Editor areas. You can navigate anywhere you want within a tab, just as you can when there are no tabs. Everything works exactly the same; you just now have multiple views into your project, each of which can show different information. Tabs can be a great way to keep frequently used items available, particularly when used in conjunction with the Jump Bar. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/navigator_workspace_with_tabs.png To open a project item in a tab, you can use the contextual menu and select “Open in New Tab”. You can also use :kbd:`Option ⌘` when double-clicking on a project item to open it in a new tab (on Windows and Linux, use :kbd:`Shift Control`). There is also a preference to have project items open in a new tab when double-clicked. The “Double click opens item in new tab” setting in Preferences alters the above behavior. When that setting is checked, double-click opens the item in a new tab. Using :kbd:`Option ⌘` when double-clicking on a project item allows you to drill into it (on Windows and Linux, use :kbd:`Shift Control`). Tabs can be locked or unlocked. A locked tab will not have its contents changed when you click on Filter or Search results, nor will it be changed when you use Go To Location. In those cases, a new tab (or the next available unlocked tab) are used. Click the small “x” in the tab to close it. Hold down :kbd:`Option` (on Mac) or :kbd:`Alt` (on Windows or Linux) when clicking the "x" to close all tabs but the left-most one. You can also use View>Unlock Current Tab or View>Lock Current Tab (along with its keyboard shortcut). If you open more tabs than can be displayed in the tab bar, an “overflow” icon appears for you to click to get a drop-down list of the remain tabs. Select a tab from the list replaces the currently selected tab. You can switch between tabs (wrapping around at the beginning or end) using :kbd:`⌘ Shift }` or :kbd:`⌘ Shift {`. A new tab is added when you run your project. This tab has the App name and displays the Debugger when selected. If you navigate to another section of your project, clicking on this tab while the project is running returns to the debugger. Closing this tab is the same as clicking the "Stop" button in the Debugger. .. _/getting_started/using_the_ide/navigator/adding_project_items: Adding project items -------------------- Use the Insert button on the `toolbar` or the Insert menu to add new project items (which appear in the Contents area). You can also use the contextual menu when clicking on the "Contents" header in the Navigator to add project items. .. youtube:: rBaX20RP3_M You can also add new project items by dragging a control directly from the Library to the Navigator Contents area. This creates a subclass of the control. Learn about subclasses in the Object-Oriented Programming chapter. Project items cannot be named “Xojo”. .. _/getting_started/using_the_ide/navigator/common_project_items: Common project items ******************** .. csv-table:: :header: "Project Item", "Description" :widths: auto "Build Step","Build Steps are used to automate the build process." "Class","A class is a container for code and is the fundamental building block for object-oriented programming." "Class Interface","A class interface is a contract that defines methods that specific classes must implement." "Container Control","Used to combine multiple controls to create custom or reusable controls." "Database Connection","Used to add a database connection object to your project." "Folder","A folder is a collection of project items and is used for organizational purposes only." "Module","A module can contain members (methods, properties, etc.) that are global to your project. It can also contain project items, such as classes and even other modules, allowing you to control access and scope of your project items." .. _/getting_started/using_the_ide/navigator/desktop_project_items: Desktop project items ********************* .. csv-table:: :header: "Project Item", "Description" :widths: auto "Color Group","A defined color or set of colors that can be used anywhere a :doc:`Color` can be used and can provide automatic support for Dark Mode." "File Type Group","Manages the files that your app uses." "Image","Adds an Image Set where you can specify pictures with different sizes (and DPI) for use with HiDPI screens." "Menu Bar","By default, MainMenuBar is included with all new projects. You can add additional menu bars as well, but this is not common." "Report","A report can be used to display information using the built-in Report Designer." "`Toolbar`","A `toolbar` that can be displayed within a Window." "Window","Desktop apps consist of Windows, which contain the controls that define your user interface." "Worker", "A Worker can be used to run code on multiple CPU cores." .. _/getting_started/using_the_ide/navigator/web_project_items: Web project items ***************** .. csv-table:: :header: "Project Item", "Description" :widths: auto "Color Group","A defined color or set of colors that can be used anywhere a :doc:`Color` can be used and can provide automatic support for Dark Mode." "File Type Group","Manages the files that your app uses." "Image","Adds an Image Set where you can specify pictures with different sizes (and DPI) for use with HiDPI screens." "Web Page","Web apps consist of Web Pages, which contain the controls that define your user interface." "Web Dialog","A dialog that can be displayed on a web page." .. _/getting_started/using_the_ide/navigator/ios_project_items: iOS project items ***************** .. csv-table:: :header: "Project Item", "Description" :widths: auto "Color Group","A defined color or set of colors that can be used anywhere a :doc:`Color` can be used and can provide automatic support for Dark Mode." "File Type Group","Manages the files that your app uses." "Icon","A default App Icon is included when creating a new iOS project. Adds an Icon Set where you can specify an icon in various sizes to handle the different iOS screen sizes." "Image","Add an Image Set where you can specify an image in various sizes to handle the different iOS screen sizes." "Launch Image","A container that appears while your app is launching which can contain only a few types of controls." "Launch Screen", "A screen that will be displayed when the app is launched." "Layout","A layout defines the primary set of Screens that are used." "Screen","iOS apps consist of Screens, which contain the controls that define your user interface." "Notifications", "Manages all incoming and outgoing notifications." "Table Custom Cell", "A container to embed a group of controls into a table cell." .. _/getting_started/using_the_ide/navigator/android_project_items: Android project items ********************* .. csv-table:: :header: "Project Item", "Description" :widths: auto "Color Group","A defined color or set of colors that can be used anywhere a :doc:`Color` can be used and can provide automatic support for Dark Mode." "Image","Add an Image Set where you can specify an image in various sizes to handle the different Android screen sizes." "Screen","Android apps consist of Screens, which contain the controls that define your user interface." .. _/getting_started/using_the_ide/navigator/adding_images_and_pictures: Adding images and pictures -------------------------- You can add images and pictures to your projects by dragging the files onto the Navigator. This creates an Image Set and puts the picture at the 1x size, displaying the :doc:`Image Set Editor`. You can drag different pictures (with the appropriate DPI to the 2x and 3x sizes). All the pictures in an Image Set must have the same aspect ratio. If multiple files of the same image at different resolutions are dragged into the Navigator together, they will all be placed into a single Image Set. You can manually add an Image Set (Insert > Image) and then drag an image or picture to each size. When your app is run, the appropriate image for the current screen resolution will be used. These contextual menu items are available when an image in an Image Set is selected: * Open in External Editor: Opens the picture in the default picture viewer for the OS. * Show on Disk: Displays the picture file using the OS file system viewer (Finder or Windows Explorer). On Desktop and Web projects, you can also drag a picture directly into the project (without creating an Image Set) by holding down :kbd:`Option` on MacOS (or :kbd:`Ctrl Alt` on Windows and Linux). Clicking on the Picture displays a preview of it in the center area of the Workspace. ` These contextual menu items are available when a Picture is selected: * Open File: Opens the picture in the default picture viewer for the OS. * Show on Disk: Displays the picture file using the OS file system viewer (Finder or Windows Explorer). * Convert To Image: Converts the Picture to an Image Set with the picture placed in the 1x slot. You can multi-select pictures to convert more than one at a time. Note: The Supports HiDPI property (in Shared Build Settings) must be set to ON in order for this option to appear. .. _/getting_started/using_the_ide/navigator/working_with_project_items: Working with project items -------------------------- You can right-click (:kbd:`Control`-Click on Mac) on any project item in the Navigator to display the contextual menu. From the contextual menu you have these options (not all options appear for every project item): .. csv-table:: :header: "Menu Item", "Description" :widths: auto "Add to","Use this command to add code items such as event handlers, methods, properties, etc. to the project item." "Inspect","Displays the Inspector for the project item." "Cut/Copy","Use to cut or copy the project item to the clipboard." "Paste","Pastes a project item in the clipboard into the Navigator, adding it to your project." "Delete","Deletes the project item. To put it in the clipboard, use Cut." "Duplicate","Creates a copy of the project item in the Navigator, adding a number suffix." "Make External/Internal","External items can be used to share project items between your projects. Learn more in the :doc:`Sharing Code` topic." "Encrypt/Decrypt","Refer to Encrypting Project Items for information on encrypting and decrypting project items." "Export","Use to export a project item to a file. This is a useful way to share a project item with someone else." "Print","Prints all the source code for the project item (see Printing)." "New Subclass","Creates a new class in the Navigator that uses the project item as its superclass. Learn about classes in Object-Oriented Programming." "Extract Interface","Displays a dialog that allows you to create a new interface using the specified name and selected methods from the class. The class has the newly created interface added to it." "Extract Superclass","Displays a dialog that allows you to create a new super class using the specified name and selected members from the class. The class has the newly created class set as its Super." "Implement Interface","Lets you select an interface that contains methods to implement for the class. When you select the interface (or interfaces) to add, the methods from the interfaces are added to the project item. Learn about Interfaces in Object-Oriented Programming." "Inspector Behavior","Displays the :doc:`Inspector Behavior` window where you can customize the properties that display in the Inspector for your own classes and controls that you add to layouts. You can choose to display properties for your custom classes and subclasses and you can choose to hide properties that would normally be displayed. Add new properties using the “+” button on the left below the list of properties. Check or uncheck the check box next to the property name to determine if it is visible in the Inspector. You can also specify default values for any properties that are displayed. Drag properties in the list to reorder them or change their grouping in the Inspector. Use the Enumerations section to add a list of values that the user can select with a drop-down menu." "Edit Super Class","Displays the parent (super) class for the currently selected class. This item only appears for subclasses. Learn about classes in Object-Oriented Programming." .. _/getting_started/using_the_ide/navigator/printing: Printing -------- You can print your source code using File > Print from the menu: .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/navigator_project_printing_output.png * When you have project items selected, only the selected items print. * If you do not have a project item selected, the entire project prints. The easiest way to print the entire project is to select something in the Build Settings. .. youtube:: LP9mhXbG_VA Refer to the :ref:`Printing settings` for other settings. .. _/getting_started/using_the_ide/navigator/keyboard_shortcuts: Keyboard shortcuts ------------------ In order for these to work, you must have focus in the Navigator, which means that the row selection is blue. * :kbd:`Home`: Move selection to first row, scrolling if necessary. * :kbd:`End`: Move selection to last row, scrolling if necessary. * :kbd:`Right` Arrow: Expand currently selected row. * :kbd:`Left` Arrow: Collapse currently selected row. * :kbd:`Up` Arrow: Move selection to previous row. If you are now selecting a code item, then focus moves to the Code Editor. * :kbd:`Down` Arrow: Move selection to next row. If you are now selecting a code item, then focus moves to the Code Editor. * :kbd:`Page Up`: Move selection up by one page, scrolling if necessary. * :kbd:`Page Down`: Move selection down by one page, scrolling if necessary. * :kbd:`Shift Up`/:kbd:`Shift Down` Arrows: Select multiple rows. ============== Picture Editor ============== If the Supports HiDPI setting in Shared Build Settings is set to OFF then pictures dragged into the project do not use the :doc:`Image Set Editor` and instead display as a single picture in the Picture Editor. Pictures added in this manner are not images and do not work well with HiDPI displays. The Picture Editor can only be used with desktop and web projects. The Picture Editor has zoom controls on the Command Bar and Transparent and Mask properties you can edit in the Inspector. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/picture_editor_picture_editor.png :scale: 50 % .. _/getting_started/using_the_ide/picture_editor/see_also: .. seealso:: :doc:`Image Set Editor` ============================= Project file information ============================= A Xojo project is a document that contain all the items that make up your app. You can have several projects open at once and you can have several windows open per project. When you choose to create a new project, you have these types from which to choose: Desktop, Web, Console, iOS or Android. .. csv-table:: :header: "Type", "Description" :widths: auto "Desktop","Desktop projects allow you to create apps with a graphical user interface that run on Windows, Mac and Linux desktop operating systems." "Web","Web projects allow you to create apps that run on the web. Users interact with these app using a web browser." "Console","Console projects are used to create text-based apps that run from the command line, terminal or as a background app (service or daemon). Windows, Mac and Linux are supported." "iOS","iOS projects are used to create apps that run on iOS devices such as iPhones and iPads." "Android","Android projects are used to create apps that run on Android devices." If you load a project into a version of Xojo that is different that the version that created it, you will get a dialog. When projects that were last edited in an older version of a Xojo are opened, a warning is displayed. If the project was last edited in a version of Xojo newer than the version of Xojo being used now, then a critical dialog is displayed because you may use properties and values that are specific to the newer version of Xojo. .. _/getting_started/using_the_ide/project_types/project_formats: Project formats --------------- You can save your projects in a several formats, including the binary single-file format (xojo_binary_project), an XML format (xojo_xml_project) and a text format (xojo_project) which uses separate files for project items. A Xojo license is required to use the XML or Text project formats. In the Settings window you can change the format that is used by default. .. youtube:: zv36LgjuTYo .. note:: Xojo can also open and save Real Studio and Realbasic project files. .. _/getting_started/using_the_ide/project_types/binary_(xojo_binary_project): Binary (xojo_binary_project) **************************** This is a binary file format. Your project is stored in a single file (except for external items such as pictures) that is easy to distribute. This is the only file format that can be used when you do not have a license. .. _/getting_started/using_the_ide/project_types/xml_(xojo_xml_project): XML (xojo_xml_project) ********************** The XML file format is an XML representation of the binary file format. It is also a single file, but it is entirely XML. Being XML, this file format can be larger than the same file saved as Binary, but it is text so you can view it in an external text editor. A Xojo license is required to use the XML project format. .. _/getting_started/using_the_ide/project_types/text_(xojo_project): Text (xojo_project) ******************* The Text file format saves your project as separate text files, one for each project item. Because of this, make sure that your save each Text project in its own folder and do not try to save multiple text projects into the same folder. The Text file format is version control ready and is ideal for use with Git, Subversion or other version control systems because you can see exactly what files have changed and track the history of changes to files. .. note:: Previously, the Text project format was also referred to as the Version Control Project (or VCP) format. A Xojo license is required to use the Text project format. This format uses the following extensions for your project items: .. csv-table:: :header: "Extension", "Description" :widths: auto "xojo_code","Contains source code project items such as modules, classes, web pages, iOS views and anything else not listed below." "xojo_window","Contains windows and container controls from desktop projects." "xojo_menu","Contains menus from desktop projects." "xojo_menu","Contains toolbars from desktop projects." "xojo_toolbar","Contains toolbars from desktop projects." "xojo_report","Contains reports from desktop projects." "xojo_resources","Contains binary information such as icons, encrypted items and other binary data." "xojo_uistate","Contains the IDE and UI layout settings such as: window positions and sizes, warning preferences, iOS debug device, bookmarks and breakpoints." When used with a version control system, you should add all the files to your repository except for the xojo_uistate file (which you'll typically want to mark as ignored). When you delete (or rename) files or folders from a text project format, they are left on disk so that you can manually handle the change using your version control software. .. _/getting_started/using_the_ide/project_types/external_items: External items -------------- Certain project items are always stored as external items and are not included in the project file. These are: pictures, movies, sounds, text files and other files added to the project. It is also possible to include windows, classes, or modules in a project that are actually stored as external files. This feature allows more than one project to share the project item. When you modify an external project item and then save your project, your changes are written out to the external file on disk. When you open any other project that refers to the same shared file, that project will reflect your changes. To convert a project item to an external item, select it in the Navigator and right-click (:kbd:`Control`-click on Mac) to display the contextual menu for the item and choose Make External. This displays a Save File dialog box. Navigate to the directory in which you want to save the item, name it, and click Save. When you have successfully saved the item, it is shown in the Navigator with a shortcut badge and its name is in italics. To add an item to a project as an external item, press and release the Alt key (hold down the :kbd:`Option Command` key on Mac) before clicking in the menu, then select the File menu and you'll see the Import menu now says "Import External". Choose that command and select the item to be imported. You can also add an external item by dragging the file from the disk to the Navigator while holding down :kbd:`⌘ Option` (on Mac) or :kbd:`Shift Ctrl` (on Windows and Linux). When an item is imported as external the icon in the Navigator appears in italics with a shortcut badge. If an external project item is set to read only (Windows or Linux) or locked (Mac), you can't modify that item within the project, though you can still view it. This provides a convenient way to protect external items (which may be shared by many projects) from accidental modification. Note that if an external file is changed on disk by something other than Xojo — such as a version control system — you 'll need to close and re-open the project to reload that item. Refer to :doc:`Sharing Code among multiple projects` for more information. .. _/getting_started/using_the_ide/project_types/encrypting_project_items: Encrypting project items ------------------------ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/project_types_encrypt_window.png If you want to distribute a copy of project items for others to use but you do not want them to be able to view or edit your code, use the Encrypt command to protect the project item prior to exporting it. An encrypted item displays in the Navigator with an icon containing a small key in its lower-right corner. An encrypted project item cannot be viewed when it is selected in the Navigator. You can encrypt (protect) or decrypt (unprotect) a project item while it is in your project using the contextual menu “Encrypt ProjectItemName...”. An encrypted item cannot be opened and no one can access any code (or layout) associated with it unless they know the password to decrypt it. When encrypting an item, you supply a password that can be used to decrypt it later. .. warning:: Do not forget the password as there is no other way to decrypt the item. .. _/getting_started/using_the_ide/project_types/import_project_items: Import project items -------------------- To import a file you wish to use in your project, simply drag it from the desktop and drop it in the Navigator. Or, if the file is not conveniently located on the desktop, choose File > Import. An Open File dialog box appears, allowing you to navigate to and import the file. For code, open the method that you wish to import code into and drag the text clipping into the body of the method. You cannot use the File > Import command to import text files into the body of a method. To delete a file that has been imported to the project, highlight it in the Navigator and press the Delete key on the keyboard or choose Edit > Delete. You can also delete a file in the Navigator by Control-clicking on the item and choosing Delete from the contextual menu. .. _/getting_started/using_the_ide/project_types/export_project_items: Export project items -------------------- You can export project items to a file in the XML, binary or text format. Select the project item item and then choose File > Export… from the menu. Exporting project items can be useful for sharing code with others. You can always copy code to the Clipboard and paste it into a text editor or word processor. On Mac, the code for methods, events, constants, properties, and so forth can be dragged to the desktop as text clippings or into a text editor. .. _/getting_started/using_the_ide/project_types/collect_project_items: Collect project items --------------------- Choosing File > Collect Project Items will make a copy of the project and the plugins folder. Some of your project items may be external to the actual project files. These can be things such as graphics or even project items that are shared across multiple projects. For distribution, it can be helpful to collect all project items from their various locations next to a saved project. External items remain as external items, but they are now grouped (pictures, data, scripts, etc.). This process does not rename items on disk, so duplicate filenames will be reported as errors. Collecting a project does not save the project. Typically you will want to do a “Save As” after collecting the project so that everything gets saved into a new location. External items will then be saved into this new location. The original project and its original external items will be unchanged. Choose File > Collect Project Items to collect the current project. .. _/getting_started/using_the_ide/project_types/templates: Templates --------- If you have several items you commonly use in every project, you can save them in a project file and make the project file a template for new projects. The template can include custom windows, classes, modules and other project items. When you create a new project based on the template, Xojo creates a new untitled project that is an exact copy of the template. The template project itself remains unchanged. This lets you create a new project using existing project items without worrying about modifying the original items. The most efficient way to use a template is to place it in a special directory in the same directory as Xojo called “Project Templates”. If you do so, your list of templates will be listed in the Project Chooser whenever you choose File > New Project. Here are descriptions of the project templates that are included by default. Although these templates are built-in, but they can be overridden by providing templates that use these names and placing them in the Project Templates folder. * **EmptyService**: This is a service application template. Its App class is based on the ServiceApplication class. It also runs in the background with no user interface. Please see the Notes section for the :doc:`ServiceApplication ` class for more information. * **Event Driven Console**: This is also a template for a Console Application. Its App class is also based on the ConsoleApplication class and it includes a custom class, MyApplication, that contains shell methods and properties for a Console Application. See the Notes section for the :doc:`Console Application ` class for information on how a console application works. .. _/getting_started/using_the_ide/project_types/changing_default_projects: Changing default projects ************************* When you create a new desktop, web or console project, a simple project is opened with just the basics to get you started. You can use your own project files as a substitute for the simple projects used for desktop, web and console projects. To do so, create projects (in Binary format) and add the project items you want to include by default. Save them with the following names in the Project Templates folder: * Default Desktop Project * Default Web Project * Default Console Project * Default iOS Project * Default Android Project For example, if you create Default Desktop Project that has two windows and some standard modules and classes that you typically use in all your projects, they will be immediately available when you select Desktop from the Project Chooser. .. _/getting_started/using_the_ide/project_types/online_and_network_file_systems: Online and network file systems ------------------------------- Do not edit your Xojo project files directly from networked file systems (SMB, etc.) or online file systems (DropBox, Google Files, Box, etc.). The multi-user access and automated syncing can cause project corruption if things happen at inappropriate times. For sharing projects between computers, Xojo recommends using a :doc:`version control system`. .. _/getting_started/using_the_ide/project_types/see_also: .. seealso:: :doc:`Sharing Code` topic ==================== Report Layout Editor ==================== .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/report_layout_editor_report_layout_editor.png The Report Layout Editor is used to design reports for desktop apps. The Report Layout Editor can only be used with Desktop projects. To create a report, use the Insert button or menu and select Report. This adds a report to your project and displays the Report Layout Editor. You can specify the units for the ruler in Inches, Millimeters or Pixels and specify the width of the report page using the Inspector. Much like the Window Layout Editor, you drag controls onto the Report Layout Editor to design you reports. The Reports uses a “banded” report design containing multiple bands where information can appear. By default, you see three bands: PageHeader, Body and PageFooter. Whatever is in the PageHeader band appears at the top of every page, including the first page. Similarly, what is in the PageFooter band appears at the bottom of every page, including the first page. The Body band is repeated for each line of data that is in the report. For example, if you have a report that is displaying a list of Teams, then you will get a separate Body band for each team. In order to display data, a report has to have a data set, which is discussed in the :doc:`Displaying Desktop Reports` topic. .. _/getting_started/using_the_ide/report_layout_editor/command_bar: Command bar ----------- The command bar has these commands: .. csv-table:: :header: "Item", "Description" :widths: auto "Add Group","Adds a new Group to the report layout. This adds both a Group Header and a Group Footer to the layout." "Add Page Header/Footer","Used to add a Page Header/Footer to a report. A report can only have a single set of Page Header/Footer bands. Since it is possible to delete these bands, use this item to add them back." "Ordering","Changes the layering of the controls on the report layout." "Fill","Fills the size of the control to match its parent container." "Alignment","Aligns selected report controls with each other." "Spacing","Spaces the selected report controls equally apart from each other." .. _/getting_started/using_the_ide/report_layout_editor/library_and_inspector: Library and Inspector --------------------- The Library displays the report controls that can be added to the report layout. The Inspector displays the properties for the selected report control. .. _/getting_started/using_the_ide/report_layout_editor/report_layout_editor_controls: Report Layout Editor controls ----------------------------- There are a variety of controls that you can use in your reports, including: Field, Label, Picture, Line, Oval, Rectangle, RoundRectangle, Date and Page Number. Each control has two events that can be used for any processing: AfterPrinting and BeforePrinting. * **Field** - A Field on a report is used to display report data. The fields map to the data source you use with the report, by specifying a value in the DataField property. Generally, you use Fields within the Body band, but they work in any band. * **Label** - Like a Label on a Window or Web Page, a Label on a report displays text. Labels are used for things like report titles and column headings. * **Picture** - A Picture is used to display a picture on the report. The picture can be specified when you are designing the report or it can be specified at runtime using information in your data source. * **Line, Oval, Rectangle, RoundRectangle** - Used to draw shapes on the report. * **Date** - Used to display the current date when the report is generated. * **Page Number** - Displays the page number on each page of the report. .. _/getting_started/using_the_ide/report_layout_editor/grouping: Grouping -------- Groups are used to display related data together. For example, in the Team and Player examples, there are two sources of data: teams and players. You could use Grouping on a report to display the teams with each player on the team listed below the team name and perhaps indented slightly. To add a group to the report, you click the “Add New Group” button on the toolbar. This creates two new bands, called GroupHeader1 and GroupFooter1, where you can place additional information. The information in the Group Header appears once for each group. The information in the Group Footer also appears once for each group, but is displayed after the information in the Detail band. .. _/getting_started/using_the_ide/report_layout_editor/see_also: .. seealso:: :doc:`Desktop Reports`, :doc:`Displaying Desktop Reports` topics =================== Running the project =================== .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/running_the_project_debugger.png :alt: Debugger While working on your project, you are going to want to test it to make sure it works like you expect. You can do this by clicking the Run button on the toolbar, selecting Project > Run from the menu or pressing :kbd:`⌘ R` (:kbd:`Ctrl R` on Windows and Linux). When you run your project like this, you are running it in debug mode. In this mode, Xojo is communicating in the background with your app. In the Workspace, a separate tab (containing the debugger) appears with the name of your app. .. tip:: If you are developing a iOS application and want to test it in the iOS Simulator on a specific device, choose Project > Run On, then select the device you wish to use for testing. When this tab is selected, the Debugger displays. In this tab, the Navigator displays the Run section and the name of the app being debugged. When this tab is selected, the Debugger displays. In this tab, the Navigator displays the Run section and the name of the app being debugged. If you are new to debugging, read the :doc:`Introduction to Debugging` and :doc:`Debugger Usage` pages. .. _/getting_started/using_the_ide/running_the_project/debugger: Debugger -------- The debugger can be used to watch how your app runs. It is divided into three main areas: * **Stack** - Displays the order that the code in your application was called. * **Variables** - Displays variables and their current values. * **Source Code** - Displays the source code that is currently running with the current line highlighted when the debugger is paused. To see the Stack, Variables and currently running source code, you can set a breakpoint in your source code or you can click the pause button on the toolbar. .. _/getting_started/using_the_ide/running_the_project/command_bar: Command bar *********** The debugger Command Bar has these commands: * **Pause** - Pauses the app at the currently running line of code. The debugger will display the line of code highlighted in gray. * **Stop** - Stops the running app, closing the debugger. * **Step** - When Paused, steps through the code one line at a time. * **Step In** - When Paused, if the current line of code is a method then this action steps you into the first line of code in the method. * **Step Out** - When Paused, if you are within a method, this action runs the rest of the code in the method and pauses debugging at the line of code after the method call. * **View Source** - Takes you to the project item and displays the source code so that you can edit it. Changes you make to the code do not take affect until you run the project again. .. _/getting_started/using_the_ide/running_the_project/using_the_debugger: Using the Debugger ------------------ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/running_the_project_debugger_with_variables_pane.png :alt: Debugger with Variables Pane You have three ways to Pause the debugger so you can review running code: * Use the Pause button, which does not allow you to control where the code will pause. * Set a breakpoint in your code. (see below) * Use the :doc:`Break` command in your code. Once you are in the debugger, you can view the source code but not make any changes to it. To edit code, use the View Source button to find the code in the project to edit. Any changes you make do not take affect until you run the project again. To resume running your app after it has paused, click the Resume button on the toolbar, which appears in place of the Run button. You can also completely stop the running app by clicking the Stop button on the toolbar. This does not take you to the debugger since the app has been terminated, but it can be useful if you need to quickly stop the app because of an infinite loop or some other programming error. If you are new to Xojo, check out the :doc:`Introduction to Debugging` and Using the Debugger pages. .. _/getting_started/using_the_ide/running_the_project/stack: Stack ***** The Stack area (lower left corner) displays the name of the objects in the order they were called with the current object at the top of the list. You can click on other items in the list to view the code that previously ran. The PopupMenu in the Stack area usually just displays “Main Thread”. If you are specifically using Threading, you can use this popup to look at code that is running in other threads. .. _/getting_started/using_the_ide/running_the_project/variables: Variables ********* The Variables area (lower right corner) displays the variables that are local to the currently running method. The left column displays variable names and the right displays their values. You can click on values on the right side to display them in more detail. Some values (such as numbers and strings) can have their values edited so that you can affect the outcome of subsequent code. As you click to view details of variables, the PopupMenu in the Variables section changes to display what is being viewed. You can use this popup to go back to the prior variables. In addition, the “Globals” item in the Variables section will let you review global values in the app, such as modules and the contents of the App object. To see just the variables/properties whose name matches a set of characters, use the :ref:`Variables Filter`. .. _/getting_started/using_the_ide/running_the_project/setting_breakpoints: Setting breakpoints ------------------- In the :doc:`Code Editor`, the gutter in the left margin shows horizontal dashes for lines of code that can have their breakpoint set. To set the breakpoint, just click on the dash. This causes a red dot to appear indicating that a breakpoint is set. To turn off the breakpoint, click on the red circle. You can also set breakpoints using Project > Breakpoint > Turn on (:kbd:`⌘ \\` on macOS, :kbd:`Ctrl \\` on Windows and Linux). To disable all the breakpoints in your app, use Project > Breakpoint > Clear All. To view all the breakpoints in your project, choose Project > Breakpoint > Show All, which displays the breakpoints in the Find panel. You can also use the :doc:`Break` command to cause your application to pause. When your code reaches a Break command, the debugger pauses at that line. In a standalone app, the Break command is ignored. You can use the Break command to conditionally pause your app and activate the debugger based on specific situations. For example, if your app is in a loop, you may only want to break into the debugger when a certain value is reached: .. code:: xojo For i As Integer = 1 To 100 If i = 50 Then Break Next .. _/getting_started/using_the_ide/running_the_project/see_also: .. seealso:: :doc:`Break` command; :doc:`DebugBuild` constant, :doc:`Code Editor`, :doc:`Debugging Overview`, :doc:`Debugger Usage` topics ======== Settings ======== There are several settings that you can use to alter default behavior. The settings are grouped into these areas: General, Navigator, Printing, Coding, Layout, Localization, Building, Debugging, Proxy and Updates. On Windows and Linux, the menu item that opens the Settings window is called Options and is in the Edit menu. On macOS, the menu item that opens the Settings window is located in the App menu. .. _/getting_started/using_the_ide/settings/general: General ------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_general.png The General area contains the most common settings. .. _/getting_started/using_the_ide/settings/show_library_and_inspector: Show Library and Inspector ************************** Specifies how you want to see the :doc:`Library` and :doc:`Inspector`. By default they appear on the right side of the Workspace, both sharing the same area. You can instead choose to display them as floating palettes so that they are both visible on the screen at the same time and do not take up space in the Workspace. .. _/getting_started/using_the_ide/settings/default_project_format: Default project format ********************** When you save your projects, Xojo uses the standard single-file binary format by default (xojo_binary_project). Use this setting to change the default project format. Refer to the :doc:`Project Types topic` to learn more about the project formats. .. _/getting_started/using_the_ide/settings/when_seeking_help: When seeking help ***************** Xojo looks up commands using the Documentation Window. By default, it displays the documentation from the xojo website so that it's always up to date. However, there is a complete set of documentation built-in to Xojo that it will use if you have no Internet connection. You can also browse the documentation `online `_. .. _/getting_started/using_the_ide/settings/when_closing_last_window: When closing last window ************************ This setting is only available on Windows. When “Keep Xojo running” is selected, a new icon appears in the Taskbar Notifications area (also known as the System Tray) and Xojo remains running when you close the last Workspace window (instead of quitting). You can right-click on the Notification icon to display a menu with “New Project”, “About Xojo” and “Exit”. “New Project” opens the Project Chooser window, “About Xojo” displays the About window and “Exit” quits Xojo completely. .. _/getting_started/using_the_ide/settings/number_of_recent_items: Number of recent items ********************** This is the number of recent items that are remembered in the File > Open Recent menu and the Project Chooser. .. _/getting_started/using_the_ide/settings/find: Find **** Here you can choose to have find results appear immediately when you pause typing or only when you press return in the find field of the Find panel or the Filter field. .. _/getting_started/using_the_ide/settings/menu_short_cuts: Menu short cuts *************** You can change and assign short cuts to any Xojo menu item. Click the *Edit Menu Shortcuts...* button to display the menu shortcut editor. The editor displays the Xojo menu structure in a hierarchical list. Click on a row to change the shortcut key and its related modifier keys. To clear a menu keyboard shortcut, click on that row and then choose Edit > Cut. Click the *Restore Defaults* button to restore the original set of short cuts. Click the *Save* button to save your changes, which take effect immediately. .. _/getting_started/using_the_ide/settings/use_large_font_in_navigator,_library_&_inspector: Use large font in Navigator, Library & Inspector ************************************************* The "Use large font" setting uses a larger font size to display the text in in the Navigator, Library, and Inspector. This setting is also used by the Constant, Enumeration, and Structure editors. Install Local Documentation *************************** By default, all Xojo documentation is online. Clicking this button will install a local version for offline use. You can click this button again to update your local documentation. .. _/getting_started/using_the_ide/settings/navigator: Navigator --------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_navigator.png The Navigator settings control how the :doc:`Navigator` works. .. _/getting_started/using_the_ide/settings/show_item_scope: Show item scope *************** This setting determines how the scope for properties and methods is displayed in the Navigator. Choosing “Behind item text” has the scope color displayed as the background color behind the item in the Navigator. Choosing “Over item icon” displays an overlay on the item in the Navigator indicating its scope. .. _/getting_started/using_the_ide/settings/navigator/double-click_opens_item_in_new_tab: Double-click opens item in new tab ********************************** The *Double-click opens item in a new tab* setting controls the behavior of double-clicking project items in the Navigator. The default behavior is to “drill into” the project item using the Jump Bar. Check the box to instead open the project item in its own tab when it is double-clicked. .. _/getting_started/using_the_ide/settings/show_default_values_in_navigator: Show default values in Navigator ******************************** The *Show default values in navigator* setting controls whether defaults values assigned to properties appear next to the property name in the Navigator. .. _/getting_started/using_the_ide/settings/show_types_in_navigator: Show types in Navigator *********************** The *Show types in navigator* setting controls whether the types of properties appear next to the property name in the Navigator. .. _/getting_started/using_the_ide/settings/printing: Printing -------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_printing.png The Printing settings control how your code looks when it is :ref:`printed`. .. _/getting_started/using_the_ide/settings/printing/font: Font **** Choose the font to use for the printed text. .. _/getting_started/using_the_ide/settings/printing/size: Size **** Choose the font size to use for the printed text. .. _/getting_started/using_the_ide/settings/features: Features ******** Check "Print in color" if you want the source code printed using the syntax colors specified in the Coding settings. Check "Include images of layouts" if you want printouts to include images of your user interface layouts (windows and web pages). .. _/getting_started/using_the_ide/settings/coding: Coding ------ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_coding.png The Coding settings control how the code is displayed in the :doc:`Code Editor`. .. _/getting_started/using_the_ide/settings/coding/font: Font **** Select the font to use in the Code Editor from the list of available fonts installed on your system. For best results, use a monospaced font. .. _/getting_started/using_the_ide/settings/coding/size: Size **** Specifies the font size to use in the Code Editor. .. _/getting_started/using_the_ide/settings/syntax_colors: Syntax colors ************* You can customize the colors of various parts of your source code. Select a particular syntax and then click the color box to choose a color for it. You can save these changes as an XML "Theme" to share with others using the Import Theme and Export Theme buttons. Use the Reset button to return the default syntax colors. .. _/getting_started/using_the_ide/settings/default_comment_style: Default comment style ********************* This setting determines the comment style that is used by the Comment button and function on the contextual menu. .. _/getting_started/using_the_ide/settings/autocomplete: Autocomplete ************ These two options allow you to control how Autocomplete works. Apply standard case will use the correct case when autocompleting (MessageBox instead of messagebox, for example). Show icons will show icons next to the values in the autocomplete list. .. _/getting_started/using_the_ide/settings/behavior: Behavior ******** The four options are: * Standardize Format: Applies *standardized format* (which automatically case-corrects keywords for you) when you press return on the line or move to a new line. * Show line numbers in gutter: Displays numbers for each line starting with 1. These numbers correspond to error messages that indicate a line number. * Hightlight current row: When the cursor is clicked on a particular line of code, the background of the line is highlighted to make it easier to see which line is being edited. * Display selection matches: When a variable is selected, all instances of that variable in the Code Editor will also be selected making it easy to find them. .. _/getting_started/using_the_ide/settings/syntax_help_area: Syntax help area **************** Allows you to decide how many lines will be displayed in the Code Editor's Syntax Help Area. You can choose from 2 to 6 lines or Resizeable which will auto-resize as necessary. .. _/getting_started/using_the_ide/settings/“home”_and_“end”_keys: Home and End keys ***************** Determine how you want the Home and End keys to work in the Code Editor. .. _/getting_started/using_the_ide/settings/layout: Layout ------ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_layout.png The Layout settings adjust how the :doc:`Layout Editors` work. .. _/getting_started/using_the_ide/settings/default_control_font: Default control font ******************** By default, System is used as the Font for all controls. This allows the platform to substitute its own default font at runtime. If you would rather use a specific font as the default, you can choose it here. .. _/getting_started/using_the_ide/settings/size: Size **** By default, 0 is used as the Font Size for all controls. This allows the platform to substitute is own default font size at runtime. If you would rather use a specific font size as the default, you can set it here. .. _/getting_started/using_the_ide/settings/when_selecting_a_control: When selecting a control ************************ When you have nested controls (one control on top of another control or control parenting), the Layout Editor highlights the parent control with a red outline. If you would rather not see this, you can turn it off here. .. _/getting_started/using_the_ide/settings/layout_editor_background: Layout editor background ************************ Used to change the background color of the layout editor. Click the Reset button to restore the default layout background. .. _/getting_started/using_the_ide/settings/localization: Localization ------------ .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_localization.png The Localization settings controls the order of languages in localization menus (such as Shared Build Settings and Constant Editors). In the list of languages, you can drag to re-order the languages or use the Move Up/Move Down buttons. By default, the more common languages are listed at the top, followed by less common languages at the bottom. .. _/getting_started/using_the_ide/settings/building: Building -------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_building.png The Building settings control what Xojo does when building or :doc:`running projects`. .. _/getting_started/using_the_ide/settings/reveal_apps: Show built apps *************** After building your app, the built app can be displayed using the built-in OS file viewer (the Finder on macOS, Explorer on Windows and File Manager on Linux). .. _/getting_started/using_the_ide/settings/when_not_using_build_folders_and_a_file_already_exists: When not using build folders and a file already exists ****************************************************** When Building your app (with Use Build Folders set to OFF in Shared Build Settings), existing built apps (and related files) are normally replaced automatically. If you would rather be prompted when a file will be overwritten, you can change this here. .. _/getting_started/using_the_ide/settings/build_unsaved_apps_in: Build unsaved apps in ********************* If you try to build an unsaved project, this location is used to store the built app. .. _/getting_started/using_the_ide/settings/clear_caches: Clear caches ************ Click the Clear button to clear Xojo build caches for plugin precompliation and conditional compilation. .. _/getting_started/using_the_ide/settings/debugging: Debugging --------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_debugging.png The debugging settings are used to control debugging and remote debugging. .. _/getting_started/using_the_ide/settings/network_interface: Network interface ***************** Specify the networking interface to use to connect to the Remote Debugger. .. _/getting_started/using_the_ide/settings/remote_debug_hosts: Remote debug hosts ****************** Here you can add, edit or remove remote debugging locations. .. _/getting_started/using_the_ide/settings/show_object_ids_in_variable_lists: Show Object IDs in variable lists ********************************* Displays object IDs in the debugger Variable area. .. _/getting_started/using_the_ide/settings/hide_bottom_pane_on_run: Hide bottom pane on run *********************** This setting controls whether the bottom panel (:doc:`Find/Errors/Messages`) is automatically hidden when you :doc:`run the project`. .. _/getting_started/using_the_ide/settings/proxy: Proxy ----- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_proxy.png The Proxy settings are used to specify proxy information so that Xojo can connect to the server to validate your licenses and check for new versions. These settings are also used when you “Sign In” to Xojo. Select the “Connect using a proxy server” checkbox to enable use of the specified proxy server. Fill in the appropriate fields to specify the location of the proxy server and any information needed for authentication. .. _/getting_started/using_the_ide/settings/updates: Updates ------- .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/settings_updates.png The Updates settings allow you to choose how often Xojo checks for new versions. You can also specify whether you will allow your system information to be sent to Xojo servers to improve system support. ================ Structure Editor ================ The Structure Editor is used to create Structure data types. A structure provides a convenient alternative to a MemoryBlock as they are also often used to group together information for external function calls. .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/structure_editor_structure_editor.png The values of structures have specific sizes giving the structure its own specific size. You typically only use structures when you have very specific memory or performance requirements or when you need to interface with an outside API that requires a structure. In most cases, you will want to use Classes with appropriate properties for your data management. .. _/getting_started/using_the_ide/structure_editor/see_also: .. seealso:: :doc:`Structure` command; :ref:`Advanced Language Features` topic ============== Toolbar Editor ============== .. image:: https://documentation.xojo.com/getting_started/using_the_ide/images/toolbar_editor_toolbar_editor_2019r2.png :alt: Toolbar Editor The Toolbar Editor is used to design toolbars for your desktop apps. In this editor, you can add buttons, remove buttons and drag the buttons around to reorder them. To remove a button, click on it and press :kbd:`Delete` (on macOS and Linux) or :kbd:`Ctrl Backspace` on Windows. The Toolbar Editor can only be used with Desktop projects. Once you have created a toolbar, you can add it to a Window by dragging it from the :doc:`Navigator` onto the Window Layout where it will appear in the :ref:`Shelf`. Refer to Desktop Toolbars for more information on how to add and use toolbars in your apps. .. _/getting_started/using_the_ide/toolbar_editor/command_bar: Command bar ----------- The command bar has these commands: .. csv-table:: :header: "Command", "Description" :widths: auto "Platform Display","The platform icons on the toolbar allow you to see how the toolbar looks on Windows, macOS and Linux." "Add","Adds a new item to the toolbar. Once added, you can drag it to another position on the toolbar." "Inspector","The Inspector displays the properties for the selected toolbar item." .. _/getting_started/using_the_ide/toolbar_editor/see_also: .. seealso:: :doc:`Desktop Toolbars` Using the Xojo Language ======================= .. toctree:: :maxdepth: 1 :name: sec-using_the_xojo_language Variables and Constants Data Types Comparing Values Event Driven Programming Controlling Code Flow Repeating Code Properties Methods Collections of Data Enumerations Modules Advanced Language Features Reserved Words ======================= Variables and constants ======================= A computer has two types of memory: temporary and permanent. Temporary memory is used to remember things for as long as the computer is on. As soon as the computer is turned off (or restarted), anything in temporary memory is lost. This is often referred to as RAM (Random Access Memory). Permanent memory is remembered even after a computer is turned off. This type of memory is also called the storage and usually a hard disk, solid-state disk (SSD) or flash memory. You save things to permanent memory using files, which are discussed in the File Management section. When you are writing code, you often need to remember things. You use a concept called *Variables* to store information in the program's temporary memory. Variables are great for holding information such as counter values, field values and anything else you need while your program is running. The term “variable” is used because the value that it contains can be changed by your program, so its value can vary. A Constant is similar to a variable, except that once its value is set it cannot be changed by your program (so it is constantly the same). .. _/getting_started/using_the_xojo_language/variables_and_constants/variables: Variables --------- A variable is the simplest way to store values. All variables are defined in your code using the Var statement and each variable must be defined before it is used. Xojo is strongly typed: when you define a variable you tell it the type of data that it can contain. A variable definition consists of four parts: the Var keyword, the variable name, the As keyword and the data type. This is also often referred to as a "variable declaration". Here is an example variable declaration: .. code:: xojo Var age As Integer .. note:: For Visual Basic compatibility, the Dim keyword is a synonym for Var. There are several simple, built-in data types: :doc:`String`, :doc:`Integer`, :doc:`Double`, :doc:`Currency`, :doc:`Boolean`, and :doc:`Color`. Data Types are covered in the :doc:`Data Types` topic. Once you have declared a variable, you can assign it a value: .. code:: xojo Var age As Integer age = 42 Variables can be declared anywhere you can write code (such as a methods or events), but the declaration must precede its first usage. If you have several variables of the same type, you can declare them all with one Var statement: .. code:: xojo Var i, j, k As Integer If you want to declare variables of different types, you can also declare them in one Var statement. For example, the following Var statement is valid: .. code:: xojo Var name, address As String, shoeSize As Integer But in general it is considered better form to declare variables of different types on separate lines. When you create a variable with the Var statement you can also assign it a value in one step. You do so by following its data type with an equals sign and the value. For example: .. code:: xojo Var i As Integer = 1 Var Name As String = "Igor", Address As String = "15 Rue de Vallee" You can also mix variables with and without initial values, as in: .. code:: xojo Var a As Integer, b As Integer = 15 Variables that are declared without an explicit value get the default value for the type. For example, the default value for Integer (and other number types) is 0. So in the above example, the variable *a* is declared as an Integer but is not assigned a value and gets the default of 0. If you examine the value of a, it will be zero. The variable b, on the other hand, gets the value of 15. Notice also that the following is valid: .. code:: xojo Var a, b, c As Integer = 15 This statement declares three Integer variables and assigns all of them the value of 15. Although this is valid, you should be careful about assigning values to more than one variable in a single Var statement because you could easily lose track of the assignments, which might lead to subtle bugs in your code. .. _/getting_started/using_the_xojo_language/variables_and_constants/accessing_a_variable: Accessing a variable ******************** A variable can be accessed only from where it was declared (such as the method or event). When the method is finished, the memory that was used to store the variable's value becomes available for other uses. This means that another method in the application cannot access the variable value. The term **scope** is used to describe where something, such as a variable, can be accessed. Variables and constants declared within methods have what is called a **local scope** because they are locally available only to the method. Thus these are also referred to as **local variables**. If you declare a variable or constant inside of a code block (such as an If statement, Select statement or any looping statement), its scope is local to the code block and not the entire method. For example, you can write: .. code:: xojo If x > 5 Then Var y As Integer y = 10 End If ' y goes out of scope here; the variable name y can now be reused. ' It is redeclared as a string in the following If statement If x < 5 Then Var y As String y = "hello" End If You can also assign variables a value by using another variable. Doing this does not affect the original variable when you are using simple data types. When using the simple data types, the new variable gets a copy of the data in the original variable. Changing the new variable does not change the value in the original variable. This behavior is different when working with objects. Learn more about object assignment in the Classes section. This example creates a :doc:`String` variable, assigns it a value and then assigns its value to another :doc:`String` variable: .. code:: xojo Var s1 As String = "hello" Var s2 As String s2 = s1 ' s2 now contains "hello" MessageBox(s2) ' Changing s2 does not affect s1 s2 = "world" MessageBox(s2) ' s1 still contains "hello" MessageBox(s1) .. _/getting_started/using_the_xojo_language/variables_and_constants/constants: Constants --------- A constant behaves similarly to a variable with these significant differences: * You use the :doc:`Const` keyword to declare the constant instead of the :doc:`Var` keyword. * You do not have to specify the type when declaring it, although it is allowed. * Its value cannot be changed once it has been set. That is what makes it constant. Here are some example constant declarations: .. code:: xojo Const pi As Double = 3.14159 Const value = 256 * 10 Const kDefault As String = "DefaultName" Constants can be assigned only a literal value, another constant value or the result of a constant expression. Constants created in this manner are also local to the method or event in which they are declared and follow the same scoping rules described for variables. To create a constant that can be used more globally, add it to a Module or Class. .. _/getting_started/using_the_xojo_language/variables_and_constants/naming_rules: Naming rules ------------ These naming rules apply to variable and constant names, but also apply to the name of everything in Xojo including methods, modules and classes which you will learn about later. * Names must start with an ASCII letter (A-Z) or (a-z) * Names that start with an underscore are not recommended and not supported so may not work in future versions * The remainder of the name can contain any of the following: * Alphanumeric ASCII characters (A-Z, a-z, 0-9) * Underscore (_) * Any Unicode character with code point greater than 127 * Names can be any length * Names are case-insensitive (**age** and **Age** are considered the same) If you use an invalid character, you will get a compile error (usually reported as a Syntax Error) when you try to run the project. For names that are specified using the Inspector, you will get a prompt indicating that the name is invalid and the name will revert to its previous name. You might also want to refer to the :doc:`Coding Guidelines` to understand how Xojo code is written and named throughout the documentation. ========== Data types ========== Xojo is a strongly-typed programming language. This means that you must specify the type of every variable you create. Strongly-typed programming languages are safer to use because the compiler can inform you of programming mistakes in variable usage before they make it into your app. Xojo has several built-in data types (called intrinsic types) which fall into these categories: :doc:`Boolean`, :doc:`String`, Numbers (:doc:`Integer`, :doc:`Double` and others) and :doc:`Color`. In addition, :doc:`DateTime` is a commonly used class that is often considered a general type. Integers, Doubles and Colors are also called value types because they contain the actual value you assign to them. Strings, :doc:`arrays` and any class types are called :ref:`reference types` because they actually contain a pointer to the value, but you don't have to specifically worry about that now. .. _/getting_started/using_the_xojo_language/data_types/boolean: Boolean ------- Boolean is one of the simplest data types. It can only contain one of two values: :doc:`True` or :doc:`False` (the default). This is how you declare a Boolean variable: .. code:: xojo Var b As Boolean The special keywords :doc:`True` and :doc:`False` used to change the value of a Boolean variable. If you want to declare a Boolean variable and assign it to :doc:`True`, you can do so in one step: .. code:: xojo Var b As Boolean = True Boolean variables can also be assigned the result of a Boolean expression. A Boolean expression is an expression that evaluates to :doc:`True` or :doc:`False`. For example, this code sets a variable to :doc:`True` or :doc:`False` depending on whether an Integer value is greater than 5: .. code:: xojo Var i As Integer = 10 Var b As Boolean b = (i > 5) ' b gets set to True Boolean values are often used with controls. For example, code such as this tests if a CheckBox has been checked: .. code:: xojo If AutoSaveCheckBox.Value = True Then ' your code here End If A Boolean value is its own expression so you could simplify the above by leaving off the comparison: .. code:: xojo If AutoSaveCheckBox.Value Then ' your code End If Some developers prefer to keep the "= True" part because it is more explicit, but that is a preference that varies from developer to developer. .. _/getting_started/using_the_xojo_language/data_types/boolean_expressions: Boolean expressions ******************* Boolean expressions deal only with the values :doc:`True` and :doc:`False`. :doc:`True` and :doc:`False` are values that Xojo can manipulate, just as it does numbers and strings. There are two types of Boolean expressions: * **Simple**: Boolean expressions state something about a value in the program, generally using operators like = (equals), <> (not equals), <= (less than or equal), or >= (greater than or equal). Sometimes a property or something else in the program will actually just be a Boolean value. The toggle switches you see in the Inspector are good examples, such as the Visible property of a control. For example, Label1.Visible evaluate to :doc:`True` when the label's Visible switch is set to ON; and it evaluates to :doc:`False` when the switch is set to OFF. * **Compound**: Boolean expressions that are made out of other Boolean expressions (which can themselves be either simple or compound), using Boolean operators Not, And or Or. As with other types of expressions, you will often need to use parentheses in Boolean expressions to make it clear in what order operations should be carried out. Also, as with other types of expressions, Xojo works out the final value from the inside out. .. _/getting_started/using_the_xojo_language/data_types/example_expressions: Example expressions ******************* Consider this expression: .. code:: xojo (Source1.Text.Val = 0 And Source1.Text <> "0") Or (Source2.Text.Val = 0 And Source2.Text <> "0") A compound expression such as this is evaluated according to the left-to-right rule. The two branches of the :doc:`Or` expression are evaluated separately prior to Or-ing them. If a compound expression is joined by :doc:`And`, then any sub-expression that evaluates to :doc:`False` ends the process of evaluating any subsequent sub-expressions within that compound expression. This is because one :doc:`False` sub-expression means that the entire compound :doc:`And` expression must also be :doc:`False`. An expression like this can be represented as a syntax tree: .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/data_types_boolean_syntax_tree.png The expression is evaluated from the bottom of the tree up. Each level of the tree is evaluated left to right. In this example, the first step is to evaluate the left branch of the :doc:`Or` expression. It is a compound expression of its own, as its branches use :doc:`And`. It starts with .. code:: xojo Source1.Text.Val = 0 If this expression returns :doc:`True` then it evaluates: .. code:: xojo Source1.Text <> "0" Lastly, if .. code:: xojo Source1.Text.Val = 0 is :doc:`False`, there is enough information to evaluate the **And** so :doc:`False` is returned to the next higher level without evaluating the second expression. Since the two compound expressions are joined by :doc:`Or`, it needs to evaluate the right compound expression at the base of the tree even if the left compound expression evaluates to :doc:`False`. If the left branch is :doc:`False`, but the right branch is :doc:`True`, then the complete compound expression is :doc:`True`. The same procedure is followed in evaluating the right branch of the compound :doc:`Or` statement. .. _/getting_started/using_the_xojo_language/data_types/strings: Strings ------- A String is any series of characters. Use the :doc:`String` data type to store a series of characters. The maximum size of a String is approximately 4GB. To declare a variable for containing text: .. code:: xojo Var firstName As String The default value of a String is the empty String, often written as "": .. code:: xojo Var firstName As String If firstName.IsEmpty Then ' Do something if string is empty End If You can also directly assign text to the variable as part of its declaration: .. code:: xojo Var t As String = "Hello, World!" Basically, any type of characters can be stored as a String, such as: "Lucas", "12/30/2015", "42.15". Yes, even though those last two values look like a date and a number, they are actually Strings because they are a series of characters. If you want to treat them as if they are an actual date or a number then you need to use a specific data type for that. .. _/getting_started/using_the_xojo_language/data_types/combining_text: Combining text ************** You can combine multiple text values together by using the addition (+) operator. This concatenates the two values together. For example: .. code:: xojo Var name As String = "Lucas" ' no space at end Var age As String = "11" Var combined As String = name + age ' result is "Lucas11" Remember, that text values combined in this manner are only ever concatenated, regardless of what value they contain. For example, even if it looks like the string contains numbers, adding them will not calculate their sum: .. code:: xojo Var value1 As String = "42" Var value2 As String = "10" Var value3 As String = value1 + value2 ' result is "4210", not "52" .. _/getting_started/using_the_xojo_language/data_types/string_methods: String methods ************** String has many methods that can be used to manage its contents. For example, you may want to check if a String variable contains another String value or you may want to remove whitespace from it. To learn more about the methods that are available with String variables, refer to the :doc:`String` data type. .. _/getting_started/using_the_xojo_language/data_types/encodings: Encodings ********* In order to store text outside of your apps (in files or databases, for example), the text needs to be encoded. When text is encoded, it is converted to a series of bytes that can be processed by other apps and systems. If you are loading text from these outside sources into your app, then you'll also need to know the encoding so you can set it using the :doc:`DefineEncoding` method. The most common encoding for Unicode text is UTF-8, but if you may run across other encodings, such as ASCII, Win-Latin, MacRoman, etc. Refer to the Encodings section for more information. .. _/getting_started/using_the_xojo_language/data_types/numbers: Numbers ------- The Xojo programming language has separate data types depending on the type of number you need. The Integer type is used for whole numbers (positive or negative and 0). If you need a number that contains a decimal, you can use either Double or Currency. Because these data types contain actual numbers, you can perform mathematical computations on them. .. _/getting_started/using_the_xojo_language/data_types/integer: Integer ******* An :doc:`Integer` contains whole numbers and is declared like this (default value is 0): .. code:: xojo Var value As Integer You can assign it a value when you declare it: .. code:: xojo Var value As Integer = 42 If you try to assign a value with a decimal to an Integer, the decimal value is truncated (not rounded) before it is assigned. For example: .. code:: xojo Var value As Integer value = 42.65 ' value will contain 42 You can perform mathematical calculations on integers: .. code:: xojo Var i1 As Integer = 42 Var i2 As Integer = 8 Var sum As Integer sum = i1 + i2 ' sum = 50 Var product As Integer product = i1 * i2 ' product = 336 Var div As Integer div = i1 / i2 ' div = 5 (truncated) .. _/getting_started/using_the_xojo_language/data_types/double: Double ****** A :doc:`Double` is a number that can contain a floating-point decimal value. In other languages, Double may be referred to as a double precision real number. Because Doubles are numbers, you can perform mathematical calculations on them. The default value of a Double is 0.0: .. code:: xojo Var value As Double ' value is 0.0 You can also assign a value when you declare it: .. code:: xojo Var value As Double = 3.14 You should avoid using Double to store monetary values. As an IEEE floating-point number, **there are some decimal values that cannot be represented as a Double and this could cause rounding issues**. In these situations, use Currency instead. For example, consider this code: .. code:: xojo Var d1 As Double = 5.1 Var d2 As Double = d1 * 100 Var d3 As Double = Round(d1 * 100) The value 5.1 cannot be stored directly in a Double (it's actually more like 5.0999999999999996). When you multiple that actually value by 100 you end up with 509.9999999999999432 (this is *d2*) when you are really expecting a value of 510. In this case you want to use the :doc:`Round` method to get the value you want (*d3*). .. note:: Learn more about how floating point values are stored by reading `A Floating Point Values Primer `_. .. _/getting_started/using_the_xojo_language/data_types/currency: Currency ******** :doc:`Currency` is a fixed-point number format that holds approximately 15 digits to the left of the decimal point and 4 digits to the right. It is always accurate to four decimal places. Currency is useful for calculations involving money and for calculations where accuracy is very important. It is compatible with the Currency data type offered in some versions of Visual Basic. The default value of a Currency variable is 0.0. Here is are some declarations: .. code:: xojo Var value As Currency Var amount As Currency = 42.56 .. _/getting_started/using_the_xojo_language/data_types/mathematical_computations: Mathematical computations ************************* Performing mathematical calculations is a very common task in programming, and all of the common mathematical operations are supported for the number data types. This is a list of mathematical operators: .. csv-table:: :header: "Operation", "Operator", "Example" :widths: auto "Addition",":doc:`+`","2 + 3 = 5" "Subtraction",":doc:`-`","3 - 2 = 1" "Multiplication",":doc:`*`","3 * 2 = 6" "Floating Point Division",":doc:`/`","6 / 4 = 1.5" "Integer Division",":doc:`\\`","6 \\ 4 = 1" "Modulo",":doc:`Mod`","6 Mod 3 = 0, 6 Mod 4 = 2" "Exponentiation",":doc:`^`","2 ^ 3 = 8" There are also many built-in mathematical operators and functions that are covered in detail in the Documentation. Expressions support standard mathematical precedence. This means that equations surrounded by parentheses are handled first. The expression is evaluated beginning with the set of parentheses that is embedded inside the most outer sets of parentheses. Next, exponentiation is performed, followed by any multiplication or division from left to right. Finally any addition or subtraction is performed. As an example, these three expressions return different results because of the placement of parentheses. .. csv-table:: :header: "Expression", "Result" :widths: auto "2 + 3 * (5 + 3)","26" "(2 + 3) * (5 + 3)","40" "2 + (3 * 5) + 3","20" For more information, refer to the :doc:`Operator Precedence` topic. .. _/getting_started/using_the_xojo_language/data_types/converting_between_numbers: Converting between numbers ************************** You can convert between numbers without any special commands, but you have to keep in mind that the numbers may be rounded or truncated. For example, converting a Double to an Integer does not round the value, but instead truncates it to the integer part: .. code:: xojo Var d As Double = 4.6 Var i As Integer = d ' i = 4 Use the Round method to round a value. .. code:: xojo Var d As Double = 4.6 Var i As Integer = Round(d) ' i = 5 You can also directly assign an Integer to a Double: .. code:: xojo Var i As Integer = 42 Var d As Double = i ' d = 42.0 For math that has values that are both Doubles and Integers, then a Double is used to calculate the value. For example, consider this code: .. code:: xojo Var result As Integer result = 5.1 * 8 In the above case, the expression is calculated as 5.1 * 8, which is 40.8 and then that is assigned to the Integer variable for *result*, which then truncates it to 40. If you wanted to Round that to 41, then you would use the :doc:`Round` method like this: .. code:: xojo Var result As Integer result = Round(5.1 * 8) ' result = 41 .. _/getting_started/using_the_xojo_language/data_types/color: `Color` ------- Color is a data type that stores the value of a color. A Color “value” actually consists of three numeric values that can be set using any of the three popular color models, Red-Green-Blue, Hue-Saturation-Value, or Cyan-Magenta-Yellow. Each RGB value is stored as a byte. That means that each number can take on 256 possible values. A common way to create a specific color is to use the :ref:`RGB method`. This creates a color variable containing a blue-gray color: .. code:: xojo Var c As Color = Color.RGB(124, 124, 163) You can also assign color values using some of the Color constants or by directly specifying hex values using the :doc:`&c` operator: .. code:: xojo Var blueColor As Color = Color.Blue Var greenColor As Color = &c00ff00 .. _/getting_started/using_the_xojo_language/data_types/datetime: DateTime -------- DateTime is a class and not an intrinsic data type. But it is commonly used and worth mentioning here. Because it is a class, you have to create an instance of it before you can use it. This also means it is a :ref:`reference type`. Because DateTime is a class, you use the :doc:`New` operator to create a new date like this: .. code:: xojo Var d As New DateTime(2017, 4, 15) The best way to compare dates is to use the SecondsFrom1970 property like this: .. code:: xojo Var d1 As New DateTime(2017, 4, 15) Var d2 As New DateTime(2018, 4, 15) If d1.SecondsFrom1970 > d2.SecondsFrom1970 Then ' do something Else ' do something End If .. _/getting_started/using_the_xojo_language/data_types/calculating_intervals_between_dates: Calculating intervals between dates *********************************** The Now method which gives you the current date and time: .. code:: xojo Var d As DateTime = DateTime.Now You can also create a specific date by providing the values like this: .. code:: xojo Var d As New DateTime(2019, 8, 20, TimeZone.Current) ' Aug 20, 2019 Once you have a DateTime, you cannot modifiy it. You can however subtract dates to get a :doc:`DateInterval` of the time between the two dates or you can add a DateInterval to a date to get a new DateTime. This code gets a date two months before today: .. code:: xojo Var twoMonths As New DateInterval twoMonths.Months = 2 ' 2 month interval ' Get date two months before today Var past As DateTime = DateTime.Now - twoMonths This calculate the interval until January 1, 2030: .. code:: xojo Var d2 As New DateTime(2030, 1, 1, TimeZone.Current) Var interval As DateInterval interval = d2 - DateTime.Now ' Check the Years, Months, Days, etc. properties To display dates you use the ToString method. This method lets you specify the format you want for the date part and the time part. You can also hide one of those parts. For example, This code creates a date for 31-Oct-2017 at 11:00am and then converts it to text in several different ways: .. code:: xojo Var myDate As DateTime(2017, 10, 31, 11, 00, 00, TimeZone.Current) ' Get just the date part Var dateValue As String = myDate.ToString(Locale.Current, _ DateTime.FormatStyles.Short, DateTime.FormatStyle.None) ' Get just the time part Var dateValue As String = myDate.ToString(Locale.Current, _ DateTime.FormatStyles.None, DateTime.FormatStyles.Short) ' Get the full date and time Var dateValue As String = myDate.ToString(Locale.Current, _ DateTime.FormatStyles.Short, DateTime.FormatStyles.Short) To convert a string that contains a date back to a DateTime instance use the FromString method. Since the format for dates varies greatly by locale when storing dates as strings you should always use the standard SQLDateTime format (YYYY-MM-DD HH:MM:SS). You can get a date in SQL format using the SQLDate or SQLDateTime methods: .. code:: xojo Var sqlDate As String = DateTime.Now.SQLDate A date in that format can be converted back to a DateTime instance using the FromString method like this: .. code:: xojo Var SQLDateTime As String = "2015-08-01 11:00" Var myDate1 As DateTime = DateTime.FromString(SQLDateTime) .. _/getting_started/using_the_xojo_language/data_types/typeless_variables: Typeless variables ------------------ There may be times where you need a variable that can contain the value of any other variable, regardless of its type. The :doc:`Variant` data type is used for this purpose. .. _/getting_started/using_the_xojo_language/data_types/variant: Variant ******* The :doc:`Variant` type can contain a value of any type and has the ability to implicitly convert its values to other data types. Use this with caution as it can result in hard-to-find bugs in your code when you run into unintended behavior from type conversions. .. _/getting_started/using_the_xojo_language/data_types/converting_between_data_types: Converting between data types ----------------------------- There may be times when you need to change a value from one data type to another. This is usually because you want to use the value with something that is designed to work with a different data type. For example, you might want to include a number in a Label's Value property, but the Value property's data type is String, not a number. Consequently, if you try to assign a number to the Value property, you will get a type mismatch error when you try to run your project. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/data_types_type_mismatch_error.png Instead, you need to convert the number to the appropriate type. Most commonly, you will need to convert from numbers to strings and from strings to numbers. You can do this using the ToString and FromString methods on the data types. For example, to convert an Integer to a String: .. code:: xojo Var i As Integer = 42 Var num As String = "The number is " + i.ToString To convert a String to an Integer: .. code:: xojo Var value As Integer value = Integer.FromString("42") ' value = 42 You may also want to display a DateTime as a string. This code display the current date and time: .. code:: xojo Var d As DateTime = DateTime.Now Label1.Text = d.ToString .. _/getting_started/using_the_xojo_language/data_types/see_also: .. seealso:: :doc:`Integer`, :doc:`Double`, :doc:`Currency` data types; :doc:`DateTime`, :doc:`Variant` classes; :ref:`About Xojo Programming Language` topic ================ Comparing values ================ There are many times when you need to compare two values to determine whether or not a particular condition exists. When making a comparison, what you are really doing is making a statement that will either be :doc:`True` or :doc:`False`. For example, the statement "My dog is a cat" evaluates to :doc:`False`. However, the statement "My dog weighs more than my cat" may evaluate to :doc:`True`. .. csv-table:: :header: "Description", "Symbol", "Example", "Result" :widths: auto "Equality","=","5 = 5","True" "Inequality","<>","5 <> 5","False" "Greater Than",">","6 > 5","True" "Less Than","<","6 < 5","False" "Greater Than or Equal To",">=","6 >= 5","True" "Less Than or Equal To","<=","6 <= 5","False" Text and Boolean values can also be used for comparisons. Text comparisons are case-insensitive and alphabetical. This means that "Steve" and "steve" are equal. But "diane" is less than "steve" because "diane" falls alphabetically before "steve". If you need to make case-sensitive or lexicographic comparisons, you use the String.Compare method like this: .. code:: xojo Var dog As String = "Dog" Var cat As String = "Cat" Var result As Integer result = dog.Compare(cat, ComparisonOptions.CaseSensitive) ' result = 1, because "Dog" > "Cat" To do a case-sensitive comparison: .. code:: xojo Var dog As String = "Dog" Var cat As String = "aCat" Var result As Integer result = dog.Compare(cat, ComparisonOptions.CaseSensitive, Locale.Raw) ' result = -1, because "Dog" < "aCat" when case-sensitive In addition, there is a way to determine whether two floating point numbers are close enough to be considered equal. This is used to account for the imprecision of operations such as floating point division. Use the Double.Equals to do this comparison. For example, if you are comparing 10000 to a value and specify x=1, then the acceptable values are 10000.000000000002, 10000.0 and 9999.999999999998. .. code:: xojo Var d As Double = 10000 Var compareValue As double = 10000.000000000002 If d.Equals(compareValue, 1) Then ' take action here... End If .. _/getting_started/using_the_xojo_language/comparing_values/logical_comparisons: Logical comparisons ------------------- You can test more than one comparison at a time using the And, Or, and Not operators. When passed Boolean values, these operators determine whether the expression is :doc:`True` or :doc:`False`. .. _/getting_started/using_the_xojo_language/comparing_values/and_operator: And operator ************ Use the :doc:`And` operator when you need to know if all comparisons evaluate to :doc:`True`. In the example below, if the variable x contains 10 then the expression evaluates to :doc:`False` because 10 is not both greater than 1 and less than 5: .. code:: xojo x > 1 And x < 5 .. _/getting_started/using_the_xojo_language/comparing_values/or_operator: Or operator *********** Use the :doc:`Or` operator when you need to know if any of the comparisons evaluate to :doc:`True`. In the example below, if the variable x contains 10 then the expression evaluates to :doc:`True`: .. code:: xojo x > 1 Or x < 5 This is because 10 is greater than 1. The fact that it is not less than 5 does not matter because you only care if at least one of the comparisons is :doc:`True`. .. _/getting_started/using_the_xojo_language/comparing_values/xor_operator: Xor operator ************ Use the :doc:`Xor` operator when you need to know whether any of the comparisons evaluate to :doc:`True`, but not all of them. In the example below, if the variable x contains 10 then the expression evaluates to :doc:`True`. .. code:: xojo x > 1 Xor x < 5 This is because only one of the comparisons is :doc:`True`. However, if x contains 3 then the above expression returns :doc:`False` because both sub expressions are :doc:`True`. .. _/getting_started/using_the_xojo_language/comparing_values/not_operator: Not operator ************ Use the :doc:`Not` operator to reverse the value of a boolean variable. For example, this returns :doc:`True` when x is equal to or greater than 0: .. code:: xojo Not x < 0 .. _/getting_started/using_the_xojo_language/comparing_values/truth_table: Truth table *********** A truth table can help you determine what the results of these various comparisons are. .. csv-table:: :header: "Expression 1", "Expression 2", "And", "Or", "Xor" :widths: auto "True","True","True","True","False" "True","False","False","True","True" "False","True","False","True","True" "False","False","False","False","False" .. _/getting_started/using_the_xojo_language/comparing_values/bitwise_comparisons: Bitwise comparisons ------------------- Sometimes it is helpful, usually in more advanced situations, to be able to compare the actual bits that make up Integers. You can do this using :doc:`Bitwise` class method comparisons, which work by treating the Integer as a binary number. With a binary number, you can then compare each individual bit to get a new set of bits, which results in a new number. Bitwise comparisons work with the And, Or and Xor operators when they are used with Integers instead of Boolean expressions. For example, consider the decimal numbers 5 and 3. Written in binary, they are: * 101 (5) * 011 (3) To evaluate 5 Xor 3, you do the comparison on each bit. So for this example: * 1 Xor 0 = 1 * 0 Xor 1 = 1 * 1 Xor 1 = 0 The new binary value is 110, which is decimal 6. So this is the final result of the equation: 5 Xor 3 = 6. The Not operator can also be used to compare Integers. If you pass an integer to Not, it reverses each bit value. To evaluate Not 5: * Not 1 = 0 * Not 0 = 1 * Not 1 = 0 The new binary value is 010, which is decimal 2. .. _/getting_started/using_the_xojo_language/comparing_values/bitwise_tables: Bitwise tables ************** Results for And, Or and Xor: .. csv-table:: :header: "Bit 1", "Bit 2", "And", "Or", "Xor" :widths: auto "1","1","1","1","0" "1","0","0","1","1" "0","1","0","1","1" "0","0","0","0","0" Result for Not: .. csv-table:: :header: "Bit 1", "Not Bit 1" :widths: auto "1","0" "0","1" ======================== Event driven programming ======================== Your users interact with your apps by clicking the mouse, tapping the screen, or typing on a keyboard. Each time the user clicks or taps on a part of your app's interface or types something in a field, an event occurs. The event is the action the user took (the click, tap or key press) and where it took place (on this button, on that menu item, or in this text field). Some events can even indirectly cause other events. For example, when the user selects a menu item (causing an event) that opens a window, it causes another event — the opening of the window. With event-driven programming, each object you create can include, as part of itself, the code that executes in response to the various events that can occur for that type of object. For example, a DesktopButton (or WebButton or MobileButton) can include the code you wish to execute when it is pressed. An object can even respond to events you might not have thought it could — such as when the user moves the mouse pointer over it. When the user causes an event, the app checks to see if the object the event was directed towards has any code that needs to execute in response to that event. If the object has code for the event, then it is called and runs. When it is finished running, the app waits for the user to cause another event to occur. This continues until something causes the app to quit (and quitting an app also raises events). .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/event_driven_programming_add_handler_dialog.png :alt: Add Event Handler Dialog showing DesktopButton Pressed Event Selected As mentioned earlier, the user can also indirectly cause events to occur. Buttons, for example, have an event called Pressed which occurs when the user presses the button. The code that handles the response to an event is called (appropriately enough) an event handler. Suppose the button's Pressed event handler has code that opens another window (for a desktop app). When the user clicks the button, the Pressed event handler opens a window and an Opening event is sent to the new window. This is not an event the user caused directly. The user caused this event indirectly by clicking the button whose code opened the new window. To add an event handler for a control, click on the control and then select the "+" button on the command bar and choose "Add Event Handler". This displays the Event Handler window for the control. There are many events that can occur for each object in your app. The good news is that you don't have to learn about all of them. You only need to know where to look for them so that, if you want to respond to an event, you can find out if the object is able to respond to that event. The User Interface section covers all the UI components that can be used by desktop, web and mobile apps. In each section you learn about the commonly used events that are available for the controls. You can also learn about all the event handlers by referring to the specific component or control in the Documentation. ===================== Controlling code flow ===================== The methods you write execute one line at a time from top to bottom, left to right. There will be times when you want your app to execute some of its code based on certain conditions (using comparisons). When your app's logic needs to make decisions it's called branching. This allows you to control what code gets executed and when. There are two branching statements: :doc:`If...Then...End If` and :doc:`Select Case`. .. _/getting_started/using_the_xojo_language/controlling_code_flow/if...then...endif: If...Then...EndIf ----------------- The :doc:`If...Then...End If` statement is used when your code needs to test a Boolean (:doc:`True` or :doc:`False`) expression and then execute code based on its result. If the expression you are testing is :doc:`True`, then the lines of code you place between the If...Then line and the End If line are executed, otherwise they are skipped. .. code:: xojo If condition Then ' [Your code goes here] End If To run your code when the Integer value month is equal to 1: .. code:: xojo If month = 1 Then ' [Your code goes here] End If The part “month = 1” is a boolean expression; it's either :doc:`True` or :doc:`False`. The variable month is either 1 or it is not 1. Suppose you have a Button that performs an additional task if a particular CheckBox is checked. The value property of a CheckBox is Boolean so you can test it in an If statement like this: .. code:: xojo If CheckBox1.Value Then ' [Your code goes here] End If Remember that you can declare local variables using the Var statement inside an If statement. However, such variables go out of scope after the End If statement. For example: .. code:: xojo If error = -123 Then Var a As String a = "Oops! An error occurred." End If Label1.Text = a ' out of scope! If you need the variable after the End If statement, you should declare it local to the entire method, not within the If...End If statement. .. _/getting_started/using_the_xojo_language/controlling_code_flow/if...then...else...end_if: If...Then...Else...End If ************************* An :doc:`If...Then...End If` statement can be expanded by including an additional Else clause. In some cases, you need to run some code if a boolean expression is :doc:`True`, but a different set of code if the boolean expression is :doc:`False`. In these situations, you add the Else clause for the code that will run when the boolean expression evaluates to :doc:`False`. This code sets the text based on a month variable: .. code:: xojo Var month As Integer = 6 Var result As String If month = 1 Then result = "It is January" Else ' everything else result = "Not January" End If ' result = "Not January" .. _/getting_started/using_the_xojo_language/controlling_code_flow/if...then...elseif...end_if: If...Then...ElseIf...End If *************************** The next step in enhancing your :doc:`If...Then...End If` statement is to have multiple tests when the initial boolean expression is :doc:`False`. For each additional test, you use the ElseIf statement. .. code:: xojo Var result As String If month = 1 Then result = "It is January." ElseIf month < 4 Then result = "It is still Winter." Else result = "It is not Winter." End If You could, of course, use an additional If...Then...End If statement inside the Else portion of the first If statement to perform another test. However, this adds another End If and needlessly complicates your code. Instead, you can use as many ElseIf statements as you need, followed by an optional final Else statement: .. code:: xojo Var result As String If month = 1 Then result = "It's January." ElseIf month < 4 Then result = "It's still Winter." ElseIf month < 6 Then result = "It must be Spring." Else result = "Summer or Fall". End If If the initial condition is :doc:`False`, your code continues to test the ElseIf conditions until it finds one that is :doc:`True`. It then executes the code associated with that ElseIf statement and continues executing the lines of code that follow the End If statement. .. _/getting_started/using_the_xojo_language/controlling_code_flow/if...then...else: If...Then...Else **************** Dialing it down a bit, a simple If statement can written on one line, provided the code that follows the Then and the (optional) Else statements can all be written on one line. When you use this syntax, you omit the End If statement. Some examples: .. code:: xojo Var s As String If error = 123 Then s = "An error occurred." If error = 123 Then s = "An error occurred." Else s = "Success" If error = 103 Then Break .. warning:: Single line If statements are not recommended in most cases as it will be difficult when debugging to know if the condition occurred or not. The exception is when used with the :doc:`Break` method (as in the last example above) to display the debugger when a particular condition occurs. .. _/getting_started/using_the_xojo_language/controlling_code_flow/if_operator: If operator ----------- For situations where you need to simply return a result based on a boolean expression, you can use the :doc:`If` operator. .. code:: xojo If(condition, resultIfTrue, resultIfFalse) The condition is evaluated and if it is :doc:`True`, the *resultIfTrue* is returned, otherwise *resultIfFalse* is returned. For example, this code outputs “Big”: .. code:: xojo Var result As String Var myInteger As Integer = 41 result = If(myInteger > 40, "Big", "Small") Because this returns a result, the types of *resultIfTrue* and *resultIfFalse* must match (or be able to be converted between each other) and must match the destination type. .. _/getting_started/using_the_xojo_language/controlling_code_flow/select...case: Select...Case ------------- When you need to test a property or variable for one of many possible values and then take action based on that value, use a :doc:`Select Case` statement. Consider the following example that uses If...ElseIf..End If to test a variable (dayNumber) and display the day of the week: .. code:: xojo Var dayName As String If dayNumber = 2 Then dayName = "Monday" ElseIf dayNumber = 3 Then dayName = "Tuesday" ElseIf dayNumber = 4 Then dayName = "Wednesday" ElseIf dayNumber = 5 Then dayName = "Thursday" ElseIf dayNumber = 6 Then dayName = "Friday" Else dayName = "the weekend." End If Var result As String = "It is " + dayName No two of these conditions can be :doc:`True` at the same time. While this method of writing the code works, it's not that easy to read. This next example uses a Select...Case statement to achieve the same result. It is far easier to read: .. code:: xojo Var dayName As String Select Case dayNumber Case 2 dayName = "Monday" Case 3 dayName = "Tuesday" Case 4 dayName = "Wednesday" Case 5 dayName = "Thursday" Case 6 dayName = "Friday" Else MessageBox("the weekend.") End Select Var result As String = "It is " + dayName The Select...Case statement compares the variable or property passed in the first line to each value on the Case statements. Once a match is found, the code between that case and the next is executed. You can use any variable type in the Select...Case. This example compare text values: .. code:: xojo ' dayName is a Text variable Var dayNumber As Integer Select Case dayName Case "Monday" dayNumber = 2 Case "Tuesday" dayNumber = 3 End Select Select...Case statements can contain an Else statement to handle all other values not explicitly handled by a Case. You can create local variables using the Var statement inside a Case statement. However, such variables go out of scope at the conclusion of the Case statement. For example: .. code:: xojo Select Case dayNumber Case 2 Var day As String day = "Tuesday" Else Var day As String ' new scope day = "It is NOT Tuesday!") End Select ' day is now out of scope To be available after the End Select statement is reached, the variable “day” should be declared prior to the Select...Case statement. The Select…Case statement works with variables of any data type, including Strings, Integers, Singles, Doubles, Booleans, and Colors. For example, you can compare colors, as in the following example: .. code:: xojo Var c As Color c = &cFF0000 ' pure red Select Case c Case &c00FF00 ' green MessageBox("Green") Case &cFF0000 ' red MessageBox("Red") Case &c0000FF ' blue MessageBox("Blue") End Select A Case statement can accept more than one value, with different values separated by commas. For example, the following is valid: .. code:: xojo Var c As Color c = &cFF0000 ' pure red Select Case c Case &c00FF00, &cFF0000 ' green, red MessageBox("Green or red") Case &cFF0000 ' red MessageBox("Red") Case &c0000FF ' blue MessageBox("Blue") End Select In the preceding example, the first Case statement is :doc:`True`, so its code executes. Although the color passed to Select…Case is Red, the code for the second case does not execute because it is not the first matching case. The Select Case statement accepts an Else clause. The code in the Else clause executes only if none of the preceding cases match. The Else clause can be written as either "Else" or "Case Else". In the following example, the Case Else clause executes because the color FFFF00 does not match any of the Case statements: .. code:: xojo Var c As Color c = &cFF0000 ' pure red Select Case c Case &c00FF00 ' green MessageBox("Green") Case &cFF0000 ' red MessageBox("Red") Case &c0000FF ' blue MessageBox("Blue") Case Else MessageBox("None of the above") End Select The Case statement can also accept a range of consecutive values using the “To” keyword. For example: .. code:: xojo Var i As Integer = 53 Select Case i Case 1 To 25 MessageBox("25 or less") Case 26 To 50 MessageBox("26 to 50") Case 51 To 100 MessageBox("51 to 100") End Select In this example, the third case, “51 to 100”, is :doc:`True`. You can combine ranges with nonconsecutive values, by separating them with commas, such as: .. code:: xojo Case 0, 26 To 50, 75, 100 To 200 You can write inequalities with the “Is” keyword and an inequality operator. The syntax is: .. code:: xojo Is ineqalityOperator For example: .. code:: xojo Var i As Integer = 10 Select Case i Case Is <= 10 ' this case selected Case Is > 10 ' this case not selected End Select You can combine inequalities with values, as in: .. code:: xojo Var i As Integer = 75 Select Case i Case 0, Is <= 10, 100 ' case not selected Case Is > 10, Is < 99 ' case selected End Select You can even use functions that return a value of the specified data type in a Case statement. Here is a simple example: .. code:: xojo Var i As Integer = 4 Var a As Integer = 2 Select Case i Case CalcSquare(a) ' case 1 Case a ' case 2 Else ' no match End Select The function in the first Case statement is: .. code:: xojo Function CalcSquare(a As Integer) As Integer Return a * a End Function In this example, the function squares the value passed to it, so the first Case statement matches. In the case of a simple function like this, you can write the expression in the Case statement itself. That is, the following is an equivalent matching Case statement: .. code:: xojo Case a * a The Select Case statement can also compare variables that are Objects. The following example uses a Select…Case statement to determine which button the user pressed in a MessageDialog box. The Select Case statement compares objects of type MessageDialogButton to determine which of three possible dialog buttons was pressed. .. code:: xojo Var d As New MessageDialog Var b As MessageDialogButton d.Icon = MessageDialog.GraphicCaution d.ActionButton.Caption = "Save" d.CancelButton.Visible = True d.AlternateActionButton.Visible = True d.AlternateActionButton.Caption = "Don't Save" d.Message = "Save changes before closing?" d.Explanation = "If you don't save your changes, you will lose your work." b = d.ShowModal Select Case b Case d.ActionButton ' user pressed Save Case d.AlternateActionButton ' user pressed Don't Save Case d.CancelButton ' user pressed Cancel End Select You can also use the IsA operator to determine whether an object is of a particular class. The syntax is: .. code:: xojo Case IsA ClassName Here is a simple example. The code in a DesktopButton.Pressed event handler in a Window: .. code:: xojo Select Case Me Case IsA DesktopButton MessageBox("I 'm a Button.") Case IsA DesktopTextField MessageBox("Nope!") End Select The term “Me” refers to the Button, so the first Case statement returns :doc:`True`. .. _/getting_started/using_the_xojo_language/controlling_code_flow/nesting: Nesting ------- All of these commands can be nested within each other in order to achieve the desired effect. For example, you can nest an If...Then within a Select...Case: .. code:: xojo Select Case value Case 42 If value2 > 3 Then MessageBox("Cancel") End If End Select ============== Repeating code ============== There may be times when one or more lines of code need to be executed more than once. If you know how many times the code should execute, you could repeat the code that many times, but that is not an efficient way to write code. For example, if you wanted a button to display a message three times when clicked, you could put the MessageBox method in your code three times like this: .. code:: xojo MessageBox("One") MessageBox("Two") MessageBox("Three") But suppose you need it to count fifty times or perhaps until a certain condition is met? Repeating the code over and over in these cases will either be just tedious or not possible. How do you solve this problem? The answer is a loop. Loops execute one or more lines of code over and over again. The following types of loop structures are available: * **While...Wend**: The loop runs until the condition specified in the While statement is satisfied. * **Do...Loop**: The loop runs until the condition specified in the Do or Loop statements are satisfied. * **For...Next**: The loop runs a specified number of times given in the For statement. A local counter variable controls the execution of the loop. * **For...Each**: The loop runs repeatedly for each element in an array. You can declare local variables inside a loop structure. When you define a local variable inside a loop structure, its scope is local to the structure itself, not the entire method. It goes out of scope after the condition of the loop is satisfied. If you need to use the variable after the loop executes, define it outside the loop, so that it is local to the method. .. _/getting_started/using_the_xojo_language/repeating_code/while...wend: While...Wend ------------ A :doc:`While...Wend` loop executes one or more lines of code between the While and the Wend statements. The code between these statements is executed repeatedly, provided that the condition passed to the While statement continues to evaluate to :doc:`True`. Consider the following example: .. code:: xojo Var i As Integer While i < 10 i = i + 1 Wend The variable “i” will be zero by default when it is created by the Var statement. Because zero is less than ten, execution will move inside the While...Wend loop. The variable i is incremented by one and the loop returns to the top where the While statement checks to see if the condition is still :doc:`True` and if it is, then the code inside the loop executes again. This continues until the condition is no longer :doc:`True`. If the variable i was not less than ten in the first place, the contents for the loop are skipped entirely and execution would continue at the line of code after the Wend statement. .. _/getting_started/using_the_xojo_language/repeating_code/do...loop: Do...Loop --------- The :doc:`Do...Loop` is similar to the While loop but a bit more flexible. Do loops continue to execute all lines of code between the Do and Loop statements until a particular condition is :doc:`True`. While loops on the other hand execute as long as the condition remains :doc:`True`. Do loops provide more flexibility than While loops because they allow you to test the condition at the beginning or end of the loop. The example below shows two loops; one testing the condition at the beginning and the other testing it at the end: .. code:: xojo Var i As Integer Do Until i = 10 i = i + 1 Loop i = 0 Do i = i + 1 Loop Until i = 10 The difference between these two loops is this: In the first case, the loop will not execute if the variable i is already equal to 10. The second loop executes at least once regardless of the value of i because the condition is not tested until the end of the loop. It is possible to create a Do loop that does not test for any condition. Consider this loop: .. code:: xojo Do i = i + 1 Loop Because there is no test, this loop will run endlessly. You use the Exit command to force a loop to exit without testing for a condition. However, this is generally considered poor design because you have to read through the code to figure out what will cause the loop to end. .. code:: xojo Do i = i + 1 If i > 10 Then Exit Loop .. _/getting_started/using_the_xojo_language/repeating_code/endless_loops: Endless loops ------------- Much like the situation with a Do...Loop without a condition, you should be sure that the code inside your While and Do loops eventually causes the condition to be satisfied. Otherwise, you will end up with an endless loop that runs forever. Should you do this accidentally, you can click the Stop button in the Debugger. If this doesn't work, you can always “force-quit” your running application using Task Manager on Windows, the Force Quit window on macOS or the Tasks window in Linux. .. _/getting_started/using_the_xojo_language/repeating_code/lengthy_loops: Lengthy loops ------------- When a loop starts running, its process “takes over” and doesn't allow the user to interact with interface elements such as menus, buttons, and scroll bars. On modern computers and reasonably short loops, this isn't a problem because the loop executes faster than the user can think of another button to push or menu item to select. If this is not true, there are a couple of things you can do: * If the user should wait until the loop is finished before doing anything else (e.g., if a user action might invalidate the results of the loop), you can signal that a lengthy operation is in progress by changing the mouse cursor to a “wait” cursor until the loop ends. * If the user is permitted to do other tasks while the loop is running, you should consider using a thread. A thread runs code in the background, allowing the main application to handle user operations in the foreground. .. _/getting_started/using_the_xojo_language/repeating_code/for...next: For...Next ---------- While and Do loops are perfect when the number of times the loop should execute cannot be determined because it is based on a condition. A :doc:`For...Next` loop is for cases in which you can determine the number of times to execute the loop. For example, suppose you want to add the numbers one through ten to a List Box. Since you know exactly how many times the code should execute, a For...Next loop is the right choice. For...Next loops also differ from While and Do loops because For loops have a loop counter variable, a starting value for that variable and an ending value. The basic construction of a For loop is: .. code:: xojo Var Counter As Integer For Counter = 0 To 100 ' [your code goes here] Next Notice that the Var statement declares the counter as an :doc:`Integer`. Although an Integer is the most common way to define the counter variable, you can also declare it as a Single or Double. In this example, the counter variable was declared in the usual way, via the Var statement. Since counter variables are rarely needed outside the For loop, you can also declare the counter variable right inside the For statement. In other words, you can redo this example like this: .. code:: xojo For Counter As Integer = 0 To 100 ' [your code goes here] Next Notice that the Var statement has been removed from the example. If you declare the counter variable this way, you can use it only within the For loop. It goes out of scope after the For loop is finished. This is the recommended way to declare a counter variable. Of course, if you need to read or change the value of the counter variable outside the For loop, which is rarely necessary, you should use the Var statement instead. In the prior examples, the starting value and the ending value are specified as numbers. You can also use variables, as shown in this example: .. code:: xojo Var startingValue, endingValue As Integer startingValue = 0 endingValue = 100 For counter As Integer = startingValue To endingValue ' [your code goes here] Next The first time through the loop, the counter variable will be set to *startingValue*. When the loop reaches the Next statement, the counter variable will be incremented by one. When the Next statement is reached and the counter variable is equal to *endingValue*, the counter will be incremented and the loop will end. Look back at the example mentioned earlier. You want to add the numbers one through ten to a List Box. The following code accomplishes that: .. code:: xojo For i As Integer = 1 To 10 ListBox1.AddRow(i.ToString) Next The counter variable (i in this case) is passed to the Str function to be converted to a string so that it can be passed to the AddRow method of ListBox1. .. note:: Note: The letter “i” is commonly used as the loop counter for historical reasons. In FORTRAN (an early programming language), the letters I through N are integers by default. Therefore, FORTRAN programmers began the practice of using those letters as counters, and in the order they appear in the alphabet. That is, if a FORTRAN programmer needed to nest one loop in another, they would use j as the counter for the inner loop. This convention made it easy for FORTRAN programmers to follow the logic of code that processed multi-dimensional arrays. By default, For loops increment the counter by one. You can specify another increment value using the Step statement. In this example, the Step statement is added to increment the counter variable by 5 instead of 1: .. code:: xojo For i As Integer = 5 To 100 Step 5 ListBox1.AddRow(i.ToString) Next All the above examples count from lowest to highest values. But you can also count in reverse order by using DownTo in place of To. For example, this counts from 10 to 1: .. code:: xojo For i As Integer = 10 DownTo 1 ListBox1.AddRow(i.ToString) Next You can also use Step when counting in reverse. In this example, the For loop starts the counter at 100 and decrements by 5: .. code:: xojo For i As Integer = 100 DownTo 1 Step 5 ListBox1.AddRow(i.ToString) Next .. _/getting_started/using_the_xojo_language/repeating_code/performance_considerations: Performance considerations ************************** So far, you have seen examples where *startingValue* and *endingValue* are integer numbers. If either *startingValue* or *endingValue* are expressions that must be evaluated to integers (such as a function call), the For loop will perform the evaluation each time it increments the counter — even if the expression always evaluates to the same integer. Therefore, for performance reasons it is advisable to perform any evaluations before entering the loop. For example, consider a loop that needs to process all the fonts that are installed on the user's computer. This number cannot be known in advance but there is a built-in function, :ref:`System.FontCount`, that you can use to obtain the total number of fonts. If you use it in the For statement to compute *endingValue* (like so): .. code:: xojo For i As Integer = 0 To System.FontCount - 1 . . Next The loop will run more slowly than if you calculate the value only once: .. code:: xojo Var numFonts As Integer = System.FontCount - 1 For i As Integer = 0 To numFonts . . Next However, the difference in speed may be of no practical value unless it is an incredibly lengthy loop. On a typical computer the difference between these two loops is only small fractions of a second — not enough to lose sleep over. .. _/getting_started/using_the_xojo_language/repeating_code/nested_loops: Nested loops ************ A For loop (as well as any other kind of loop) can have another loop inside it. In the case of a For loop, the only thing you will have to watch out for is making sure that the counter variables are different so that the loops won't confuse each other. The example below uses a For loop embedded inside another For loop to go through all the cells of a multi-column ListBox counting the number of cells in which the word “Hello” appears: .. code:: xojo Var count As Integer For row As Integer = 0 To ListBox1.LastRowIndex For column As Integer = 0 To ListBox1.ColumnCount - 1 If ListBox1.CellTextAt(row, column) = "hello" Then count = count + 1 End If Next Next MessageBox(count.ToString) One way to help keep this readable is to include the counter variable with the Next statement so you can more easily see which loop is which: .. code:: xojo Var count As Integer For row As Integer = 0 To ListBox1.LastRowIndex For column As Integer = 0 To ListBox1.ColumnCount - 1 If ListBox1.CellTextAt(row, column) = "hello" Then count = count + 1 End If Next Next MessageBox(count.ToString) .. _/getting_started/using_the_xojo_language/repeating_code/for_each...next: For Each...Next --------------- Another situation in which you want to loop through a group of values is array processing. Rather than looping through a set of statements for each value of a counter, the :doc:`For Each...Next` statement processes each element of an array that is passed to it. Take a look at an example to sum the values of an array using a counter variable: .. code:: xojo Var values() As Double values = Array(2.2, 1.1, 3.3, 4.4) Var sum As Double Var element As Double For i As Integer = 0 To values.LastIndex sum = sum + values(i) Next Here is the same example using For...Each: .. code:: xojo Var values() As Double values = Array(2.2, 1.1, 3.3, 4.4) Var sum As Double Var element As Double For Each element In values sum = sum + element Next In the For Each statement, instead of a counter variable, there is a variable that automatically gets the value of each element in the array. In the above example, the element variable gets the next value in the values array each time through the loop. When you are working with arrays, For Each allows you to write simpler code. Since the array doesn't necessarily have to be numbers, this statement enables you to process a group of objects of any type. They could be pictures, colors, documents, sets of database records, and so forth. As is the case for the For...Next loop, you can declare the data type of the element variable inside the For Each statement rather than in a separate Var statement. For example the previous example could be rewritten like this: .. code:: xojo Var values() As Double values = Array(2.2, 1.1, 3.3, 4.4) Var sum As Double For Each element As Double In values sum = sum + element Next .. _/getting_started/using_the_xojo_language/repeating_code/adding_loops_using_the_code_editor: Adding loops using the Code Editor ---------------------------------- The Code Editor's contextual menu offers an especially convenient way of adding loops to your code. There are several items in the contextual menu to wrap the selected lines of code inside a type of loop. To use these menu items, write the code that goes inside the loop, select the lines, and then choose the type of loop from the contextual menu. Your choices are Do/Loop, While/Wend and For/Next. The Code Editor can wrap the selected lines in the loop but it doesn't know what condition should terminate the loop. Therefore, it inserts the placeholder *text _condition_* in the loop statement. This condition is selected for you so all you need to do is start typing to replace it with the condition you want. .. _/getting_started/using_the_xojo_language/repeating_code/see_also: .. seealso:: :doc:`For...Next`, :doc:`For Each...Next`, :doc:`Do...Loop`, :doc:`While...Wend` commands ========== Properties ========== A property is a value of a class or module. You can think of it as a variable that belongs to a class or module. Most of the built-in classes have properties. For example, the :doc:`DateTime` class has a property to get the Year. For more about using properties with Modules, refer to the :doc:`Modules` topic. For more about properties when used with classes, refer to the :doc:`Class Properties, Methods and Events` topic. There are two types of properties you can add: a standard property and a computed property. .. _/getting_started/using_the_xojo_language/properties/property: Property -------- Properties can be added to project items, including classes (including Windows, Web Pages, iOS Views) and modules. There are several way to add a property: * Insert button on the main toolbar * Add button on the Code Editor toolbar * Insert > Property from the menu * The contextual menu * Keyboard shortcut (:kbd:`⌥ ⌘ P` on macOS or :kbd:`Ctrl Shift P` on Windows and Linux) .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/properties_teamname_property.png To quickly create a property, you can enter both its name and type on one line in the Name field like this: PropertyName As DataType. When you leave the field, the type will be set in the Type field. Any of these steps adds the property, displays a blank editor (where you can type notes) and displays the Inspector where you can set the values for the property. These are the values you can change: * **Name** The name of the property. Just like variables, properties are given names to describe them and the same naming rules apply. To create a property that is an array, add parenthesis after the name just as you would when creating an array variable. * **Type** The Type can be any valid data type, including classes. * **Default** The default value for the type. For example, for an Integer property you can specify 42 to have its default value be 42. You can only set default types for the intrinsic properties (Integer, Double, String, etc.) and not for object types. * **Scope** Scope indicates what parts of your code can access the property. Choices are Global, Public, Protected and Private. * Global properties (used with :doc:`Modules`) can be used anywhere in your code with no restrictions. * Public properties (used with :doc:`Classes`) can be called from anywhere in your code with no restrictions. * Protected properties when used in classes can be used only within the class itself or within a subclass. When used with modules, the property can be used only in conjunction with the module name. * Private properties can only be called by the module or class that contains the property. .. note:: Scope is indicated in the Navigator via the background color of the item. An item that is public will have no background color. An item that is Protected has a yellow background. An item that is Private will has a red background. The property signature (its name, and type) also appears at the top of the Editor for reference. Properties are referenced by their name when used within the module or class that owns them. This is how you would reference a TeamName property in your code: .. code:: xojo TeamName = "Flying Squirrels" For usage outside of the class or module that owns them, they are often referenced differently (usually with the class or module name as a prefix). Refer to the :doc:`Modules` and :doc:`Classes` topics for specifics. .. _/getting_started/using_the_xojo_language/properties/computed_property: Computed property ----------------- A computed property is a property that does not store its value but instead calculates it each time it is accessed. This means a computed property does not store an actual value. Instead it uses a standard property to store a value or contains code to calculate a value. There are several ways to add a computed property: * Insert button on the main toolbar * Add button on the Code Editor toolbar * Insert > Computed Property from the menu * The contextual menu Like with a property, you have to specify the values for the computed property, which are: * **Name** The name of the property. Just like variables, properties are given names to describe them and the same naming rules apply. * **Type** The Type can be any valid data type, including classes. * **Scope** Scope indicates what parts of your code can call the method. Choices are Global, Public, Protected and Private. * Global computed properties can be used anywhere in your code with no restrictions. These are available only with :doc:`Modules`. * Public computed properties can be called from anywhere in your code with no restrictions. These are available only with :doc:`Classes`. * Protected computed properties have some restrictions, which vary depending on where the method is located (:doc:`class` or :doc:`module`). * Private computed properties can only be called by the module or class that contains the method. The property signature (its name, and type) also appears at the top of the Editor for reference. A computed property has two additional parts to it: a Getter and a Setter. A computed property can be expanded in the Navigator to display the Get and Set sections below the property name. You add code to the Get section to get or calculate a value for the property. You add code to the Set section to set a value for the property. If you do not include code in Set then the property becomes a **read-only property**. You can also leave out code in the Get section to make the property write-only, but that is less common. In the Navigator, the Get and Set sections appear as bold when they have code. An example of a computed property might be a TotalPrice property of a Product class. You could implement TotalPrice with this code in Get: .. code:: xojo Return Price * Quantity If your computed property needs to retain a value, often you would create a matching private property to do so (usually prefixed with an “m”). For a computed property called "TeamName As String", this means you might do something like this: .. code:: xojo Get: Return mTeamName Set: mTeamName = Value The above computed property is referring to a companion property: .. code:: xojo mTeamName As String Code can access the computed property by its name: .. code:: xojo Var tName As String = TeamName For usage outside of the class or module that owns the computed property, they are often referenced differently (usually with the class or module name as a prefix). Refer to the Modules and Classes sections for specifics. Although the behavior is different, using a computed property in this manner is not much different than a regular property. There is one notable benefit: you can set a breakpoint in the Set section so you can track when the property value changes. Computed properties appear in the debugger, but it is important to note that the debugger will call the "Get" section of code in order to get the value to display. This may have unintended side-effects depending on how your code works. .. note:: Computed properties cannot be array types. .. _/getting_started/using_the_xojo_language/properties/see_also: .. seealso:: :ref:`Module Properties`, :doc:`Class Properties` topic ======= Methods ======= .. rst-class:: forsearch Method Now that you understand the various coding concepts, it is time to learn more about the place where you write you code: methods. Methods are the building blocks of your application. The code you write most often exists in a method. A method is one or more instructions that are performed to accomplish a specific task; an action of some sort. Xojo has many built-in methods. Some are global such as the :doc:`Quit` method that causes your application to quit. Most framework classes have built-in methods. For example, the :doc:`DesktopListBox` class has a method called AddRow for adding rows to it (as the name implies). And you can of course create your own methods. .. _/getting_started/using_the_xojo_language/methods/creating_your_own_methods: Creating your own methods ------------------------- .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/methods_calculateamount_method.png Methods can be added to project items, including classes (including :doc:`Desktop Windows`, :doc:`Web Pages`, :doc:`Mobile Screens`) and modules. To add a method, choose Insert -> Method from the menu or toolbar when the appropriate project item is selected. This adds the method, displays a blank code editor and displays the Inspector where you can set the properties for the method: * **Method Name** The name of the method. Just like variables, methods are given names to describe them and the same naming rules apply. * **Parameters** Parameters are values that you pass to the method that it can then use within the method as if they were variables. Parameters are separated by commas and are declared similarly to how you use the Var statement: .. code:: xojo value As Integer value As Integer, name As String * **Return Type** Methods that do not specify a return type are called Subroutines (or Procedures in some other languages). Methods that specify a return type are called Functions. The Return Type can be any valid data type, including classes. .. _/getting_started/using_the_xojo_language/methods/creating_your_own_methods/scope: * **Scope** Scope indicates what parts of your code can call the method. Choices are Public, Protected and Private. * Global methods (used with :doc:`Modules`) can be called from anywhere in your code with no restrictions. * Public methods (used with :doc:`Classes`) can be called from anywhere in your code with no restrictions. * Protected methods when used in classes can be used only within the class itself or within a subclass. When used with modules, the method can be used only in conjunction with the module name. * Private methods can only be called by the module or class that contains the method. .. note:: Scope is indicated in the Navigator via the background color of the item. An item that is public will have no background color. An item that is Protected has a yellow background. An item that is Private will has a red background. The method signature (its name, parameters and return type) also appears at the top of the :doc:`Code Editor` for reference. .. _/getting_started/using_the_xojo_language/methods/using_methods: Using methods ------------- To use a method you specify its name, optionally prefixed by the :doc:`module` or :doc:`class instance name` (depending on how the method is defined). An example of a method you have used before is the ToString method on the number data types: .. code:: xojo Var i As Integer = 42 Var s As String = i.ToString .. _/getting_started/using_the_xojo_language/methods/passing_parameters_to_methods: Passing parameters to methods ----------------------------- Some methods are called simply with their name. But some methods require additional information or values. This information that is passed to a method is called a parameter. A method can have any number of parameters. If you have a :ref:`Dictionary` containing a value for key "Name", then this method call removes the key and value from the Dictionary: .. code:: xojo myDictionary.Remove("Name") The value "Name" was passed as a parameter to the Remove method. Methods define their parameters and specify the types for them. A method can have any number of parameters. When passing multiple parameters, separate each one with a comma. The Lookup method on a Dictionary takes two parameters and can be used to get a value for a key and if not available, provide a default: .. code:: xojo Var value As String = myDictionary.Lookup("Name", "Unknown") In these examples, the parameters have been passed as text literals, but you can also pass variables and constants as parameters instead: .. code:: xojo Var key As String = "Name" Var default As String = "Unknown" Var value As String = myDictionary.Lookup(key, default) As with variable assignment, the parameters you pass to the method must match the types of the parameters as declared on the method. .. _/getting_started/using_the_xojo_language/methods/passing_arrays_as_parameters: Passing arrays as parameters **************************** An array can be passed as a parameter in a call to a method or function. You can pass both one and multi-dimensional arrays. To specify that a parameter is a one-dimensional array, put empty parentheses after its name in the declaration. For example: .. code:: xojo names() As String can be used in the declaration when you want to pass an array of strings to the method. Since you do not need to specify the number of elements in the array to be passed, you can pass a different number of elements at different places in your code. When you pass an array to the method or function, omit the parentheses in the array. For example: .. code:: xojo PrintLabels(allNames) PrintLabels is the name of the method that accepts the text array as its parameter. You can pass multi-dimensional arrays without specifying the number of elements in each dimension, but you need to indicate the number of dimensions. Do this by placing one fewer commas in the parentheses than dimensions. For example, if names were a two-dimensional String array, you would declare the array as a parameter in the following manner: .. code:: xojo names(,) As String When you pass a multi-dimensional array to a method or function, you can include the parentheses but not any commas: .. code:: xojo PrintLabels(allNames()) .. _/getting_started/using_the_xojo_language/methods/returning_values_from_methods: Returning values from methods ----------------------------- Some methods return values. These methods are also called **Functions**. When a method returns a value, the value is passed back from the method to the line of code that called the method. For example, the method :ref:`System.Ticks` returns the number of ticks (a tick is 1/60th of a second) that have passed since you turned on your computer. You can assign the value returned by a method the same way you assign a value to a variable. In the example below, the value returned by Ticks is assigned to the variable elapsed: .. code:: xojo Var elapsed As Double elapsed = System.Ticks Some methods require parameters and return a value. You saw this earlier with the Lookup method for a Dictionary: .. code:: xojo Var value As String = myDictionary.Lookup("Name", "Unknown") The above method takes two parameters and returns a value. You can also directly pass methods that return values to other methods. This code finds "are" in the source String: .. code:: xojo Var source As String = "Hello, how are you?" Var search As String = "You are" Var position As Integer position = source.IndexOf(search.Right(3)) For a method to return a value, you just have to specify a Return Type in the method properties of the Inspector. For example, set the Return Type to "Integer" to return an Integer. A method can return any type, from the intrinsic data types to any class or other type you have created yourself. Methods can also return arrays of any type. To indicate that the return type is an array, add the double parenthesis to it. To return an Integer array set the Return Type to "Integer()". .. _/getting_started/using_the_xojo_language/methods/passing_parameters_by_value_and_by_reference: Passing parameters by value and by reference -------------------------------------------- By default, you pass values to a method by value. When you do so, the method receives a copy of the data that you pass allowing the method to modify it without affecting the original value. .. _/getting_started/using_the_xojo_language/methods/passing_by_value: Passing by value **************** .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/methods_squareit_method_with_parameter.png Parameters passed by value are essentially treated as local variables inside the method — just like variables that are created using the Var statement. This means that you can modify the values of the parameters themselves rather than first assigning the parameter to a local variable. For example, if you pass a value to the Integer parameter “x”, you can increment or decrement the value of x rather then assigning the value of x to a local variable that is created using Var. This example modifies the parameter and returns the result: .. code:: xojo Function SquareIt(num As Integer) As Integer num = num * num Return num End Function The original value passed to the method is not changed, as shown in this code: .. code:: xojo Var value As Integer = 10 Var result As Integer result = SquareIt(value) ' value remains 10 ' result is 100 .. _/getting_started/using_the_xojo_language/methods/passing_by_reference: Passing by reference ******************** .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/methods_squareit_method_with_byref_parameter.png When you write your own methods, you have the option of passing information by reference by using the :doc:`ByRef` statement. The practical advantage of this technique is that the method can change the values of each parameter and replace the values of the parameters with the changed values. When you pass parameters by value, you can't do this because the parameter only represents a copy of the data itself. When you pass information by reference, you actually pass the object itself rather than a copy of it. .. note:: There are basically two kinds of variables: value types, which hold the value directly (e.g., an :doc:`Integer` or :doc:`Double`), and reference types, which contain too much data to store in a single memory location. Instead, a reference is a pointer to a larger block of data — such as an array or a class instance. Arrays and objects are reference types. So when you don't use :doc:`ByRef`, what gets passed for a value type is a copy of the value. For a reference type, a copy of the reference is passed. Since it's still a reference, you can access and modify the original array or object through it. Now, let's add :doc:`ByRef` into the picture: With a value type, :doc:`ByRef` passes a reference to the original value — so you can modify the original directly. With a reference type, it's essentially the same — you get a reference to the reference, allowing you to change what the original reference points to. Here the SquareIt example from above written to use :doc:`ByRef`: .. code:: xojo Sub SquareIt(ByRef num As Integer) num = num * num End Sub Since this modifies the parameter value and does not return a value, it is called like this: .. code:: xojo Var value As Integer = 10 SquareIt(value) ' value is now 100 .. _/getting_started/using_the_xojo_language/methods/optional_parameters_and_default_values: Optional parameters and default values -------------------------------------- There are two ways to make a parameter optional, both of which you do at the time you write the method declaration: * Assign a value to it in the declaration * Use the :doc:`Optional` keyword in the declaration The value you assign can be a literal value or a standard constant (not a :doc:`localized string`). The best way to make the parameter's explicit status clear is to use the :doc:`Optional` keyword. This modifier precedes the parameter name and may be used with or without a default value. When the caller omits passing this parameter, the parameter receives the assigned default value or the default value for its data type if no default value was assigned in the method declaration. For example: .. code:: xojo MyMethod(a As Integer, b As Integer, Optional c As Integer) The parameter c is optional. If it is omitted in the method call, then it gets the default value of Integer, which is 0. If you want a different default value, you assign it to the parameter: .. code:: xojo MyMethod(a As Integer, b As Integer, Optional c As Integer = 5) Now if the parameter c is omitted in the method call, then it gets the value 5. Lastly, you can omit the Optional keyword from the declaration if you specify a default value: .. code:: xojo MyMethod(a As Integer, b As Integer, c As Integer = 5) With any of the the above method declarations, you can call the method with only 2 of the 3 parameters: .. code:: xojo MyMethod(3, 4) .. _/getting_started/using_the_xojo_language/methods/identifying_the_currently_executing_method_name: Identifying the currently executing method name ----------------------------------------------- For logging purposes, it can often be useful to know the name of the currently running method. Use the :doc:`CurrentMethodName` method in your code to get the name of the currently running method: .. code:: xojo MessageBox(CurrentMethodName) .. _/getting_started/using_the_xojo_language/methods/setters: Setters ------- A value passed to a method is normally supplied in parenthesis following the method name, such as: .. code:: xojo MyMethod(10) Sometimes it is preferred to have the method call behave differently so that you assign the parameter like this: .. code:: xojo MyMethod = 10 This is called a Setter because it looks like you are setting the MyMethod value to 10 rather than passing 10 as a parameter to MyMethod. You can enable this alternative syntax by using the :doc:`Assigns` keyword in the parameter declaration: .. code:: xojo Sub MyMethod(Assigns value As Integer) Now you call MyMethod like this: .. code:: xojo MyMethod = 10 You can have more than one parameter with this technique but only the last parameter can have the Assigns keyword. The other parameters are passed as before. For example: .. code:: xojo Sub MyMethod(desc As String, Assigns value As Integer) To call this method: .. code:: xojo MyMethod("test") = 10 .. _/getting_started/using_the_xojo_language/methods/events: Events ------ Events are a special type of method commonly used with control classes. But they can also be implemented in your own non-control classes as well. Events can be called only by the class that declares the event. Generally speaking, events are used to provide a way for subclasses to provide additional functionality. You don't need to worry about Events right now. Both :doc:`subclasses` and :doc:`events` are described in more detail in other topic pages. =================== Collections of data =================== Programs often have a lot of data to manage. Two common techniques for managing data include arrays and dictionaries. Although they both store large collections of information, they do so in different ways. .. _/getting_started/using_the_xojo_language/collections_of_data/arrays: Arrays ------ You declare an array by specifying the index of the last element of the array. The index that you specify in the Var statement is actually one less than the number of elements in the array because arrays always have an element at position zero (0). Such an array is sometimes referred to as a zero-based array. Since arrays are zero-based, this statement creates a String array with eleven (11) elements: .. code:: xojo Var names(10) As String This statement creates an array with one element, element zero: .. code:: xojo Var names(0) As String In other words, you declare a variable as an array by adding the index of the last element to the Var statement. The index of the last element must be either a number or a constant. You cannot use variables with this syntax. .. code:: xojo Const kSize As Integer = 10 Var names(kSize) As String If you don't know the size of the array you need at the time you declare it, you can declare it as a null array, i.e., an array with no elements. You do this by using an index of -1 in the Var statement or leave empty parentheses. This means “an array of no elements.” These two examples create an array with no initial elements: .. code:: xojo Var firstName(-1) As String Var lastName() As String Since the array does not have any elements, you'll have to actually add elements to it. You can do this by resizing it using the ResizeTo method or by adding elements to it using the Array method (at assignment) or by using the Add or AddAt methods. This uses the Array method to populate an array, setting its size to the number of elements specified: .. code:: xojo Var values() As Integer = Array(5, 8, 42, 56, 32) This code uses the Add method to add items to an empty array. This technique is useful when the data to go in the array comes from another source that may vary in size, such as a file or a database: .. code:: xojo Var names() As String names.Add("Bob") names.Add("Phil") names.Add("Larry") ' The array now has a size of 2, with indexes 0, 1 and 2. .. _/getting_started/using_the_xojo_language/collections_of_data/multidimensional_arrays: Multidimensional arrays *********************** You can create multi-dimensional arrays. For example, a spreadsheet layout can be thought of as a two-dimensional array, rows by columns. Each dimension is referred to by its own index. For example, the elements of an array with two dimensions are referred to by one index for the rows and the other for the columns. The first element in the upper-left corner is element 0,0. You create a multi-dimensional array by specifying an index for each dimension. For example, this statement creates a two-dimensional array with 3 rows and 11 columns: .. code:: xojo Var names(2, 10) As String You can use constants in Var statements to set the size of an array: .. code:: xojo Const kLanguages As Integer = 100 Var names(10, kLanguages) As String .. _/getting_started/using_the_xojo_language/collections_of_data/referring_to_array_elements: Referring to array elements *************************** You refer to an element of an array by placing the desired element in parentheses. This example places the text “Frank” in array element (1,1): .. code:: xojo names(1, 1) = "Frank" .. _/getting_started/using_the_xojo_language/collections_of_data/getting_the_index_of_the_last_element: Getting the index of the last element ************************************* The LastIndex method returns the index of the last element of a one-dimensional array. For example: .. code:: xojo names.LastIndex The number of elements is one greater than this number, since the array has an element zero. For example, the following code returns 5 in the variable i: .. code:: xojo Var i As Integer Var names(5) As String i = names.LastIndex If you need the number of elements, use the Count function. .. code:: xojo names.Count .. _/getting_started/using_the_xojo_language/collections_of_data/initializing_arrays: Initializing arrays ******************* After you have declared an array, you can assign initial values to the elements with the Array function as well as individual assignment statements, such as shown above. The Array function takes a list of values and assigns the values to the elements of the array, beginning with element zero. In other words, it provides the same functionality as separate assignment statements for each element of the array. For example, the following statements initialize the array using separate assignment statements for each array element: .. code:: xojo Var names(2) As String ' Separate assignment statements names(0) = "Fred" names(1) = "Ginger" names(2) = "Stanley" The following statements use the Array function to accomplish the same thing. Note that you don't have to declare the exact size of the array in the Var statement. .. code:: xojo Var names() As String names = Array("Fred", "Ginger", "Stanley") The Array function will add elements to the array as needed. If you declare the array as a fixed size but don't specify as many values as elements, the Array function will start with element zero and use as many elements as are specified. .. _/getting_started/using_the_xojo_language/collections_of_data/array_assignment: Array assignment **************** If you have two arrays of compatible data types, you can assign one array to the other array. Use the assignment statement without the parentheses. Here is a simple example: .. code:: xojo Var names(2), copyNames(2) As String names = Array("Fred", "Ginger", "Stanley") copyNames = names The last statement assigns the values of all three elements of names to the first three elements of copyNames. If copyNames had fewer elements than names, then additional elements would first be added to copyNames and the assignment of all the elements of names to copyNames would be completed. For example, the following is valid: .. code:: xojo Var names(3), copyNames(2) As String names(0) = "Fred" names(1) = "Ginger" names(2) = "Tommy" names(3) = "Woody" copyNames = names After the code runs, the copyNames array has a fourth element for storing the value “Woody”. It is important to understand that when you copy an array like this, both variables are essentially pointing to the same array contents. Changing an element in copyNames would also change the same element in the original names array. If you need to actually make a copy of the individual elements to a new array, then you will have to loop through and copy each element one by one: .. code:: xojo Var names() As String = Array("Fred", "Ginger", "Tommy", "Woody") Var copyNames() As String For i As Integer = 0 To names.LastIndex copyNames.Add(names(i)) Next .. _/getting_started/using_the_xojo_language/collections_of_data/resizing_arrays: Resizing arrays *************** There are several ways to resize arrays after they have been created. **Add**: The Add method adds an element to a one-dimensional array, increasing its size by one. You pass the value you want to add to the array when you call Add. For example, the following statement adds an element to the array names and sets the value of this element to the string, “Dave”: .. code:: xojo names.Add("Dave") **AddAt**: The AddAt method creates an additional element in a one-dimensional array and adds it in the place you specify. It takes two parameters, the index position of the element to add and the value of the new element. For example: .. code:: xojo names.AddAt(9, "Hal") After this statement runs, the value of names(9) would be “Hal”. The old element(9) would be shifted up to element(10) and so forth. The size of the array would be increased by one, as with Add. **RemoveAt**: The RemoveAt method removes the element whose index you specify. This example removes the element with index 9, decreasing the size of the array by one and shifting the array elements after the removed element down by one: .. code:: xojo names.RemoveAt(9) **ResizeTo**: The ResizeTo method resizes an existing array. You pass the new values of the array's indexes but you don't specify the data type, which is set by the initial Var statement. This example resizes the array names to 101 elements: .. code:: xojo names.ResizeTo(100) ResizeTo works on both one- and multi-dimensional arrays. For multi-dimensional arrays, you can only resize the existing dimensions; you cannot add or reduce the number of dimensions themselves. A difference between Var and ResizeTo is that Var accepts only integers or constants, while ResizeTo accepts any expression that returns an Integer. This includes, for example, a user-written function that returns an Integer value or a simple variable. Using this feature, you can dimension your arrays on-the-fly. If you don't know the size of the array you need at the time you declare it, you can declare it as a null array, i.e., an array with no elements, and use the ResizeTo command to resize it later. If your program needs to load a list of names that the user enters, you can wait to size the array until you know how many names the user has entered. You can write a function to figure out what that number is and use it with ResizeTo or use the Add methods to add the required elements to the array one-by-one. If you need to quickly remove all elements of an array, use the RemoveAll method. .. code:: xojo names.RemoveAll .. _/getting_started/using_the_xojo_language/collections_of_data/converting_to_and_from_an_array_to_variables: Converting to and from an array to variables ******************************************** Two functions enable you to take a take an array and break it up into separate variables and take a single string variable and convert it into an array. **ToArray**: This function takes a String variable and creates a one-dimensional array by dividing the string up into elements separated by a delimiter. The delimiter is a character or series of character that signals the end of one element and the start of the next element. By default, the delimiter is a space, but you can specify another delimiter. Here is an example that divides up the contents of a string into array elements. It specifies the comma as the delimiter. After this call, the resulting array, names, has three elements: .. code:: xojo Var names() As String ' Convert using the ToArray method Var s As String s = "Juliet,Taylor,Casting" names = s.ToArray(",") ' names(0) = "Juliet" ' names(1) = "Taylor" ' names(2) = "Casting" **FromArray**: This method does the opposite of ToArray. It takes a one-dimensional array and returns a string containing all the elements separated by the specified delimiter. This example takes an array and returns its contents as a single text variable: .. code:: xojo Var names(2) As String = Array("Bob", "Phil", "Larry") Var s As String s = String.FromArray(names, ",") ' t = "Bob,Phil,Larry" .. _/getting_started/using_the_xojo_language/collections_of_data/sorting_arrays: Sorting arrays ************** You may need to sort the contents of an array, which you can easily do using the Sort method: .. code:: xojo Var names(2) As String = Array("Betty", "Phil", "Linda") names.Sort ' Names now contains: Betty, Linda, Phil For more complicated sorting needs, you can use the SortWith method to sort an array and keep the contents of two or more other arrays in sync with the now sorted first array. This is useful when you have multiple arrays of related information and want to sort one of the arrays, but also ensure the data in the other arrays are still correctly related to the newly sorted array. As an example, you have one array called names and another called zips defined like this: .. code:: xojo Var names() As String = Array("Mozart", "Bing", "Jackson", "Flintstone") Var zips() As String = Array("04101", "04240", "04123", "04092") Now if you were to sort just names, the index for "Bing" will change from 1 to 0. But the zips array has not changed, so its corresponding zip will no longer be correct because zip(1) is "04240", but zip(0) is "04101". What you want to do is to also re-arrange zips so that its contents continue to match the newly sorted names array. You do this by using the SortWith method to sort the names array like this: .. code:: xojo Var names() As String = Array("Mozart", "Bing", "Jackson", "Flintstone") Var zips() As String = Array("04101", "04240", "04123", "04092") names.SortWith(zips) ' names() = "Bing", "Flintstone", "Jackson", "Mozart" ' zips = "04240", "04092", "04123", "04101" As you can see, the zips array has its contents adjusted so that "04240" remains related to the new position for "Bing". Lastly, you can also implement your own sorting method to sort an array of classes. This is useful when your array consists of Classes, rather than simple data types such as String or Integer. .. _/getting_started/using_the_xojo_language/collections_of_data/dictionary: Dictionary ---------- A :doc:`Dictionary` is an object that is made up of a list of key-value pairs. That is, each value is paired with an identifying key. The interesting feature of Dictionaries is that both the key and the value are a :doc:`Variant` so they can contain a value of any data type. This means that a dictionary can store a mixture of data types — and that the key doesn't have to be an integer. You use the key to look up a value in a Dictionary or you can loop through the values. A Dictionary is a great way to have fast lookups of information and can be considerable faster than a large array. Although technically a class (and classes are not described until later in the Documentation), Dictionary is so commonly used that it is worth covering here as a data type. As a class, you have to create an instance with the :doc:`New` command before you can use a Dictionary: .. code:: xojo Var d As New Dictionary You add values to the Dictionary using the Value method to specify a key name and assign it the value: .. code:: xojo d.Value("FirstName") = "Julie" You can look up a value using the key: .. code:: xojo Var name As String name = d.Value("FirstName") Once you have a Dictionary populated with values, you can iterate through them using a :doc:`For Each...Next` loop. This loops through the Dictionary to get the keys and then uses the key to lookup the value: .. code:: xojo Var pets As New Dictionary pets.Value("Cat") = "Shawmut" pets.Value("Dog") = "Seltzer" pets.Value("Gerbil") = "Topher" For Each key As Variant In pets.Keys Var value As String = pets.Value(key) TextArea1.Text = TextArea1.Text + key + " is " + value + ", " Next .. _/getting_started/using_the_xojo_language/collections_of_data/pair: Pair ---- The :doc:`Pair` class is similar to the Dictionary. It has two properties: Left and Right. Thus, each Pair instance consists of a key-value pair. As it the case with the Dictionary, the values in the pair are variants. You use the “:” operator to assign the Left and Right values when the Pair is declared. For example: .. code:: xojo Var p As Pair = "Telephone Number" : "(406) 737-8946" This assigns “Telephone Number” to the Left property of the pair and the value of the number to the Right property, which you can access like this: .. code:: xojo Var kind As String = p.Left Var phone As String = p.Right It also can store a linked list of pairs when it is passed a list of items, such as: .. code:: xojo Var p As Pair = 1 : 2 : 3 : 4 : 5 The first pair consists of the pair “1” (Left property) and the second pair object (Right property); the next pair consists of the “2” and the third pair, and so forth. Pair is not available for iOS projects. .. _/getting_started/using_the_xojo_language/collections_of_data/see_also: .. seealso:: :doc:`Dictionary`, :doc:`Pair` classes; :doc:`Arrays` topic; :doc:`Array`, :doc:`Var`, :ref:`Arrays.ResizeTo` commands ============ Enumerations ============ An enum or :doc:`enumeration` is a data type consisting of a set of named values, which are called elements. .. _/getting_started/using_the_xojo_language/enumerations/creating_your_own_enumerations: Creating your own enumerations ------------------------------ Enumerations can be added to project items such as classes (including Windows, Web Pages, iOS Views) and modules. To add an enumeration, choose Insert > Enumeration from the menu or toolbar when the appropriate project item is selected. This adds the Enumeration, displays a blank :doc:`Enumeration Editor` and displays the Inspector where you can set the properties for the enumeration. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/enumerations_enumerations_editor.png * **Name** The name of the enumeration. * **Type** Enumerations are always an Integer type and default to “Integer”. You can change the type to other Integer types (such as UInt64) should you need to use larger values for the enumeration elements. * **Scope** Scope indicates what parts of your code can access the enumeration. Choices are Public, Protected and Private. * Public enums can be called from anywhere in your code with no restrictions. * Protected when used in classes can be used only within the class itself or within a subclass. When used with modules, the enumeration can be used only in conjunction with the module name. * Private enums can only be called by the module or class that contains the enum. In the Enumeration Editor, you use the “+” icon to add a new enumeration element to the set. Use the “-” button to remove an element from the set. When you add an element, you also give it a name. The name is used to refer to the value. You can edit the name by clicking on it once to select it and a second time to edit it. .. note:: Scope is indicated in the Navigator via the background color of the item. An item that is public will have no background color. An item that is Protected has a yellow background. An item that is Private will has a red background. The enumeration name cannot be left blank. Although enumeration elements are integers behind the scenes, you don't normally worry about the integer values. However, should you need to assign a specific integer value to an enumeration item, you can do so by assigning it as part of the name like this: .. code:: xojo EnumElement = 10 To use an enumeration in code, you refer to the container of the enumeration, the enumeration name and then the enumeration element name. When an enumeration is in a class, refer to it using the class name and not the name of the instance variable or property. This code assigns an element to an enumeration: .. code:: xojo Var pictureSize As Module1.Sizes ' Sizes is an enumeration pictureSize = Module1.Sizes.Large ' Large is an element on Size If you find you need the actual Integer value stored by the enumeration, then you have to convert (cast) it to an Integer first: .. code:: xojo Var pictureSize As Integer pictureSize = Integer(Module1.Sizes.Large) .. _/getting_started/using_the_xojo_language/enumerations/binary_enumerations: Binary enumerations ------------------- You can create enumerations that support binary operations by enabling Binary in the Inspector. See the :doc:`Enumerations data type` for a list of supported operations. The binary values will be assigned automatically when you create the enumeration members. Binary enumerations are only available in modules. They must be of type Integer. .. _/getting_started/using_the_xojo_language/enumerations/see_also: .. seealso:: :doc:`Enumeration` data type ======= Modules ======= A module is a collection of project items, usually methods, properties and constants. But a module can also contain other project items such as classes or even other modules. In fact, modules can pretty much contain anything except layouts such as Windows, Web Pages, Views and Container Controls. .. _/getting_started/using_the_xojo_language/modules/when_to_use_a_module: When to use a module -------------------- In general, modules should be used sparingly in an object-oriented language such as Xojo. In most cases, you will probably be better served by a class. With that said, here are some common (and valid) uses for modules: * Global constants, particularly for localization * Grouping project items into namespaces * Class extensions * Global properties, but these should be minimal to avoid tightly-coupled "spaghetti code" * Methods Modules are covered here first since they are easier to use and learn and transition nicely to classes (which are covered in other topics). In Web projects, module properties and methods are global across all sessions. If you want information to remain specific to a session, use the :doc:`Session` object. .. _/getting_started/using_the_xojo_language/modules/adding_a_module_to_your_project: Adding a module to your project ------------------------------- Add a new module to your project by clicking the Insert button on the toolbar and selecting Module (or by using the menu Insert > Module or the contextual menu). The new module appears in the Navigator with a default name (the first module you add will be named Module1, for example). Use the Inspector to rename the module to something more appropriate. For example, if the module contains financial functions, you might name it Financial. You edit modules with the Code Editor: click the module's name in the Navigator. Modules are identified by a "globe" icon in the Navigator. Modules primarily contain properties, methods and constants so those are covered first. .. _/getting_started/using_the_xojo_language/modules/adding_properties: Adding properties ***************** :doc:`Properties` are variables that belong to the entire module rather than a single method. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/modules_module_property.png There are several way to add a property to a module: * Insert button on the main toolbar * Add button on the Code Editor toolbar * Insert > Property from the menu * The contextual menu * Keyboard shortcut (:kbd:`⌥ ⌘ P` on macOS or :kbd:`Ctrl Shift P` on Windows and Linux) You can set the property Name, Type, Default value and Scope using the fields in the Inspector. To quickly create a property, you can enter both its name and type on one line in the Name field like this: PropertyName As DataType. When you leave the field (or press return), the type will be set in the Type field. You can also add computed properties to modules. .. note:: When a local variable is shadowing a module property, assignment to the module property is not supported. .. _/getting_started/using_the_xojo_language/modules/adding_methods: Adding methods ************** Methods are discussed in the :doc:`Methods` topic. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/modules_method_inspector.png There are several ways to add a method to a module: * Insert button on the main toolbar * Add button on the Code Editor toolbar * Insert > Method from the menu * The contextual menu * Keyboard shortcut (:kbd:`⌥ ⌘ M` on macOS or :kbd:`Ctrl Shift M` on Windows and Linux) You can set the Method Name, Parameters, Return Type and Scope using the Inspector. The method code is typed in the :doc:`Code Editor`. .. _/getting_started/using_the_xojo_language/modules/adding_constants: Adding constants **************** Constants added to a module work like constants added to a method except they are accessible anywhere in the module and possibly also available to code not in the module as well (depending on the scope). .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/modules_module_constant.png There are several ways to add a constant to a module, which all display the :doc:`Constant Editor`: * Insert button on the main toolbar * Add button on the Code Editor toolbar * Insert > Method from the menu * The contextual menu * Keyboard shortcut :kbd:`⌥ ⌘ C` on macOS or :kbd:`Ctrl Shift C` on Windows and Linux) You can set the Constant Name, Default Value, Type (Number, String, Boolean, Color, Text) and Scope using the Inspector. For the Default Value, Constants can be assigned only a literal value, another constant value or the result of a constant expression. Constants of type String or Text can also be set to Dynamic. A dynamic constant can have values that are specific to an OS or language and is referred to as a Localized String. For more about this, refer to the :doc:`Localization` topic. .. _/getting_started/using_the_xojo_language/modules/adding_other_items: Adding other items ****************** You can also add other items to a module, including: Enumerations, External Method, Note, Structure, Using Clause. * Enumeration An Enumeration is a data type consisting of a set of named values. * External Method Add a reference to an external method (also called a Declare), which can be a method on a DLL (on Windows), a dylib (on macOS) or a shared library (on Linux). * Note A Note is essentially a text field that is added to a project item. Use a Note to add comments, description or any other text to the module that is not part of the code. Think of a Note as a giant comment. * Structure Adds a Structure, which is an advanced data type typically used with Declares. * Using Clause Adds a Using statement that is applied to the entire module. For more about Using, see Namespaces below. .. _/getting_started/using_the_xojo_language/modules/scope_of_items: Scope of items -------------- When you add an item to a module, you need to set its Scope using the Scope property in the Inspector. The Scope is also indicated in the Navigator. The Scope of an item determines its accessibility to other items in the project. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/modules_module_scope.png These are the choices: * **Global** A Global item can be called from code anywhere in the project. For example, you can use a global property to store a piece of information that needs to be available from several different Windows (or Web Pages or Views). To reference a global item in your code, just use its name. Global can only be used for items added to a top-level module. If you add a global property called MyGlobalProperty As String, then you can refer to it any code just by its name: .. code:: xojo Var value As String = MyGlobalProperty **Be careful of extensive use of global values. This can lead to error-prone code that is hard to debug.** It would be better to at least use "Protected" module properties as shown below to make it easier to know the owner of the property and to allow auto-complete to be more useful. * **Public** The Public scope can only be used for project items (such as classes) that are added to the module. These items are accessible by including the module name as the prefix (similar to Protected). For example if you have added Class1 to Module1 with a scope of Public you can access is like this: .. code:: xojo Var c As New Module1.Class1 * **Protected** A Protected item (such as a property or method) can also be called from code anywhere in the project. To reference a Protected item in a module, you use "dot" notation and prefix it with the name of the module. For example, if in Module1 you declare a Protected property called MyProperty As String, then you call it like this in code outside of Module1: .. code:: xojo Var value As String = Module1.MyProperty For code within Module1 you can just refer to it using MyProperty. * **Private** A Private item is available only within the module. It is inaccessible outside the module. To reference the Private item inside the module in which it was created, you use its name: .. code:: xojo Var value As String = MyPrivateProperty .. _/getting_started/using_the_xojo_language/modules/extension_methods: Extension methods ----------------- An extension method is a special method definition that can be called using syntax that indicates that it belongs to another object. For example, you can add a method that is called from any FolderItem object to save something in a particular format. After you add the class extension method to a module, you can call it as if it were built into the FolderItem class. Using these methods does require an understanding of Classes, which are covered in the Object-Oriented Programming topics. This feature is useful when you would like to add a method to a class for which you do not have access to the source code. In particular, it allows you to add functionality to built-in framework classes. .. youtube:: TEDlEZisZZg To define a method as a class extension method, use the :doc:`Extends` keyword prior to the first parameter. The data type of the first parameter is the object type from which the method must be called. In other words, the use of the Extends keyword indicates that the parameter is to be used on the left side of the dot (".") operator in a calling statement. When you use the Extends keyword, you do not have to pass an object of that type to the method. You can add "normal" parameters to the parameter declaration that follow the Extends parameter. The Extends keyword can be used only with Global methods that reside in modules. .. _/getting_started/using_the_xojo_language/modules/creating_and_using_an_extension_method: Creating and Using an extension method ************************************** As an example, say you want to add a Clear method that can be called from a :doc:`DesktopTextArea` control to clear all its text. If you don't already have a module in your project, add one. In the module, add a method and name it Clear. Its parameter is: .. code:: xojo Extends ta As DesktopTextArea And its code is: .. code:: xojo ta.Text = "" You can now call this method on any :doc:`DesktopTextArea` control in the project. For example, a button could have this code to clear TextArea1 on the Window: .. code:: xojo TextArea1.Clear Extension methods for for all classes, including the ones included with Xojo and any that you create. .. _/getting_started/using_the_xojo_language/modules/namespaces: Namespaces ---------- Modules can also contain other project items, including classes, class interfaces and even other modules. Modules cannot contain Windows, Web Pages, Mobile Screens or Containers. When you add other project items to a module, you have created what is called a **Namespace**. A Namespace is a collection of (typically related) items. Some parts of the framework are included in a namespace, such as Introspection. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/modules_module_classes.png .. _/getting_started/using_the_xojo_language/modules/the_using_statement: The Using statement ------------------- You can use the Using statement to avoid having to write the full namespace name each time you want to access an item within it. For example, you can write code like this to avoid having to use full dot notation to access classes within a module called Company in this case: .. code:: xojo Using Company Var e As New Employee ' instead of Company.Employee e.Name = "Tom Smith" Alternatively, you can add a Using Clause to a module to apply a namespace to all the methods of the module. To do so, choose Insert > Using Clause and enter the namespace name in the Inspector field. ========================== Advanced language features ========================== These language features are not commonly used on most projects and are for advanced users. .. _/getting_started/using_the_xojo_language/advanced_language_features/external_methods: External methods ---------------- An External Method adds a reference to an external method which can be a method on a DLL (on Windows), a dylib (on macOS) or a shared library (on Linux). You can also use the Declare command to create an external method. External Methods are not supported on iOS. Use the :doc:`Declare` command instead. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/advanced_language_features_external_method_ptr.png :scale: 50 % External methods are added using Insert > External Method for a class or module. In the Inspector for the External Method, you can provide the necessary information, including: * **Method Name** The name of the method. This is the name you will use to call the method. * **Parameters** The parameters required by the method. * **Return Type** The return type of if the method is a function. * **Scope** The scope of the method matches the scoping rules for items in a module or class. * **Lib** The library containing the method. * **Alias** This is optional and refers to the actual method name in the library. You only need to provide it if you are using a different Method Name than what the method is called in the library. * **Soft** Select Soft to only check if the method exists at runtime, rather than when the app launches. * **Objective-C** Select Objective-C to add an external method to an Objective-C method. This changes the Alias field to say "Selector" where you can specify the selector to call the method. There are two attributes that can be used with external methods: * Platform - Indicates which platform the method can be used on. Valid values are: mac, Win and Linux. * Handlenames - A comma-delimited list of :doc:`Integer` parameter names that should take either an :doc:`Integer` or a :doc:`Ptr`. See :doc:`OSHandle` when creating external methods that can take an :doc:`Integer` or a :doc:`Ptr`. For more information on creating External Methods, refer to the :doc:`Declare` command in the Documentation. .. _/getting_started/using_the_xojo_language/advanced_language_features/structures: Structures ---------- A :doc:`Structure` is a compound value type. It consists of a series of fields that are grouped together as a single block. You control the size and order of the fields. A structure provides a convenient alternative to a :doc:`MemoryBlock` as they are also often used to group together information for external function calls. The values of structures have specific sizes giving the structure its own specific size. You typically only use structures when you have very specific memory or performance requirements or when you need to interface with an outside API that requires a structure. In most cases, you will want to use Classes with appropriate properties for your data management. Structures are added to project items such as modules, classes and windows. To add a structure, select the Add button on the toolbar and select Structure from the menu (or use the Insert menu in the main menu bar). This displays the Structure Editor where you specify the fields in the structure and their sizes. Use the “+” button to add a new field. Fields are added by entering their name followed by the type in this form: .. code:: xojo Name As Type So to enter an Integer you would do: .. code:: xojo Age As Integer Note that when you enter a field, it shows the size. This is the amount of bytes that the field uses in the structure. Integers use 4 bytes (in 32-bit apps), for example. You can prefix a name with an underscore (_) in order to hide it from auto-complete. .. _/getting_started/using_the_xojo_language/advanced_language_features/array_of_bytes: Array of bytes ************** The preferred way to deal with strings of text in a Structure is to create an array of Bytes, which you can do like this: .. code:: xojo Name(50) As Byte .. _/getting_started/using_the_xojo_language/advanced_language_features/strings: Strings ******* Not supported on iOS. Use an Array of Bytes instead. Strings are a special case because you have to specify the exact size of the string in bytes. Unlike normal strings, a string in a structure always takes up the specified size in the structure and you cannot exceed it. Also, strings in structures do not contain encoding information. The syntax is: .. code:: xojo Name As String * size So to have a String with a size of 50: .. code:: xojo Name As String * 50 When you are creating a structure to pass to an external method, be sure that your sizes precisely match the sizes specified by the API of the method. .. _/getting_started/using_the_xojo_language/advanced_language_features/usage: Usage ***** When you are creating a structure to pass to an external method, be sure that your sizes precisely match the sizes specified by the API of the method. .. image:: https://documentation.xojo.com/getting_started/using_the_xojo_language/images/advanced_language_features_structure_editor.png You can reference structure members using dot notation: .. code:: xojo Var cust As CustomerStruct cust.Age = 60 Structures are useful for organizing data, but they are not object-oriented and are limited in many ways (such as with string sizes). For your project's internal data management, you should almost always use a class over a structure. However, structures are small and memory efficient. In addition to being useful when used with external methods and Declares, they may also be useful in specific situations where memory must be managed carefully. .. _/getting_started/using_the_xojo_language/advanced_language_features/blank_and_comment_lines: Blank and comment lines *********************** When creating your structure, you can leave specific lines blank (nothing for the description) or you can enter a comment for the description. These lines are properly ignored. .. _/getting_started/using_the_xojo_language/advanced_language_features/structure_alignment: Structure alignment ******************* See :doc:`Structure` for information on structure alignment. .. _/getting_started/using_the_xojo_language/advanced_language_features/64-bit_notes: 64-bit notes ************ The :doc:`Integer` data type varies in size for a 32-bit or 64-bit app. For a 32-bit app, an Integer is 4 bytes. For a 64-bit app, an Integer is 8 bytes. In the Structure Editor, 32-bit sizes are shown by default. If you want to see 64-bit sizes, turn the "Show 64 bit Sizes" property to ON in the advanced Inspector for the structure. .. _/getting_started/using_the_xojo_language/advanced_language_features/delegates: Delegates --------- A :doc:`Delegate` data type is an object representing a specific method. Delegates are an advanced feature that decouple interface from implementation in a similar way to events or interfaces. This decoupling allows you to treat a method implementation as a variable that is changeable based on runtime conditions. They represent methods that are callable without knowledge of the target object. You can change the function the delegate points to on the fly. A Delegate can be declared in either a module or a class. You use the Insert→Delegate menu command or the Add button on the toolbar in the Code Editor to create a Delegate entry, which appears under the containing object. A delegate must have a name and can have optional parameters and a return type. To use a delegate, you have to point it to an address for a method. You can do this using the AddressOf or WeakAddressOf commands. Only methods that match the parameters and return type (the signature) of the delegate may be assigned to the delegate. When the Delegate contains the address of a method, you can call the method using the Invoke method of the Delegate. To try out a quick example, create a Delegate on a window and call it MethodCaller. Also on the window, create two methods: TestMethod and AnotherMethod. Each with this code: .. code:: xojo MessageBox(CurrentMethodName) Since these two methods have the same signature they can be assigned to a Delegate and called using the Invoke method. This code in the Pressed event handler of a Button calls a different method depending the state of a CheckBox: .. code:: xojo Var callMethod As MethodCaller If MethodCheck.Value Then callMethod = AddressOf TestMethod Else callMethod = AddressOf AnotherMethod End If callMethod.Invoke You can also create a delegate using an external function that returns a pointer. That code would look like this: .. code:: xojo Var fp As Ptr = ExternalFunctionThatReturnsAPointer Var callMethod As New MethodCaller(fp) .. _/getting_started/using_the_xojo_language/advanced_language_features/see_also: .. seealso:: :doc:`AddressOf`, :doc:`Declare`, :doc:`Structure` commands ============== Reserved words ============== A reserved keyword may not be used as identifiers for your own variables, objects, event definitions, methods, etc. In addition, do not use the underscore ("_") as the first character of an identifier. Note that :doc:`data types` are not reserved words. .. csv-table:: :header: "Word" :widths: auto "#Bad" ":doc:`#Else`" ":doc:`#Elseif`" ":doc:`#Endif`" ":doc:`#If`" ":doc:`#Pragma`" "#Tag" ":doc:`AddHandler`" ":doc:`AddressOf`" "Aggregates" ":doc:`And`" ":doc:`Array`" ":doc:`As`" ":doc:`Assigns`" "Async" "Attributes" "Await" ":doc:`Break`" ":doc:`ByRef`" ":doc:`ByVal`" ":doc:`Call`" ":doc:`Case`" ":doc:`Catch`" ":doc:`Class`" ":doc:`Const`" ":doc:`Continue`" ":doc:`CType`" ":doc:`Declare`" ":doc:`Delegate`" ":doc:`Dim`" ":doc:`Do`" ":doc:`DownTo`" ":doc:`Each`" ":doc:`Else`" ":doc:`ElseIf`" ":doc:`End`" ":doc:`Enum`" ":doc:`Event`" ":doc:`Exception`" ":doc:`Exit`" ":doc:`Extends`" ":doc:`False`" ":doc:`Finally`" "For" ":doc:`Function`" ":doc:`Global`" ":doc:`GoTo`" "Handles" ":doc:`If`" ":doc:`Implements`" ":doc:`In`" ":doc:`Inherits`" ":doc:`Interface`" ":doc:`Is`" ":doc:`IsA`" "Lib" ":doc:`Loop`" ":doc:`Me`" ":doc:`Mod`" ":doc:`Module`" "Namespace" ":doc:`New`" ":doc:`Next`" ":doc:`Nil`" ":doc:`Not`" ":doc:`Object`" "Of" ":doc:`Optional`" ":doc:`Or`" ":doc:`ParamArray`" ":doc:`Private`" ":doc:`Property`" ":doc:`Protected`" ":doc:`Public`" ":doc:`Raise`" ":doc:`RaiseEvent`" ":doc:`Redim`" ":doc:`Rem`" ":doc:`RemoveHandler`" ":doc:`Return`" ":doc:`Select`" ":doc:`Self`" ":doc:`Shared`" "Soft" ":doc:`Static`" ":doc:`Step`" ":doc:`Structure`" ":doc:`Sub`" ":doc:`Super`" ":doc:`Then`" ":doc:`To`" ":doc:`True`" ":doc:`Try`" ":doc:`Until`" ":doc:`Using`" ":doc:`Var`" ":doc:`WeakAddressOf`" ":doc:`Wend`" ":doc:`While`" "With" ":doc:`Xor`" Object-Oriented Programming =========================== .. rst-class:: forsearch Object Oriented Programming Xojo's language is object-oriented. Object-Oriented Programming (or OOP for short) makes code easier to write, easier to debug and easier to reuse. It allows you to combine both code and data into a single container of sorts called a Class. The pages below introduce and explain how to use Object-Oriented Programming in Xojo. .. toctree:: :maxdepth: 1 :name: sec-object-oriented_programming Class Properties, Methods and Events Me vs. Self OOP Classes OOP Design Concepts Subclassing Examples Constructors and Destructors Advanced OOP Features Interfaces ==================================== Class properties, methods and events ==================================== This topic covers properties, methods and events that you can add to classes. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/property_overview: Property overview ----------------- A Property is a trait that tells you something about an object. You use them as a way for class instances to store or calculate values. You can add four types of properties to classes: properties, computed properties, shared properties and shared computed properties. You can add properties to a subclass to store values that its super class doesn't store. For example, you might want to create a subclass of the TextField control that stores the last value the user entered. This would allow you to selectively reject the current entry and restore the last entry. Learn more about Properties and Computed properties in the :doc:`Properties` topic. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/properties: Properties ********** Properties are variables that belong to an entire class instance rather than just a single method. To add a property to a class, use the Add button on the Code Editor toolbar, Insert > Property from the menu, the contextual menu or the keyboard shortcut (:kbd:`Option Command P` on macOS or :kbd:`Ctrl Shift P` on Windows and Linux). You can set the property name, type, default value and scope using the Inspector. To quickly create a property, you can enter both its name and type on one line in the Name field like this: PropertyName As DataType. When you leave the field, the type will be set in the Type field. Properties added in this manner are sometimes called Instance Properties because they can only be used with an instance of the class. You can also add computed properties to a class. These are properties whose values are calculated rather than stored. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/shared_properties: Shared properties ***************** A shared property (sometimes called a Class Property) is like a "regular" property, except it belongs to the class, not an instance of the class. A shared property can be accessed from anywhere its scope allows. In many ways, a public shared property works like a protected module property. It is important to understand that if you change the value of a shared property, the change is available to every usage of the shared property. Generally speaking, shared properties are an advanced feature that you only need in special cases. For example, if you are using an instance of a class to keep track of items (e.g., persons, merchandise, sales transactions, and so forth) you can use a shared property as a counter. Each time you create or destroy an instance of the class, you can increment the value of the shared property in its constructor and decrement it in its destructor. (For information about constructors and destructors, see the section Constructors and Destructors.) When you access it, it gives you the current number of instances of the class. Shared Computed Properties work exactly like you expect. They are shared versions of computed properties. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/method_overview: Method overview --------------- Methods provide functionality for your classes. They usually perform some action, such as loading data or calculating values. Learn more about Methods in the :doc:`Methods` topic. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/methods: Methods ******* Methods (Instance Methods) are methods that belong to a class instance. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/shared_methods: Shared methods ************** Shared Methods (also called class methods) are methods that belong to the class rather than an instance. A shared method is like a "regular" method, except it belongs to the class, not an instance of the class. A shared method can be accessed from anywhere its scope allows. In many ways, a public shared method is equivalent to a protected module method. Generally speaking, shared methods are an advanced feature that you only need in special cases. For example, one use of a shared method is for the Factory pattern. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/scope_of_items: Scope of items -------------- Items on a class, such as method or properties must have a scope defined. The scope indicates where the property can be used. You set the Scope using the Inspector for the item. There are three choices: * **Public**: Can be used anywhere in your code with no restrictions, either within the class or from any class instance. To access the item within the class, refer to it by its name. For example, to use a property or method in a class instance, use dot notation: .. code:: xojo MyClassInstance.MyProperty = value MyClassInstance.MyMethod * **Protected**: Can be used by the class in which it is contained and any subclasses. To access the item, refer to it by its name. * **Private**: Can be used only by the class in which it is contained. To access the item, refer to it by its name. Refer to :ref:`Encapsulation` for more on scope. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/events: Events ------ Events are a type of method that is called by some action (or event) that has occurred. These events have nothing to do with the “events” in “event-driven programming”. Many classes and controls have their own built-in events (which are covered in the User Interface and Framework books), but you can also create your own events using Event Definitions. You do not directly call event handlers in your own code. For example, if you find that you need to manually call the Pressed event of a button, then you should instead put the code you need to call in a method. You can have the Pressed event handler call the method and you can now call the method directly in your own code as needed. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/event_definitions: Event definitions ***************** Event Definitions gives you a way to add your own Event Handlers to subclasses. Event Definitions can be called only from the class itself, but they can be implemented as an Event Handler only by its subclasses. To add an event definition to a class, use the Add button on the Code Editor toolbar, Insert > Event Definition from the menu or the contextual menu. With an event definition in place, you can then implement the event using Add Event Handler on a subclass or when you add this class to a layout. For example, say you have a Save method on a class and decide that you should give subclasses a way to do processing before and after the save. One way you could do this is by overriding the Save method on the subclass like this: .. code:: xojo Sub Save PreSave Super.Save PostSave End Sub This works but it relies on you defining and implementing everything properly. If you forget to call Super.Save then your overridden method won't actually save! But if you do this with events, then there is no room for error. With events, you create two event definitions on the super class: PreSave and PostSave. In the Save method on the super class, you then call PreSave at the beginning and PostSave at the end. When you create a subclass, you will see that it has two Event Handlers you can add to it: PreSave and PostSave. Add those event handlers and implement them as needed. Since you are not overridden the Save method, your code circumvents the possible issue shown above. The built-in control classes all contain a wide variety of event handlers (such as Opening), all of which work in this manner. Note that when you implement an event handler, it no longer appears in subclasses. If you want the event handler to still be available to additional subclasses, you need to create a new event definition in the subclass (matching the name and parameters) and then call it in from event handler you implemented. You can easily add a matching event definition by right-clicking on the event you have implemented and choosing "Create Event Definition from Event". When you create an event definition, you can enter a description for it in the Description field on the Advanced (gear) tab of the Inspector. This description appears in the Add Event Handler window when the event is selected. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/animal_example: Animal example ************** Looking back at the :ref:`Animal example that was done using Inheritance with method overriding`, this is how you would do it using events. Create a new class called Animal. Add to it a Speak method that returns String with this code: .. code:: xojo Return SpeakSound Now add to the Animal class an Event Definition called SpeakSound and set its return value to String. Next, create a subclass of Animal, called Cat. Click on the button “+” button on the toolbar and select “Add Event Handler”. In the dialog, you will see the SpeakSound event handler. Select and and press OK. The code for this event handler is: .. code:: xojo Return "Meow!" Create another subclass of Animal, called Dog, and also add the SpeakSound event handler with this code: .. code:: xojo Return "Woof!" To test this, create a button on a window. In the Pressed event of the button add code to create an array of Animals: .. code:: xojo Var animals() As Animal animals.Add(New Cat) animals.Add(New Dog) For Each a As Animal In animals MessageBox(a.Speak) Next When you run this code, you will see “Meow!” and then “Woof!”. Because Cat and Dog are both subclasses of Animal, they are allowed to be assigned to a variable (the animals array) with a type of Animal. When you call the Speak method of Animal (in the loop), it in turn calls the SpeakSound event handler of each subclass that was added to the array. .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/addhandler: AddHandler ********** The AddHandler command is used to have a method on one object handle the processing of an event on another. This is often used to allow you to instantiate a class directly and implement its event without having to create a separate subclass. For example, if you want to implement the Run event of a Timer, you will typically create a subclass. This can be done by dragging a Timer onto your window or web page or by adding a Timer subclass to your project and using that instead. Alternatively, you can declare the Timer in your code and then use AddHandler to have the Action event handled by a method on your window. For example: .. code:: xojo Var t As New Timer AddHandler t.Run, AddressOf MyTimerMethod t.RunMode = Timer.RunModes.Multiple t.Period = 500 t.Enabled = True In order for this to work, you must have a method in the window called MyTimerMethod and it must have a parameter for the timer itself: .. code:: xojo Sub MyTimerMethod(t As Timer) ' Your code goes here End Sub If the event you are handling has additional parameters, include them after the initial parameter used for the class itself. AddHandler can also be used in this manner for Threads: .. code:: xojo Var t As New Thread AddHandler t.Run, AddressOf MyThreadMethod t.Run .. _/getting_started/object-oriented_programming/class_properties,_methods_and_events/see_also: .. seealso:: :doc:`Me vs Self` topic =========== Me vs. Self =========== When you add a control such as a :doc:`DesktopButton` to a :doc:`DesktopWindow`, an instance of the control is created for you and added to the Window. Code that is in the event handlers of a control added to the Window can refer to both its own properties and methods as well as the properties and methods of the window. By default, all code without a prefix refers to the properties and method of the :doc:`DesktopWindow` itself (or :doc:`WebPage` or :doc:`MobileScreen`). You can use the :doc:`Self` prefix to be explicit that you are referring to the DesktopWindow/DesktopContainer (or WebPage/WebContainer or MobileScreen/MobileContainer) or you can leave it off. If you want to access a property or method of the control from the code in one of its event handlers on the DesktopWindow/WebPage/MobileScreen or on the DesktopContainer/WebContainer/MobileContainer (such as Button.Pressed), you must use the :doc:`Me` prefix. For example, consider the Visible property which is on both a Button and the Window itself. If you want to make the button invisible when it is clicked, you have to make sure you use the proper Visible property in the Pressed event handler. This code: .. code:: xojo Visible = False ' Hides window will hide the window and not the button because the Visible property defaults to Self, which is the window. To access the Button's Visible property, do this: .. code:: xojo Me.Visible = False ' Hides button To be even clearer, you can use the Self keyword to specifically state that you want to use the Window property. To hide the window, you could instead write: .. code:: xojo Self.Visible = False ' Hides window Note that you might be tempted to refer to the window name directly and write code such as this: .. code:: xojo Window1.Visible = False **Do not do this!** Should you rename your window, this code will cause a compile error because the name is no longer valid. If you use the :doc:`Me` prefix outside of a control's event handler on a DesktopWindow/WebPage/MobileScreen or on a DesktopContainer/WebContainer/MobileContainer (such as in a method), then :doc:`Me` will work the same as :doc:`Self`. To prevent confusion, you should refrain from using the :doc:`Me` prefix outside of control event handlers on DesktopWindows/WebPages/MobileScreens or DesktopContainer/WebContainer/MobileContainer. When working with classes and subclasses you have added to your project, you should always use :doc:`Self` (or nothing since Self is the default). This is true even if you create a control subclass and are implementing its event handlers. Again, you only use :doc:`Me` when in the event handler for a control (or class) that is added to a DesktopWindow/WebPage/MobileScreen or if you use the :doc:`Me` prefix outside of a control's event handler on a DesktopWindow/WebPage/MobileScreen or on a DesktopContainer/WebContainer/MobileContainer (such as in a method), then :doc:`Me` will work the same as :doc:`Self`. To prevent confusion, you should refrain from using the :doc:`Me` prefix outside of control event handlers on DesktopWindows/WebPages/MobileScreens or on DesktopContainer/WebContainer/MobileContainer. .. _/getting_started/object-oriented_programming/me_vs._self/see_also: .. seealso:: :doc:`DesktopWindow`, :doc:`WebPage`, :doc:`MobileScreen`, :doc:`DesktopContainer`, :doc:`WebContainer`, :doc:`MobileContainer` classes; :doc:`Me`, :doc:`Self` commands =========== OOP classes =========== In its simplest form, a class is a container of code and data much like a module. But unlike a module, a class provides better code reuse. Classes are the fundamental building blocks of object-oriented programming. Classes offer many benefits, including: * **Reusable Code** - When you directly add code to a DesktopButton on a Window to customize its behavior, you can only use that code with that one DesktopButton. If you want to use the same code with another DesktopButton, you need to copy the code and then make changes to the code in case it refers to the original DesktopButton (since the new DesktopButton will have a different name than the original). Classes store the code once and refer to the object (like the DesktopButton) generically so that the same code can be reused any number of times without modification. If you create a class based on the DesktopButton control and then add your code to that class, any usage (instances) of that custom class will have that code. * **Smaller Projects and Apps** - Because classes allow you to store code once and use it over and over in a project, your project and the resulting app is smaller in size and may require less memory. * **Easier Code Maintenance** - Less code means less maintenance. If you have basically the same code copied in several places of your projects, you have to keep that in mind when you make changes or fix bugs. By storing one copy of the code, when you need to make changes, you'll spend less time tracking down all those places in your project where you are using the same code. Changes to the code in a class are automatically used anywhere where the class is used. * **Easier Debugging** - The less code you have, the less code there is to debug. * **More Control** - Classes give you more control than you can get by adding code to the event handlers of a control in a window. You can use classes to create custom controls. And with classes, you have the option to create versions that don't allow access to the source code of the class, allowing you to create classes you can share or sell to others. .. _/getting_started/object-oriented_programming/oop_classes/understanding_classes,_instances_and_references: Understanding classes, instances and references ----------------------------------------------- Before you can use a class in your project, it is important to understand the distinction between these three concepts: the class itself, the instance of the class and the reference to the class. **The Class**: Think of the class as a template for a container of information (code and data), much like a module. And like a module, each class exists in your project only once. But unlike a module, a class can have multiple instances that each contain different data. **The Instance**: Classes provide better code reuse because of a concept called instances. Unlike a Module, which exists only once in your running app, a class can have multiple instances. Each instance (also called an object) is a separate and independent copy of the class and all its methods and properties. .. _/getting_started/object-oriented_programming/oop_classes/the_class: The class ********* There are many built-in classes for user interface controls. Some commonly used ones are :doc:`DesktopButton`, :doc:`WebButton`, :doc:`MobileButton`, :doc:`DesktopLabel`, :doc:`WebLabel`, :doc:`MobileLabel`, :doc:`DesktopTextField`, :doc:`WebTextField`, :doc:`MobileTextField`, :doc:`DesktopListBox`, :doc:`WebListBox` and :doc:`iOSMobileTable`. By themselves, control classes are not all that useful; they are just abstract templates. But each time you add a control class to a layout (a :doc:`DesktopWindow`, :doc:`WebPage` or :doc:`MobileScreen`) you get an instance of the class. Because each button is a separate instance, this is what allows you to have multiple buttons on a Window, with each button being completely independent of each other with its own settings and property values. This is an example of a Vehicle class that will be used in later examples: .. code:: xojo Class Vehicle Property Brand As String Property Model As String End Class Note that this above code is for reference. You do not actually write code like this to create a class. To create a class in Xojo use the Insert menu or toolbar and choose Class. .. _/getting_started/object-oriented_programming/oop_classes/the_instance: The instance ************ These instances are what you interact with when writing code on the window and is what the user interacts with when they use your app. For example, when you drag a TextArea from the Library to a Window, you create a usable instance of the TextArea on the Window. The new instance has all the properties and methods that were built into the TextArea class. You get all that for free — styled text, multiple lines, scroll bars, and all the rest of it. You customize the particular instance of the TextArea by modifying the values of the instance's properties. When you add a control to a window (or web page), the Layout Editor creates the reference for you automatically (it is the name of the control). However, when writing code you create instances of classes using the :doc:`New` keyword. For example, this create a new instance of a Vehicle class that is in your project: .. code:: xojo Var car As New Vehicle .. _/getting_started/object-oriented_programming/oop_classes/the_reference: The reference ************* A reference is a variable or property that refers to an instance of a class. In the above code example, the car variable is a reference to an instance of the Vehicle class. You interact with properties and methods of the class using dot notation like this: .. code:: xojo Var car As New Vehicle car.Brand = "Ford" car.Model = "Focus" Here the Brand and Model were defined as properties of the Vehicle class and they are given values for the specific instance. Having an instance is important! If you try to access a property or method of a class without first having an instance, you will get a NilObjectException likely causing your app to quit. This is is one of the most common programming errors that people make and is typically caused by forgetting the New keyword. For example, this code might look OK at first glance: .. code:: xojo Var car As Vehicle car.Brand = "Ford" car.Model = "Focus" But the 2nd line will result in a :doc:`NilObjectException` when you run it because car is not actually an instance. It is just a variable that is declared to eventually contain a Vehicle instance. But since an instance was not created, it gets the default value of Nil, which means it is empty or undefined. If you are not sure if a variable contains an instance, you can check it before you use it by using the :doc:`Nil` keyword like this: .. code:: xojo If car <> Nil Then car.Brand = "Ford" car.Model = "Focus" End If When the variable is not Nil, then it has a valid reference. And since it is a reference, it has important considerations when assigning the variable to another variable: When you do an assignment from one reference variable to another, the second variable points to the same reference as the first variable. This means the second variable refers to the same instance of the class as the first variable and is not a copy of it. So if you change a property of the class with either variable, then the property is changed for both. An example might help demonstrate this: .. code:: xojo Var car As New Vehicle car.Brand = "Ford" car.Model = "Focus" Var truck As Vehicle truck = car ' truck.Model is now "Focus" car.Model = "Mustang" ' truck.Model is now also "Mustang" truck.Model = "F-150" ' car.Model is now also "F-150" In this diagram you can see that the variables for both car and truck point to the same instance of Vehicle. So changing either one effectively changes both. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/oop_classes_oop_diagram_1.png :scale: 50 % :alt: Vehicle Class with car and truck objects If you want to create a copy of a class, you need to instead create a new instance (using the :doc:`New` keyword) and then copy over its individual properties as shown below: .. code:: xojo Var car As New Vehicle car.Brand = "Ford" car.Model = "Focus" Var truck As New Vehicle truck.Brand = car.Brand trunk.Model = car.Model ' truck.Model is now also "Focus" truck.Model = "F-150" ' car.Model remains "Focus" When you do it this way, you get two separate instances. Changes to one do not affect the other as you can see in these diagrams: .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/oop_classes_oop_diagram_2.png :scale: 50 % :alt: Two variables pointing at two instances of the same class .. _/getting_started/object-oriented_programming/oop_classes/adding_classes: Adding classes -------------- To add a class to your Xojo project, use the Insert menu or toolbar and choose Class. Your class will appear in the Navigator. Note: You do not directly create classes in code. They are always added using Insert as described above. .. _/getting_started/object-oriented_programming/oop_classes/adding_properties_to_classes: Adding properties to classes ---------------------------- Properties are variables that belong to an entire class instance rather than just a single method. To add a property to a class, use the Add button on the Code Editor toolbar, Insert > Property from the menu, the contextual menu or the keyboard shortcut (:kbd:`Option Command P` on macOS or :kbd:`Ctrl Shift P` on Windows and Linux). You can set the property name, type, default value and scope using the Inspector. To quickly create a property, you can enter both its name and type on one line in the Name field like this: PropertyName As DataType. When you leave the field, the type will be set in the Type field. Properties added in this manner are sometimes called Instance Properties because they can only be used with an instance of the class. You can also add properties that can be accessed through the class itself without using an instance. These are called Shared Properties. .. _/getting_started/object-oriented_programming/oop_classes/shared_properties: Shared properties ***************** A shared property (sometimes called a *class property*) is like a regular property, except it belongs to the class, not an instance of the class. A shared property is global and can be accessed from anywhere its scope allows. In many ways, it works like a module property. It is important to understand that if you change the value of a shared property, the change is available to every usage of the shared property. Generally speaking, shared properties are an advanced feature that you only need in special cases. For example, if you are using an instance of a class to keep track of items (e.g., persons, merchandise, sales transactions, and so forth) you can use a shared property as a counter. Each time you create or destroy an instance of the class, you can increment the value of the shared property in its constructor and decrement it in its destructor. (For information about constructors and destructors, see the section Constructors and Destructors.) When you access it, it gives you the current number of instances of the class. .. _/getting_started/object-oriented_programming/oop_classes/adding_methods_to_classes: Adding methods to classes ------------------------- To add a method to a class, use the Add button on the Code Editor toolbar, Insert > Method from the menu, the contextual menu or the keyboard shortcut (:kbd:`Option Command M` on macOS or :kbd:`Ctrl Shift M` on Windows and Linux). You can set the method name, parameters, return type and scope using the Inspector. When typing the method name, the field will autocomplete with the names of any methods on its super classes. Methods added in this manner are called Instance Methods because they can only be used with an instance of the class. You can also add methods that can be accessed through the class itself. These are called Shared Methods. .. _/getting_started/object-oriented_programming/oop_classes/shared_methods: Shared methods ************** A shared method (sometimes called a *class method*) is like a normal method, except it belongs to the class, not an instance of the class. A shared method is global and can be called from anywhere its scope allows. In many ways, it works like a module method. Shared Methods do not know about an instance so its code can only access other shared methods or shared properties of the class. Generally speaking, shared methods are an advanced feature that you only need in special cases. A common usage of shared methods is to create an instance (rather than relying on a Constructor). .. _/getting_started/object-oriented_programming/oop_classes/see_also: .. seealso:: :doc:`OOP Design Concepts` topic =================== OOP design concepts =================== This topic covers some of the important object-oriented design concepts that apply to classes: Encapsulation, Overloading, Inheritance and Polymorphism. .. _/getting_started/object-oriented_programming/oop_design_concepts/encapsulation: Encapsulation ------------- Encapsulation is the process of hiding information that does not need to be exposed to other parts of the code. With classes, you can control this using the scope of your properties and methods. In order from least visible to most visible, there are: Private, Protected and Public. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/oop_design_concepts_class_scope.png .. _/getting_started/object-oriented_programming/oop_design_concepts/private: Private ******* Setting the scope to private means that the item is only accessibly from with the current class instance (or class for shared items). This makes it inaccessible to the rest of your code. Private is the most restrictive setting. It is good coding practice to get into the habit of setting your items to Private if they are not needed outside the class instance. Private items can be referenced by their name. .. _/getting_started/object-oriented_programming/oop_design_concepts/protected: Protected ********* A scope of protected means the item is accessible from the current class instance (or the class itself for shared items) and its subclasses. A Protected method, property, or constant is available only to other code within the class and subclasses based on the class. It is inaccessible to the rest of your code. When other code in the class needs to access a Protected method, property, or constant, you reference it by name. If you try to access a Protected method, property, or constant outside of the class, you will get an error message indicating that the item is out of scope when you try to run your project. .. _/getting_started/object-oriented_programming/oop_design_concepts/public: Public ****** A Public method, property, or constant is available to code throughout the project. Within the class, you can refer to the item by its name. In code outside the class, you reference it using dot notation using the class reference and the name, such as: .. code:: xojo car.Model .. _/getting_started/object-oriented_programming/oop_design_concepts/how_scope_affects_access: How scope affects access ************************ Given ClassA as an Object and ClassB is a subclass of ClassA. * ClassA defines a public method, MyPublicMethod. That method can be called from any code. * ClassA defines a protect method, MyProtectedMethod. Code in ClassA or ClassB can call that method. * ClassA defines a private method, MyPrivateMethod. Only code in ClassA may call the method. .. _/getting_started/object-oriented_programming/oop_design_concepts/overloading: Overloading ----------- Overloading is the term for methods that have the same name, but have different method signatures (such as a different number of parameters or different data types for the parameters). You cannot overload methods based on different return types, although overloads do distinguish between having any return type and not having a return type at all. To overload a method in your own classes, add a new method to the class and give it the same name as an existing method, but with different parameters. The method appears in the Navigator with the overloaded parameters shown below the method so that you can tell them apart. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/oop_design_concepts_class_method_overloading.png To overload methods in built-in framework classes, you first need to create a subclass using inheritance. You can also overload operators using a variety of specially implemented functions. A good example of a built-in overloaded operator is the “+” operator. If its arguments are numbers, it computes the sum; if the arguments are text, it concatenates the text. Refer to Operator Overloading for more information. .. note:: Overloading by return type or by having no return type is not currently supported for Android. .. _/getting_started/object-oriented_programming/oop_design_concepts/inheritance_(subclassing): Inheritance (subclassing) ------------------------- Inheritance a feature of object-oriented programming where you create a new class that is based on an existing class. The new class is called the subclass and the existing class is called the super class. The subclass "inherits" and can thus use all the public and protected properties and methods of its superclass. Why is this useful? You may find situations where you would like to have an object that is a slightly altered version of one of the built-in classes. For example, you might want a version of the TextArea control that disables the Cut and Copy items on the Edit menu, preventing the user from putting sensitive data on the Clipboard. You might want to create a List Box that, by default, has the months of the year in it. You can create your own versions of these built-in classes by creating subclasses that you add to your project. There's an important difference between adding an instance of TextField to a window versus adding a subclass based on TextField to your project. In the latter case, you can customize the subclass based on TextField itself and use instances of the customized subclass in several places in the application. You can also save the customized subclass so that you can reuse it in other projects. .. _/getting_started/object-oriented_programming/oop_design_concepts/what_is_a_subclass?: What is a subclass? ******************* A subclass is a class that has a super class. A super class is a class the subclass is based on, also sometimes called the parent class. Subclasses inherit all of their super's (public or protected) properties, methods and constants. The subclass can also implement any event handlers that were not implemented on the super class or it can implement event definitions on the super class. In fact, a subclass works identically to its super class until you start modifying it. After that, it's different from its super class only in the ways you make it different by adding properties, modifying events, or adding or modifying methods. A subclass can use any of the public or protected properties and methods on the super class by default. .. _/getting_started/object-oriented_programming/oop_design_concepts/creating_a_subclass_from_an_existing_class: Creating a subclass from an existing class ****************************************** There are several ways to create a subclass from an existing class in your project or from an existing framework class. 1. If want to subclass a built-in control class, you can drag the control from the Library to the Navigator. #. You can select a class in the Navigator and use the contextual menu option “New Subclass” to create a new subclass based on the selected class. #. You can add a class to your project (using the Insert button on the toolbar or the Insert menu) and then manually change its Super property to the name of the class that it is based on. You can just type the name of the superclass in the Super field or you can click the small "pencil" button icon next to the field to display the Select Super Class window where you can choose a class. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/oop_design_concepts_select_superclass.png .. _/getting_started/object-oriented_programming/oop_design_concepts/creating_a_superclass_from_an_existing_class: Creating a superclass from an existing class ******************************************** If you have an existing class that you have determined should really be a subclass, you can create a super class from it. Use the contextual menu for the class and select “Extract Superclass”. This opens a window that allows you to specify the name of the super class and to choose the method and properties to move from the class to the super class. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/oop_design_concepts_extract_superclass.png If you extract a superclass from an existing subclass, the new class will be added between the old superclass and the current subclass. .. _/getting_started/object-oriented_programming/oop_design_concepts/virtual_methods_(overriding): Virtual methods (overriding) ---------------------------- Virtual methods provide a way for a subclass to override its own version of a method that its super class has. Ordinarily, a subclass inherits the methods belonging to its parent. When a subclass has a method that has the same name (and parameters) as a method on the super class, the subclass method is instead called. This is referred to as “overriding”. You can still call the method on the superclass by calling it specifically using the :doc:`Super` prefix with the method call: .. code:: xojo Super.MethodName .. _/getting_started/object-oriented_programming/oop_design_concepts/polymorphism: Polymorphism ------------ Polymorphism is the ability to have completely different data types (typically classes) behave in a uniform manner. You can do this using inheritance and overriding, inheritance and events or class interfaces. .. _/getting_started/object-oriented_programming/oop_design_concepts/animal_example: Animal example ************** A simple example of this is the Animal, Cat and Dog relationship. If you want to have a generic animal object that knows how to speak “Meow!” or “Woof!” depending on whether it is actually a Cat or Dog, you could implement it using inheritance and overriding. First, create a new class called Animal. Add to it a Speak method that returns String. Now create a subclass of Animal, called Cat. Add to it a Speak method that returns String. This means you are overriding the unimplemented Speak method on Animal. The code for this method is: .. code:: xojo Return "Meow!" Next, create another subclass of Animal, called Dog. Add to it a Speak method that returns a String with this code: .. code:: xojo Return "Woof!" To test this, add a button to a layout. In the Pressed event of the button add code to create an array of Animals and then to loop through the array calling the Speak method: .. code:: xojo Var animals() As Animal animals.Add(New Cat) animals.Add(New Dog) For Each a As Animal In animals MessageBox(a.Speak) Next When you run this code, you will see “Meow!” and then “Woof!”. Because Cat and Dog are both subclasses of Animal, they are allowed to be assigned to a variable (the animals array) with a type of Animal. So an Animal can be either a Cat or Dog. And when you call the Speak method of Animal (in the loop), because of polymorphism, your code calls the Speak method of the actual subclass type that was added to the array. You can also achieve the same effect by using Inheritance with Events or with Class Interfaces. .. _/getting_started/object-oriented_programming/oop_design_concepts/see_also: .. seealso:: :doc:`Advanced OOP Features` topic ==================== Subclassing examples ==================== These are some examples of control subclasses. Subclassing can be used in all project types. .. _/getting_started/object-oriented_programming/subclassing_examples/selectablepopupmenu: SelectablePopupMenu ------------------- As an example, you can create a PopupMenu subclass that adds a method that can be used to select an existing item in the :doc:`DesktopPopupMenu`. Create a new class, called SelectablePopupMenu, and set its Super to PopupMenu. On this class, add a new public method and call it “SelectRow” with the parameter “value As String”. This method will loop through all the rows in the PopupMenu and check to see if the text for a row matches the supplied value. If the text matches, it will select the row and return. If no matches are found, then nothing is selected. This is the code for the SelectRow method: .. code:: xojo For i As Integer = 0 To Self.LastRowIndex If Self.RowTextAt(i) = value Then ' Found a row with matching ' text, so select it Self.SelectedRowIndex = i Return End If Next ' Select nothing Self.SelectedRowIndex = PopupMenu.NoSelection .. _/getting_started/object-oriented_programming/subclassing_examples/monthpopup: MonthPopup ---------- Here is another example. Suppose you want to create a :doc:`DesktopPopupMenu` that, by default, displays the names of the months of the year, with the current month selected. To do this, create a new class, call it MonthPopup, and choose :doc:`DesktopPopupMenu` as its super class. Now you can add an Opening event handler to MonthPopup and add the month names, the heading, and code that selects the appropriate month in the list. In this case, the Opening event handler is: .. code:: xojo Self.AddRow("January") Self.AddRow("February") Self.AddRow("March") Self.AddRow("April") Self.AddRow("May") Self.AddRow("June") Self.AddRow("July") Self.AddRow("August") Self.AddRow("September") Self.AddRow("October") Self.AddRow("November") Self.AddRow("December") ' Select the current month Var d As DateTime = DateTime.Now Self.SelectedRowIndex = d.Month - 1 To add an instance of the MonthPopup class to a window, switch to the window's Window Editor and then drag MonthPopup from the Navigator to the Window Editor. When you run the application, the Opening event handler will run and populate the PopupMenu with the months of the year, selecting the current month. .. note:: Once created, custom control classes can be exported as self-contained objects that can be used in other projects. Just right-click (:kbd:`Control`-click on Mac) on the custom control class in the Navigator and choose Export... from the contextual menu. .. _/getting_started/object-oriented_programming/subclassing_examples/subclasses_are_classes: Subclasses are classes ---------------------- Remember, subclasses are classes. They are called subclasses to differentiate them from the original classes and emphasize the point that they inherit the properties, events, and methods of their parent class. Because subclasses are classes, they can be the super class to other subclasses. For example, suppose you had a NumbersOnlyTextField subclass, but now you need a TextField that allows only numbers within a certain range. You could duplicate the NumbersOnlyTextField subclass and then modify its code. However, this would make your project larger and more difficult to maintain. If you found a bug in the code of the NumbersOnlyTextField, you would have to remember that you used that code in other places as well, track them down, and fix them. A more efficient way is to create a new subclass and choose the NumbersOnlyTextField as its super class. The new subclass (let's call it “NumberRangeTextField”) would utilize all of the properties, events, and methods of its super class. However, you can add code to the TextChanged event handler that allows only numbers within a specific range. .. _/getting_started/object-oriented_programming/subclassing_examples/see_also: .. seealso:: :doc:`Desktop Custom Controls` topic ============================ Constructors and Destructors ============================ When you create a new object, you will sometimes want to perform some sort of initialization on the object. The constructor is a mechanism for doing this. An object's constructor is the method that is executed automatically when an instance of the class is created. Conversely, when an object is removed from memory, its destructor is called. .. _/getting_started/object-oriented_programming/constructors_and_destructors/constructors: Constructors ------------ You add a constructor to a class by adding a method to the class called Constructor. For convenience, “Constructor” is a choice in the Method Name popup menu in the Code Editor. A default constructor is implied for all classes. This constructor will be called automatically when an instance is created via the New operator. You can override the default constructor or add your own constructors with parameters. If your constructor accepts parameters, you must pass the required number of parameters and they must be of the correct data type. Whenever you create a constructor for a subclass, the Code Editor automatically adds a call to the super class's constructor for you in the Code Editor and adds comments that explains its purpose. .. code:: xojo ' Calling the overridden superclass constructor. Super.Constructor This is added because the constructor that you are writing overrides its Super class's constructor, but the new object may not be initialized correctly unless its Super class's constructor executes. The Super method is how you call a method of a Super class that is being overridden by a method of the subclass. Here is a simple example of a constructor. Suppose you have a class that contains customer information, with properties for FirstName and LastName. You might assign these values in this manner: .. code:: xojo Var c As New Customer c.FirstName = "Betty" c.LastName = "Draper" But you can instead add a Constructor to the class to initialize it with those values. This constructor takes firstName and lastName as parameters and assigns them to the corresponding properties: .. code:: xojo Sub Constructor(fName As String, lName As String) FirstName = fName LastName = lName End Sub You can now initialize the customer like this: .. code:: xojo Var c As New Customer("Betty", "Draper") .. _/getting_started/object-oriented_programming/constructors_and_destructors/initializing_control_subclasses: Initializing control subclasses ******************************* If you need to initialize an instance of a control subclass, don't use the constructor. Instead, put the code that does the initialization in the Opening event handler for the control. When you access Inspector properties in the Constructor, they will not have the default value specified in the Inspector and will instead have the default value for the data type. If you assign values to these public properties, the values will get overridden with what is specified in the Inspector. .. _/getting_started/object-oriented_programming/constructors_and_destructors/constructor_scope: Constructor scope ***************** Like with methods, you can set a Constructor's scope to Public, Protected and Private. One useful technique is to disable the standard constructor so that you force your own constructor to be used. For example, with the Customer class described above, you could add an empty, Constructor with its scope set to private to prevent the standard "New Customer" from working. Instead, only the Constructor with the parameters is allowed. If your tried to write this code: .. code:: xojo Var c As New Customer You would get this compilation error: The constructor of this class is protected, and can only be called from within this class .. _/getting_started/object-oriented_programming/constructors_and_destructors/destructors: Destructors ----------- You can also create a destructor, although they are not usually necessary. The destructor is called automatically when an instance of the parent class is deleted or goes out of scope — for example, when the user closes the window. Name the new method “Destructor”. Destructors take no parameters and do not return a value. Destructors are called when the last reference to an object is removed, even if execution is in a destructor for another object. Note that this means you can cause a stack overflow if your destructor triggers other destructors in a deep recursion. However, such overflow will not happen as long as properties of the object are being cleaned up automatically. So, it is generally preferable to not set properties to Nil in your destructor, but instead allow them to be automatically cleaned up for you. .. _/getting_started/object-oriented_programming/constructors_and_destructors/memory_management: Memory management ----------------- Refer to the :doc:`Memory Management` topic for information on how Xojo uses Automatic Reference Counting to manage memory used by objects. .. _/getting_started/object-oriented_programming/constructors_and_destructors/see_also: .. seealso:: :doc:`Advanced OOP Features` topic ===================== Advanced OOP features ===================== This section covers more advanced features of classes. You will not need these features often, but they are definitely useful. .. youtube:: dH4FiW6HM1g .. _/getting_started/object-oriented_programming/advanced_oop_features/assigning_a_value_to_a_method: Assigning a value to a method ----------------------------- Sometimes it is helpful, from a syntax standpoint, to have a method that gets a value using an assignment operator. Rather than doing this: .. code:: xojo order.Quantity(2) You would write: .. code:: xojo order.Quantity = 2 which looks very much like a property. You can get the syntax behavior of a property using a method by using the Assigns keyword in the parameter list. The declaration looks like this: .. code:: xojo Sub Quantity(Assigns amount As Integer) You can also have more than one parameter, something that you can not do with a simple property: .. code:: xojo Sub Quantity(productName As String, Assigns amount As Integer) This command would be used like this: .. code:: xojo order.Quantity("Book") = 2 When you use this technique, it will look like you are setting a property, even though you are really dealing with a method. This can be handy when designing classes and can make your code easier to read. .. _/getting_started/object-oriented_programming/advanced_oop_features/operator_overloading: Operator overloading -------------------- You can easily overload methods on a class, but how do you overload an operator on a class? For example, what if you have two instances of a SalesOrder class and want to tell if one is greater than the other? You do this by implementing one of the Operator overload methods (Figure 5.10) on the SalesOrder class, in this case Operator_Compare. .. code:: xojo Sub Operator_Compare(rightOrder As SalesOrder) As Integer If Self.TotalAmount > rightOrder.TotalAmount Then Return 1 ElseIf Self.TotalAmount < rightOrder.TotalAmount Then Return -1 Else Return 0 End If End Sub These are the operator overload methods: .. csv-table:: :header: "Operator", "Function" :widths: auto "+",":doc:`Operator Add`, :doc:`Operator AddRight`" "-",":doc:`Operator Subtract`, :doc:`Operator SubtractRight`" "*",":doc:`Operator Multiply`, :doc:`Operator MultiplyRight`" "/",":doc:`Operator Divide`, :doc:`Operator DivideRight`" "\\",":doc:`Operator IntegerDivide`, :doc:`Operator IntegerDivideRight`" "Mod",":doc:`Operator Modulo`, :doc:`Operator ModuloRight`" "And",":doc:`Operator And`, :doc:`Operator AndRight`" "Or",":doc:`Operator Or`, :doc:`Operator OrRight`" "Not",":doc:`Operator Not`" "=, <, >, <=, >=",":doc:`Operator Compare`" "(lookup)",":doc:`Operator Lookup`" "(negation)",":doc:`Operator Negate`" "(convert)",":doc:`Operator Convert`" "Subscript (array)",":doc:`Operator Subscript`" "Redim",":doc:`Operator Redim`" For more information about operator overloading for custom classes, refer to :doc:`Operator Overloading`. .. _/getting_started/object-oriented_programming/advanced_oop_features/operator_lookup: Operator lookup *************** Dot notation is used to access methods and properties of a class instance. If you try to access a method or property that does not exist, you get a compilation error. Operator Lookup is a special class method that is called when anything following the "." that is not an actual method or property of the class is used. One use for this feature is to look up a value that may not be known until runtime. Suppose you are creating a Preferences class that will be used to save your application preferences. Normally, you would have to create a property for each preference you want to save. If you find you have a new preference value, you 'll need to go back to the class and add the property before you can use it. But you could use operator lookup instead. With operator lookup, you use the method to check what was typed after the "." and have that be the name of the preference. The value that is assigned can then be stored (perhaps using a Dictionary). Your code to save a value might look like this: .. code:: xojo Sub Operator_Lookup(name As String, Assigns value As String) If mPreferenceDict = Nil Then mPreferenceDict = New Dictionary End If mPreferenceDict.Value(name) = value End Sub And the code to get a value might look like this: .. code:: xojo Function Operator_Lookup(name As String) As String If mPreferenceDict = Nil Then Return "" If mPreferenceDict.HasKey(name) Then Return mPreferenceDict.Value(name) End If End Sub With this in place, you can now write code to save preference values even though you have never defined specific properties or methods for the preference. In the App class, add a property called Prefs as Preferences. In the App.Opening event, initialize this property: .. code:: xojo Prefs = New Preferences Now, anywhere in your project you can write code like this to store a preference value: .. code:: xojo App.Prefs.UserName = "Mary Roberts" App.Prefs.UserEmail = "mary@gmail.com" And code like this to get a preference value: .. code:: xojo Var userName As String userName = App.Prefs.UserName ' Not an actual property, but calls Operator_Lookup .. _/getting_started/object-oriented_programming/advanced_oop_features/attributes: Attributes ---------- Attributes are compile-time properties. They can be added to project items and code items such as method and properties. An attribute consists of its Name and its Value. The Name is required, but the value is optional. Attributes are added using the advanced tab of the Inspector. Attributes can be added to classes, modules, windows, containers, interfaces and toolbars. A subclass inherits attributes from its super class. These inherited values can be overridden by the subclass by redefining them. .. note:: User-added attributes are not currently supported for Android. .. _/getting_started/object-oriented_programming/advanced_oop_features/creating_an_attribute: Creating an attribute ********************* You create an attribute using the Attribute Editor in the advanced tab of the Inspector for the item. Use the “+” or “-” buttons to add or remove attributes. Specify the Name and Value for the attribute in the list. There are two ways to specify the value. If it is a literal value, such as “ID”, then the value must be enclosed in quotes. If it is a constant, you can just use the name of the constant, for example kID. If you forget the quotes for a literal value, you will get a compiler error if the constant is not found. .. _/getting_started/object-oriented_programming/advanced_oop_features/accessing_an_attribute: Accessing an attribute ********************** Attributes are accessed in your code using Introspection. The AttributeInfo class is used to fetch the Name-Value attribute pairs for a particular object. This code gets the attribute values of the default window and displays them in a List Box: .. code:: xojo Var myAttributes() As Introspection.AttributeInfo = Introspection.GetType(Window1).GetAttributes For i As Integer = 0 To myAttributes.LastIndex ListBox1.AddRow(myAttributes(i).Name) If myAttributes(i).Value.IsNull Then ListBox1.CellTextAt(ListBox1.LastIndex, 1) = "No Value" Else ListBox1.CellTextAt(ListBox1.LastIndex, 1) = myAttributes(i).Value.ToString End If Next .. _/getting_started/object-oriented_programming/advanced_oop_features/special_attributes: Special attributes ****************** There are several reserved attributes that perform special actions when used on a project item (such as a class or module) or a method, property (or constant, etc.) that you can add to the item. These attributes may be useful on frameworks you create for use by other developers. * **Deprecated**: The Deprecated attribute allows you to indicate that an item “has a replacement” that should instead be used. Set the attribute value to the name of the class/method/property that should be used instead (be sure to enclose the name with quotes). A deprecated item appears in the Errors Panel when you use Analyze Project. * **Hidden**: The Hidden attribute hides the specified item from introspection, the debugger and auto-complete. You do not need to specify a value. * **HideFromLibrary**: The project item will not be shown in the Library's Project Controls section. You do not need to specify a value. * **StructureAlignment**: This attribute adjusts how structures align on byte boundaries. * **DefaultEvent**: When creating your own control classes, you can add this property to determine the event that is selected by default in the Add Event Handler window. Set the attribute value to the name of an event definition on the class (be sure to enclose the name with quotes). * **LibraryDescription**: When creating your own control classes, you can add this property to display the value text in the Description area of the Library when hovering over the control. .. _/getting_started/object-oriented_programming/advanced_oop_features/casting: Casting ------- An extremely powerful way of creating generic, reusable code is to take advantage of the ability to convert an instance of a class to an instance of its subclass. The idea is best illustrated by an example. Since the objects you create are subclasses of base classes, you can always test to see whether an object is a member of a specific subclass. The IsA operator does this. With the IsA operator, you test whether an object is of a specific subclass and, if it is, cast it as that type to do something specific with it. You cast an object by using the classname as a function that operates on the instance. When you cast an instance, compile-time error-checking cannot guarantee that you are casting to a legal object type. The instance that you are casting has to be of the type that you specify. Casting just tells your code to treat the object as a instance of the class to which it is cast. It doesn't convert it from one class to another. If you cast incorrectly, you will get an IllegalCastException at runtime. Here is an example that uses a For loop to cycle through all the controls in a window. It checks each control to see if it is a Label and if it is, it casts the control and displays its text: .. code:: xojo Var labelText As String ' Loop through controls in window For Each aControl As DesktpUIControl in Self.Controls If aControl IsA DesktopLabel Then ' Cast the control to a Label ' and gets its Text property labelText = DesktopLabel(aControl).Text MessageBox(labelText) End If Next As you can see, the code does not refer to any specific windows, Labels or anything else. Therefore, you can write this routine once and use it in any window in any project. .. _/getting_started/object-oriented_programming/advanced_oop_features/type_casting_rules: Type casting rules ****************** Generally speaking, type casts take a value and reinterpret it as some other type. The exact rules differ based off of the type of the value being cast. Below are some examples: * If the value being cast is an object, the value can be cast to different classes or class interface types. This will get type checked at runtime and raise a TypeMismatchException is it's an invalid cast. * If the value being cast is an Integer type, it can be cast to a Ptr, Color, or Enumeration. The restriction is that the size of the destination type be the same as the Integer. That is to say, a UInt16 can't be cast to a UInt8 or a Ptr. * If the value being cast is an Enumeration type, it can be cast to Integers of the same size as its underlying type. It is worth noting is that unless explicitly specified, the underlying type of an Enumeration is Integer. * If the value being cast is a Color, it can be cast to Int32 or UInt32. * If the value being cast is a Ptr, it can be cast to Integer, UInteger, Int32 (32-bit), UInt32 (32-bit), Int64 (64-bit), or UInt64 (64-bit). Everything else gives a compile time error and can't be cast. .. _/getting_started/object-oriented_programming/advanced_oop_features/static_variables: Static variables ---------------- A Static variable is a global variable that has the scope of a local variable. It is declared in a method and, unlike with a normal local variable, the value is retained the next time the method in which it is declared is called. This value is retained for the method in all instances of the class, whether they already exist or are created later. You declare a Static variable by using the :doc:`Static` keyword in place of :doc:`Var`: .. code:: xojo Static myValue As Integer The most common use of a Static variable is when you need set a value using a method that might take a noticeable time to execute. By using a Static variable, you can ensure the initialization is only done once, but the value is available for re-use each time the method containing it is called. .. _/getting_started/object-oriented_programming/advanced_oop_features/sorting_arrays_of_classes: Sorting arrays of classes ------------------------- It is common to have arrays of classes that need to be sorted in some manner. For example, you may have a Customer class that has a LastName property and you'd like to use that to sort an array of Customer classes. The standard array :ref:`Sort` method can only sort simple types (Text, Integer, etc.) so it cannot be used to sort a class. Fortunately there are a couple alternatives you can use. .. youtube:: hHtveLxJxyQ One way to do this is to use the :ref:`Arrays.SortWith` method and an additional array. What you do is create an all-new array of the simple type and copy the property values you want to sort to it. Then you use SortWith to sort the array with your class array. To take the Customer class as an example: .. code:: xojo ' customers() is a populated array of Customers, which have a LastName String property ' First, copy the LastName property to a new array Var lastNames() As String For i As Integer = 0 To customers.LastIndex lastNames.Add(customers(i).LastName) Next ' Now sort this new array with the customers array lastNames.SortWith(customers) ' You no longer need the lastNames() array The above technique works fine and is relatively easy to understand, but it does have the wasted step of copying the property value to its own array. So another alternative is to create your own comparison method and then tell the array :ref:`Sort` method to use your custom comparison method. First, you'll need to create your own comparison method. .. code:: xojo Function CustomerLastNameCompare(c1 As Customer, c2 As Customer) As Integer ' This assumes the array is populated with non-Nil Customers If c1.LastName > c2.LastName Then Return 1 If c1.LastName < c2.LastName Then Return -1 Return 0 End Function Now you can call the Sort method, but tell it to delegate sorting to your CustomerLastNameCompare method: .. code:: xojo ' customers() is a populated array of Customers, which have a LastName Text property customers.Sort(AddressOf CustomerLastNameCompare) Now the Customer array is sorted without having the overhead of creating a separate array. .. _/getting_started/object-oriented_programming/advanced_oop_features/see_also: .. seealso:: :doc:`Operator Overloading` functions; :doc:`Assigns`, :doc:`Static` commands ========== Interfaces ========== A class interface is a construct that you can use to tie together classes that do not share a super class but have something in common in your application. Class `interfaces` are used to specify what an object does without specifying how it does it. In order to understand class `interfaces` better, it's helpful to think of a class as consisting of two components, the (public) interface to the class and the implementation. The interface consists of the class's Public method calls and the implementation is the code that implements the methods. The interface says what the class does and the implementation says how it does it. In the object hierarchy, a subclass inherits both the interface and the implementation from its super class. That is, it gets both the method calls and the specific implementation of the method. Class `interfaces` enable you to separate the two constructs. If two or more classes need to do the same thing but do it in different ways, you use an interface instead of a super class. A class interface operates as a “spec” that contains a list of methods that custom classes in your project use. It does not actually contain any code for the methods themselves. The methods in the class interface are placeholders for methods that are actually contained in each custom class that “implements” the class interface. Also, a custom class can implement more than one class interface. The term “implement” means that the class has methods of the same names and declarations that are found in the class interface. The class interface specifies the methods and their declarations but not the code. Several class `interfaces` are built-in to the framework. You can implement any of these in your classes or add and implement your own. For example, the Iterator and Iterable `interfaces` specify methods that you can use to provide a way to iterate through the contents of a class by using the For Each...Next command. When you specify that a class implements a class interface, the class must implement all the methods in the class interface and the method declarations must match. However, the classes are free to implement the methods in different ways. For example, a method that changes the font in a Label would be implemented in a different way than a method that changes the font in a Canvas control that displays text via calls to the Graphics class. The process involves three basic steps: * Creating the class interface and its method definitions * Creating the classes that implement the class interface * Adding the classes to your project and calling the class interface methods in your program. Typically, that means writing generic code that tests whether a class implements a class interface and executing class interface methods where appropriate. To add a new class interface, click the Insert button on the toolbar and choose Class Interface or select Class Interface from the Insert menu. This adds a new class interface to the Navigator with the default name (Interface1 for the first class interface). Use the Inspector to change the name of the class interface. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/interfaces_add.png :scale: 50% :align: center You can only add methods to class `interfaces`. To add a method to a class interface, use the Add button on the Code Editor toolbar, Insert > Method from the menu, the contextual menu or the keyboard shortcut (:kbd:`Option Command M` on macOS or :kbd:`Ctrl Shift M` on Windows and Linux). You cannot specify any code in the class interface, so the Code Editor is disabled. Use the Inspector to specify method names and parameters. .. _/getting_started/object-oriented_programming/interfaces/implementing_the_interface_methods_in_a_class: Implementing the Interface methods in a class --------------------------------------------- To implement the methods of the class `interfaces`, you need to assign the :doc:`class interface` to a class. To do so, select the class in the Navigator and in the Inspector select the Choose button next to the `Interfaces` label. You can also use the “Implement Interface” option in the contextual menu of the method in the Navigator. When you click Choose, the Choose `interfaces` dialog displays showing the names of all the class `interfaces` available to you. Some of the `interfaces` are ones that are built-in and others are ones you created yourself. Select one or more `interfaces` to assign to the class. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/interfaces_class_interface_dialog.png :scale: 50% :align: center You can also select the “Include #pragma error” in the source of each method” to force a compiler error to be generated with the message “Don't forget to implement this method!". Remove the pragma after you have added code to implement the interface method. You can define the scope for the methods of an interface. When added to a class, all methods will automatically inherit this scope. Press OK to have the Code Editor automatically add the methods of the interface to your class with a comment telling you the interface to which it belongs (along with the optional pragma). If you modify or change the class interface after you have already applied it to a class, you will need to manually update the class. If you attempt to compile a project that contains a class with an interface, but the class does not implement all the interface methods, you get a compile error: “This class is missing one or more methods of an interface it implements.” .. important:: When calling a method via an Interface, the scope of the method (public, protected or private) is not considered. .. _/getting_started/object-oriented_programming/interfaces/modifying_and_deleting_interfaces: Modifying and deleting `interfaces` *********************************** If you change your mind and want to delete or replace an interface, you do so the same way you added the interface. When the Interface dialog appears, uncheck the `interfaces` you no longer want. Any methods that were added to the class while the interface was implemented are not modified or deleted when you remove the interface that they belonged to. That is, if you add an interface via the Implement `Interfaces` dialog, the methods that are specified by that interface remain as part of the class even if you delete the interface itself. You must take care of any “clean up” activities. .. _/getting_started/object-oriented_programming/interfaces/specifying_the_interface_being_implemented: Specifying the interface being implemented ****************************************** In rare cases you may find you need a class to implements multiple `interfaces` each with methods with the same signature (same name and parameters or no parameters at all). This will cause a compiler error because the compiler doesn't know for which interface the method has been implemented. Fortunately, there is a solution using the optional Implements field in the method declaration pane of the Inspector, available using the *Show Implements* contextual menu of the method. In this case, after selecting both `interfaces`, your class will have the duplicate method but with two signatures. First, rename one of the signatures so there is no longer duplication. Next, right-click (:kbd:`Control` click on macOS) on one of the signatures and choose *Show Implements* from the contextual menu. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/interfaces_show_implements.png :scale: 50% :align: center The Implements field will then appear in the Inspector. In this field, using the standard dot syntax, you can indicate the interface and method name that this signature is implementing. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/interfaces_implements.png :scale: 50% :align: center For example, if you had two `interfaces` (Foo and Bar) that both include a *Test* method, in the Implements field, you would indicate *Foo.Test* for one method and *Bar.Test* for the other. .. _/getting_started/object-oriented_programming/interfaces/creating_a_new_class_interface_from_an_existing_class: Creating a new class interface from an existing class ----------------------------------------------------- If an existing class in your project contains methods that you want to extract as a class interface, you can generate the new class interface directly from the Navigator. To do so, select “Extract Interface” from the contextual menu of the method in the Navigator. The new class interface is added to the project and the current class is made an implementor of the new interface. .. image:: https://documentation.xojo.com/getting_started/object-oriented_programming/images/interfaces_extract_interface.png :scale: 50% :align: center .. _/getting_started/object-oriented_programming/interfaces/polymorphism: Polymorphism ------------ Polymorphism is the ability to have completely different data types (typically classes) behave in a uniform manner. This can be done using class `interfaces`. .. _/getting_started/object-oriented_programming/interfaces/animal_example: Animal example ************** Here is how to implement the Animal, Dog and Cat example using class `interfaces`, like you previously saw using :ref:`inheritance with overridden methods` and :ref:`inheritance with events`. First, create a new class interface (called Animal). Add to it a *Speak* method that returns :doc:`String`. Now create a new class (Cat) and select Animal as its interface. The Code Editor automatically adds the Speak method for you. Add this code to it: .. code:: xojo Return "Meow!" Add another new class (Dog) and select Animal as its interface. The Code Editor adds the Speak method where you can put this code: .. code:: xojo Return "Woof!" To test this, create a button on a window. In the Pressed event of the button add code to create an array of Animals: .. code:: xojo Var animals() As Animal animals.Add(New Cat) animals.Add(New Dog) For Each a As Animal In animals MessageBox(a.Speak) Next When you run this code, you will see “Meow!” and then “Woof!”. Because Cat and Dog both implement Animal, they are allowed to be assigned to a variable (the animals array) with a type of Animal. And when you call the *Speak* method of Animal (in the loop), because of polymorphism, your code calls the *Speak* method of actual type (Dog or Cat) that was added to the array. .. _/getting_started/object-oriented_programming/interfaces/interface_aggregation: Interface Aggregation --------------------- Interface aggregation is the ability to group several `interfaces` together into a “super interface.” This is an advanced feature that you may not need to use often. Consider these `interfaces`: .. code:: xojo Interface Test Sub Foo() End Interface Interface Awesome Sub Bar() End Interface Interface Sweet Aggregates Test, Awesome Sub Blah() End Interface The Sweet interface is an aggregate of the Test and Awesome `interfaces`. In this example, there are three `interfaces` that contain a single method each. If a class were to implement the Test interface, then it would be required to implement just the method Foo. However, if the class were to implement the Sweet interface, then it would have to implement Foo, Bar and Blah. That's because Sweet aggregates both the Test and Awesome `interfaces`. Basically, when a class implements an interface, it must implement all of the methods from the interface, as well as methods from any aggregated `interfaces`. When a class implements an interface, then calling :doc:`IsA` on the class will return :doc:`True` for that interface as well as any aggregated `interfaces`. From the example above, a class implementing Sweet, then IsA Sweet, IsA Test and IsA Awesome are all :doc:`True` (since it essentially implements all three of the `interfaces`). This allows you the ability to combine `interfaces` in creative ways. Although `interfaces` don't necessarily relate to one another, there are times when you need something that satisfies the :doc:`IsA` command for multiple different `interfaces`. Or, when you want to merge functionality together for `interfaces`. For instance, say that you want to have an item which can be iterated over. A common interface might be: .. code:: xojo Interface Iterator Function Current() As Variant Function MoveNext() As Boolean Sub Reset() End Interface This allows you to iterate over the object in a generic fashion. However, it could very well be that creating this iterator will cause circular references, or some sort of memory management issues. In that case, you might want to use a disposable interface, like this: .. code:: xojo Interface Disposable Sub Dispose() End Interface In the example, you could have the Iterator interface aggregate the Disposable interface. This would guarantee that anything which can be iterated over can also be disposed of afterwards. You might be wondering “so why not just put the Dispose method right onto the Iterator interface?” That's a perfectly legitimate way to accomplish the same goal — however, Iterator and Disposable aren't the same conceptually. So it doesn't make sense to force the two `interfaces` together in such a fashion. It doesn't hurt, but it just doesn't help either. Next is an example that would hurt. Say you have a method that is going to generically take a parameter which represents something that can read and write to or from a source. In this case, you want something that's both Readable and Writeable. You could just declare the method like this: .. code:: xojo Sub DoSomethingAwesome(foo As Readable) If foo IsA Writeable Then ' Do reading and writing End If End Sub This code will compile and will work, but it's also not safe. The user could pass in something which is Readable, but not Writeable and it will compile. Instead, the user could easily do this instead: .. code:: xojo Interface ReadableAndWriteable Aggregates Readable, Writeable End Interface Sub DoSomethingAwesome(foo As ReadableAndWriteable) End Sub Now there will be a compile error if the item doesn't implement both `interfaces`. That's a much better solution to the problem. .. _/getting_started/object-oriented_programming/interfaces/see_also: .. seealso:: `Object-Oriented Programming Concepts `_, `Advanced Object-Oriented Programming Concepts `_ (with information on `Interfaces`) videos Examples ======== .. rst-class:: forsearch Xojo projects Reviewing and experimenting with example projects is another great way to learn. There are hundreds of them available right in Xojo. You can access them in Xojo by choosing File > New Project and then clicking on Examples in the New Project window. .. image:: https://documentation.xojo.com/getting_started/example_projects/images/example_projects.png .. tip:: The examples in this dialog box are loaded via the Internet. If you need access to them when you're not connected to the Internet, you can `download `_ them or click the **Download Examples** button on the Examples pane of the Project Chooser. You can search the examples by name. Selecting an example will display information about it in the pane to the right. .. image:: https://documentation.xojo.com/getting_started/example_projects/images/search_example_projects.png You can also limit your search to examples for a specific kind of project and platform (Android, iOS, Linux, macOS, Raspberry Pi, Windows). .. image:: https://documentation.xojo.com/getting_started/example_projects/images/filter_example_projects.png While most examples are designed to show off a specific feature, the :doc:`Eddie's Electronic's` example project is a full application. Complete list of included example projects ------------------------------------------ .. The section below is added via the Examples Page function in the Dox app. .. The function assumes below this section on the page. The following example projects are available in Xojo via the Project Chooser window. .. collapse:: Application Structure .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Handling Termination Signal in a Console App","Handling an external termination signal:e.g. kill -s TERM .","Console","macOS" "Interacting with the Console","Creates an interactive console application.","Console","All" .. collapse:: Code Execution .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Making a Class Iterable","How to use a Class in a loop. Uses a DesktopListBox control.","Desktop","All" "Pointing Events at Methods","How to use methods to respond to events.","Desktop","All" "Evaluator","Use XojoScript to evaluate simple expressions.","Desktop","All" "Graphics Context Example","Use XojoScript to draw to a Graphics object.","Desktop","All" "ScriptImaging","Create and manipulate images using XojoScript.","Desktop","All" "XojoScript","Run simple XojoScript a graphical user interface.","Desktop","All" .. collapse:: Communication .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Line State Change Tester","Visualise serial port line state changes using the SerialConnection class.","Desktop","All" "Serial Device Bar Code Reader Example","Read bar codes using the SerialConnection class.","Desktop","All" "Serial Line Indicator","Control serial port lines using the SerialConnection class.","Desktop","All" "IPCSocket","Communicate between applications using the IPCSocket class.","Desktop","All" "Sending Email from a Console App","Send an email from a console application using the SMTPSecureSocket class.","Console","All" "WebServer","A simple Web server using the ServerSocket and TCPSocket classes.","Desktop","All" "WebServerWindowsService","A simple Web server using the ServiceApplication, ServerSocket and TCPSocket classes.","Console","Windows" "Cats","Display pictures of cats.","Mobile","iOS" "EddiesWebService","Implement a simple Web service API. Get more information `here `_.","Web","All" "Feedbin","Access `Feedbin `_ using the Feedbin API and the URLConnection class.","Desktop","All" "RESTy","Implement a `REST `_ API, uses the URLConnection class.","Desktop","All" "Slack","Access `Slack `_ via the Slack API. This example requires a Slack Authentication Token.","Desktop","All" "TwilioAnswerCall","Receive Twilio phone calls using the App.HandleURL event.","Web","All" "TwilioSMS","Receive Twilio SMS messages using the URLConnection class:Requires a Twilio Test Credential (see Notes in the project).","Desktop","All" "YouTube","Display a YouTube using the DesktopHTMLViewer class.","Desktop","All" .. collapse:: Cryptography .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Crypto","Demonstrates the generation of hashes using the Crypto module.","Desktop","All" "RSA","Demonstrates RSA Public and Private key generation using the Crypto module.","Desktop","All" .. collapse:: Databases .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "DBKit-Desktop","Demonstrates the DBKit module for creating database clients for the desktop.","Desktop","All" "DBKit-Web","Demonstrates the DBKit module for creating database clients for the web.","Web","All" "MySQLDatabase","Connect to and use a MySQL database using the MySQLCommunityServer class.","Desktop","All" "Accessing a Firebird Database","Connect to and use a Firebird database using the ODBCDatabase class.","Desktop","All" "GetDataSourceNames","Display the ODBC data sources defined on the local computer.","Desktop","All" "ODBCDatabase","Connect to and use a Microsoft Access database using the ODBCDatabase class.","Desktop","All" "LargeObjects","Demonstrate the use of the PostgreSQLDatabase.CreateLargeObject method.","Desktop","All" "Listen","Demonstrate the use of the PostgreSQLDatabase.Listen method. Paired with the Notify example project.","Desktop","All" "Notify","Demonstrate the use of the PostgreSQLDatabase.Notify method. Paired with the Listen example project.","Desktop","All" "PostgreSQL","Connect to and use a PostgreSQL database using the PostgreSQLDatabase class.","Desktop","All" "Backup Example","Demonstrate synchronous and asynchronous backup of an SQLite database.","Desktop","All" "Full-Text Searching 4","Demonstrate the use of the SQL 'Match' statement.","Desktop","All" "Full-Text Searching 5","Demonstrate the use of the SQL 'Match' and 'ORDER BY' statements.","Desktop","All" "SQLite (Desktop)","Connect to and use an SQLite database using the SQLiteDatabase class.","Desktop","All" "SQLite (Web)","Connect to and use an SQLite database using the SQLiteDatabase class.","Web","All" "SQLite Prepared Statement","Demonstrates using prepared statements in SQLite.","Desktop","All" "Storing Pictures","Store pictures in an SQLite database using the 'Blob' column type.","Desktop","All" "Working with BLOBs","All. Create and manipulate the SQLite database 'Blob' column type.","Desktop","All" .. collapse:: Declares .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "DeclareGetPID","Use the Xojo 'Soft Declare' statement to get a Process Identifier on each desktop platform.","Desktop","All" "WindowOpacity","Use Xojo 'External Methods' to set the transparency of the desktop application window.","Desktop","All" .. collapse:: Design Patterns .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Factory","The `Factory `_ pattern.","Desktop","All" "Interpreter","Demonstrates the `Interpreter design pattern `_.","Desktop","All" "Lazy Initialization","Demonstrates the `Lazy Initialization `_ design pattern.","Desktop","All" "Observer","Demonstrates the `Observer `_ design pattern.","Desktop","All" "Singleton","Demonstrates the `Singleton `_ design pattern.","Desktop","All" .. collapse:: Files .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Copy and Move","Create a file using the TextOutputStream class. Move and copy using FolderItem.MoveTo and FolderItem.CopyTo.","Desktop","All" "Drag To Desktop","Create a desktop file using drag and drop.","Desktop","All" "File Browser","A simple file browser using the FolderItem class and a DesktopListBox control.","Desktop","All" "File Search","Search a folder for files containing the specified text.","Desktop","All" "FileTypeGroup","Demonstrate the use of a FileTypeGroup to create text files with a specified file extension.","Desktop","All" "PNG FileType","Demonstrates opening and creating PNG image files using the FolderItem and Picture classes.","Desktop","All" "Folder Info","Display folder information using FolderItem methods.","Desktop","All" "FolderItem Dialogs","Demonstrate the use of OpenFileDialog, SaveFileDialog and SelectFolderDialog.","Desktop","All" "Reading and Writing Binary Data","Demonstration of creating, reading and writing binary data files using the FolderItem and BinaryStream classes.","Desktop","All" "SpecialFolder Paths","Display the folder paths available via SpecialFolder.","Desktop","All" .. collapse:: Getting Started .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Data Types","Demonstrating some of the data types available in Xojo.","Desktop","All" "Enumerations","Demonstrating how enumeration types are used in Xojo.","Desktop","All" "Parameter Options","Demonstrating how method parameters are used in Xojo.","Desktop","All" "Sort Algorithms","Implementing the Bubble and Merge sort algorithms in Xojo.","Desktop","All" .. collapse:: Graphics .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Animation","Animate a bouncing soccer ball. Uses a DesktopCanvas control and the Timer class.","Desktop","All" "Wavy Text","Text animation. Uses a DesktopCanvas control and the Timer class.","Desktop","All" "DesktopOpenGLSurface","Simulate a two dimensional fluid field using the OpenGLSurface class.","Desktop","All" "GraphicsPath","Draw card deck suit shapes using the GraphicsPath class.","Desktop","All" "Graphing Sign Waves","Draw sine waves using a DesktopCanvas control and the Graphics class.","Desktop","All" "Interactive Grid","Draw a user defined grid using a DesktopCanva control and its built-in Graphics class.","Desktop","All" "Objects in a Canvas","Display and move two dimensional objects on a DesktopCanvas control.","Desktop","All" "Object2D Text Rotation","Rotate text using the Object2D class.","Desktop","All" "Saving and Load Vector Graphics","Saving and loading vector graphics using the Picture class.","Desktop","All" "Sparkler","Draw animated particles using a DesktopCanvas control and the Picture class.","Desktop","All" "System Colors","Display the current System colours using the Color class.","Desktop","All" .. collapse:: IDE .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Code Assistant Tester","Use this to test and debug your Xojo IDE Code Editor Code Assistants.","Desktop","All" "IDECommunicator","Automate the Xojo IDE from the command line.","Console","All" .. collapse:: Language .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "ActionSource","Demonstrate the use of the ActionSource and ActionNotificationReceiver interfaces.","Desktop","All" "Arrays","Demonstrate a simple array and an array with elements containing a class.","Desktop","All" "Assigns","Demonstrate the use of the 'Assigns' keyword in method parameters.","Desktop","All" "Delegate","Demonstrate the use of Delegates in a sorting application.","Desktop","All" "Dictionary","Demonstrates the use of the Dictionary data type.","Desktop","All" "Extension Methods","Demonstrates the ability of Xojo to extend the functionality of data types and classes.","Desktop","All" "Faster String Appending with MemoryBlock","Demonstrates the use of MemoryBlocks to improve the speed of appending strings.","Desktop","All" "Introspection","Demonstrates the ability of Xojo to inspect the runtime instances of classes within an application.","Desktop","All" "Operator Lookup","Dynamically access class methods.","Desktop","All" "Operator Overloading","How to implement custom operators. This example uses Operator_Compare to provide a customised comparator.","Desktop","All" "Semaphore Example","Control access to resources in a multithreaded application.","Desktop","All" "Sorting Arrays","Sorts an array of classes (DateTime) using the customisable DesktopListBox.Sort method.","Desktop","All" "Using Attributes","Using IDE attributes to manage code maintenance and visibility.","Desktop","All" "Using a Timer in a Console App","Using the Timer class in a Console application.","Console","All" "PictureResizer","Demonstrates a way to utilise multiple CPU cores.","Desktop","All" "WordCounter","Demonstrates a way to utilise multiple CPU cores.","Desktop","All" .. collapse:: Networking .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "AutoDiscovery","Uses the AutoDiscovery class to find another application, on the same local network, to communicate with.","Desktop","All" "NetworkInterface","Uses the NetworkInterface class to display connection information.","Desktop","All" "ServerSocket Client","A simple client application to communicate with ServerSocket Server. Used the TCPSocket class.","Desktop","All" "ServerSocket Server","A server to handle message to and from ServerSocket Client. Uses the ServerSocket and TCPSocket classes.","Desktop","All" "TCPSocket","Connect to a web site using the TCPSocket class.","Desktop","All" "UDP Multicast","Use the UDPSocket class to communicate with other applications on the same local network.","Desktop","All" "UDPSocket","A simple chat application. Uses the UDPSocket class.","Desktop","All" "Downloading Files","Download a picture file from the Internet. Uses the URLConnection class.","Desktop","All" "Using GET with URLConnection","Retrieve information from a website. Uses the URLConnection class.","Desktop","All" "Using POST with URLConnection","Send information to a website. Uses the URLConnection class.","Desktop","All" .. collapse:: OS .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Get Default Control Size","Adjust desktop control sizes. Does not use the IDE 'Normal Control Sizes' switch.","Desktop","Linux" "Load a Theme","Demonstrates how to load a GTK theme for a desktop application.","Desktop","Linux" "LEDBlinker-Console","Flash a light.","Console","Raspberry Pi" "LEDButton-Console","Control an light with a button.","Console","Raspberry Pi" "WiringPi-GPIO","Demonstrates using the WiringPi library to control lights, speakers and LCD displays. Uses a Console project.","Console","Raspberry Pi" "WiringPi-UI","Demonstrates using the WiringPi library to control lights and speakers. Uses a Desktop project.","Desktop","Raspberry Pi" "Creating a Custom Window Shape with Declares","Demonstrates using Declares to create a customised window shape.","Desktop","Windows" "Declare Example - CPU Usage","Demonstrates using Declares to create a CPU idle time monitor.","Desktop","Windows" "Embedding a Windows Native Control","Add Win32 controls to a Xojo application. Example embeds a ControlLink into a Desktop application.","Desktop","Windows" "OLE Automation Tests","Demonstrates OLE Automation to create a Word document. Needs the Word application installed to work.","Desktop","Windows" "Excel Automation","Access and control Excel via Xojo code. Requires the MSOfficeAutomation plugin.","Desktop","Windows" "PowerPoint Automation","Access and control PowerPoint via Xojo code. Requires the MSOfficeAutomation plugin.","Desktop","Windows" "Word Automation","Access and control Word via Xojo code. Requires the MSOfficeAutomation plugin.","Desktop","Windows" "ButtonStyling.xojo_binary_project","Demonstrates various button styles.","Desktop","Windows" "ButtonStyling","Demonstrates various button styles.","Desktop","Windows" "DifferentViews","Demonstrates various list-style controls.","Desktop","Windows" "InputFields","Demonstrates various input fields.","Desktop","Windows" "Layouts","Demonstrates various layouts.","Desktop","Windows" "MenuBar","Demonstrates creating a menu bar.","Desktop","Windows" "TabViewAndWebView","Demonstrates a tab control and a web viewer control.","Desktop","Windows" "XAMLGallery","Demonstrates a large number of varying controls.","Desktop","Windows" "XAMLSubclasses","A variety of other controls including a calendar, rating, and more.","Desktop","Windows" "Accessing the Tabbar","Demonstrates access to the TabBar via code.","Mobile","iOS" "Add and Edit Layout Constraints from code","Managing control constraints via code.","Mobile","iOS" "Auto-Size Label","Demonstrates auto-resizing a label to fit the text it contains.","Mobile","iOS" "Proportional Spacing without code","Demonstrates using control constraints to maintain spacing on a rotable device.","Mobile","iOS" "Switching Constraints","Managing collections of constraints. Uses the iOSLayoutConstraint class.","Mobile","iOS" "Clipping Graphics","Draw clipped graphics. Uses the MobileCanvas control and its built-in Graphics class.","Mobile","iOS" "Graphing Sine Waves","Draw user-defined sine waves. Uses the MobileCanvas control and its built-in Graphics class.","Mobile","iOS" "Notification Categories","Demonstrates notificiations for iOS applications. Uses the MobileNotifications class.","Mobile","iOS" "Simple Notification","Introduction to notifications for iOS applications. Uses the MobileNotifications class.","Mobile","iOS" "Xojo Cloud Remote Notification Server","A simple iOS application notification server.","Web","iOS" "Rotating Graphics","Simple animated graphic. Uses the MobileCanvas control and its built-in Graphics class.","Mobile","iOS" "Screen Navigation","Navigating screens on different types of mobile device.","Mobile","iOS" "Sharing Documents folder with The Finder","How to share the Documents folder between a mobile device and a Mac.","Mobile","iOS" "Shortcuts","Managing application shortcuts using the IDE Entitlements editor.","Mobile","iOS" "ShowModal","Demonstrates the use of Modal screens.","Mobile","iOS" "TabBar","Managing the application TabBar.","Mobile","iOS" "Adding Sections","Demonstrates adding sections to an iOSMobileTable control via code.","Mobile","iOS" "Controlling Row Selection","Demonstrates selectable table rows using the iOSMobileTable control.","Mobile","iOS" "Custom Cell Dynamic Height","Demonstrates how row height can change fit content at runtime using the iOSMobileTable control.","Mobile","iOS" "Custom Cells and Scrolling","Demonstrates row customisation using the iOSMobileTable control.","Mobile","iOS" "Table Actions","Demonstrates assigning actions to an iOSMobileTable control using the iOSMobileTableDataSource interface.","Mobile","iOS" "Table Cell Sizes","Demonstrates how iOSMobileTable cell space can be dynamically allocated at runtime.","Mobile","iOS" "Table DataSource via Class","Demonstrates the iOSMobileTableDataSource interface.","Mobile","iOS" "Table DataSource via Database","Connecting an SQLite database and an iOSMobileTable control.","Mobile","iOS" "Table Row Selection via Code","Demonstrates iOSMobileTable row selection via code.","Mobile","iOS" "Table editing and Reordering","Demonstrates editing and reordering of iOSMobileTable cells.","Mobile","iOS" "Table with Detail View","Demonstrates how to display details from an iOSMobileTable control.","Mobile","iOS" "URL Schemes","Demonstrates how to enable an application to respond to a URL Schema.","Mobile","iOS" "User Authentication","Demonstrates how to authenticate a user. Uses the UserAuthentication class.","Mobile","iOS" "External Methods","Demonstrates the use of External Methods by displaying fonts and font information.","Desktop","macOS" "Soft Declares","Demonstrates the use of Soft Declares by modifying window properties.","Desktop","macOS" "Using AppleScripts in Xojo","Demonstrates accessing AppleScripts via Xojo code.","Desktop","macOS" .. collapse:: PDF .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Adding Links to PDF files","Add URL links to a PDF document. Uses the PDFDocument class.","Desktop","All" "Adding the ability to sign a document","Sign PDF document with a digital certificate. Uses the PDFDocument class.","Desktop","All" "Attaching Files","Embed a file within a PDF document. Uses the PDFDocument class.","Desktop","All" "Callouts","Add a Callout annotation to a PDF document. Uses the PDFDocument class.","Desktop","All" "Lines","Add a Line annotation to a PDF document. Uses the PDFDocument class.","Desktop","All" "Sounds","Embed a sound file within a PDF document. Uses the PDFDocument class.","Desktop","All" "Text","Add Text annotations to a PDF document. Uses the PDFDocument class.","Desktop","All" "`Video`","Embed a `video` file within a PDF document. Uses the PDFDocument class.","Desktop","All" "Brushes","Draw using graphical brushes in a PDF document. Uses the PDFDocument class.","Desktop","All" "Creating PDF Documents from iOS","Create a PDF document on an iOS device. Uses the PDFDocument class.","Mobile","iOS" "Creating PDF Documents from the Desktop","Create a PDF document from a desktop application. :Uses the PDFDocument class.","Desktop","All" "Creating PDF forms","Creating PDF forms via Xojo code. Uses the PDFDocument class.","Desktop","All" "Password Protection","Password protect and encrypt a PDF document. Uses the PDFDocument class.","Desktop","All" "Table of Contents","Create a PDF document table of contents. Uses the PDFDocument class.","Desktop","All" "Using Transparency","Demonstrates multiple transparent layers in a PDF document. Uses the PDFDocument class.","Desktop","All" .. collapse:: Platforms .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Calculator","A calculator app.","Mobile","Android" "Taskmanager","The completed example from the Android tutorial.","Mobile","Android" "Tipcalculator","A tip calculator app.","Mobile","Android" "Xojodraw","A drawing app.","Mobile","Android" "Container","Demonstrates using a container.","Mobile","Android" "Controlthemes","Demonstrates using themes with controls.","Mobile","Android" "Datetimepicker","Demonstrates the DateTimePicker control.","Mobile","Android" "Htmlviewer","Demonstrates the HTMLViewer control.","Mobile","Android" "Imagepicker","Demonstrates the ImagePicker.","Mobile","Android" "Location","Demonstrates getting the user's location.","Mobile","Android" "Messagebox","Demonstrates the MessageBox method.","Mobile","Android" "Modalscreen","Demonstrates creating a modal screen.","Mobile","Android" "Movieplayer","Demonstrates the MoviePlayer control.","Mobile","Android" "Progress","Demonstrates the ProgressBar control.","Mobile","Android" "Samplecontrols","Demonstrates several different controls.","Mobile","Android" "Scrollablearea","Demonstrates using a ScrollableArea.","Mobile","Android" "Sharingpanel","Demonstrates using the SharingPanel.","Mobile","Android" "Slider","Demonstrates the Slider control.","Mobile","Android" "Switch","Demonstrates the Switch control.","Mobile","Android" "Tabpanel","Demonstrates a tabbed interface.","Mobile","Android" "Table","Demonstrates the AndroidMobileTable control.","Mobile","Android" "Textfield","Demonstrates the TextField control.","Mobile","Android" "`Toolbar`","Demonstrates using a `toolbar`.","Mobile","Android" "Sqllite","A simple SQLite example.","Desktop","All" "Sqliteinmemory","Demonstrates creating a SQLite in-memory database.","Desktop","All" "Sqliteversion","Demonstrates how to get the version number of SQLite.","Desktop","All" "Buttonbackgroundcolor","Shows how to use a declare to set a button's background color.","Mobile","Android" "Declare","A basic declare example.","Mobile","Android" "Chime","Playing a sound.","Mobile","Android" "Dictionary","Accessing a dictionary.","Mobile","Android" "Folderitem","Accessing files via the FolderItem class.","Mobile","Android" "Introspection","Demonstrates using Introspection.","Mobile","Android" "Memoryblock","Demonstrates using a MemoryBlock.","Mobile","Android" "Thread","Demonstrates threads.","Mobile","Android" "Appversion","Demonstrates getting the app version.","Mobile","Android" "Analogclock","Demonstrates drawing an analog clock.","Mobile","Android" "Canvasbounce","Shows how to use the Canvas control to do simple animation.","Mobile","Android" "Canvasdrawpicture","Demonstrates drawing picture files in a Canvas.","Mobile","Android" "Canvasrotation","Demonstrates shape rotation animation in a Canvas.","Mobile","Android" "Canvastext","Demonstrates drawing user-entered text in a Canvas.","Mobile","Android" "Graphicsclip","Demonstrates clipping graphics in a Canvas.","Mobile","Android" "Graphicspath","Demonstrates using a GraphicsPath to draw shapes in a Canvas.","Mobile","Android" "Imageset","Demonstrates accessing images in an ImageSet and then drawing them into a Canvas.","Mobile","Android" "Picturedrawing","Drawing a simple picture in a Canvas.","Mobile","Android" "Sinewaves","Drawing sine waves in a Canvas control.","Mobile","Android" "Systemimageicons","Accessing system image icons. There are over 2400 for Android included with Xojo.","Mobile","Android" "Turtle","A simple Frogger-style 2D graphics game using the Canvas control.","Mobile","Android" "Catsup","Demonstrates using the URLConnection class to get random pictures of cute kitties from the Internet.","Mobile","Android" "Feedbin","Demonstrates basic username/password authorization via feedbin.com.","Mobile","Android" "Urlconnectionget","Demonstrates doing a GET via the URLConnection class.","Mobile","Android" "Urlconnectionpost","Demonstrates performing a POST via the URLConnection class.","Mobile","Android" "BevelButton","Demonstrate the desktop BevelButton control and some of its properties.","Desktop","All" "Dice","Demonstrates drawing simple graphics with the DesktopCanvas control.","Desktop","All" "Drag and Drop","Demonstrates selecting, dragging and dropping a graphic using the DesktopCanvas control.","Desktop","All" "Drawing Text","Demonstrates drawing text using the DesktopCanvas control.","Desktop","All" "Interactive Canvas","Demonstrates interactive graphics using the DesktopCanvas control.","Desktop","All" "Scrolling","Demonstrates scrolling a picture within the confines of a DesktopCanvas control.","Desktop","All" "Zooming","Demonstrates zooming a picture within the confines of a DesktopCanvas control.","Desktop","All" "Chartdesktopexample","Demonstrate the DesktopChart control. Also demonstrates saving charts using PDFDocument class.","Desktop","All" "Clipboard","Demonstrating sending and getting data from the Clipboard class.","Desktop","All" "ColorPicker","Demonstrates selecting colours using the DesktopColorPicker control.","Desktop","All" "Container","Demonstrate different uses of the DesktopContainer control.","Desktop","All" "Control Sets","Demonstrates the concept of control sets by creating a user-defined number of DesktopButtons.","Desktop","All" "CalendarWindow","Demonstrates creating a custom control by using a DesktopWindow.","Desktop","All" "CanvasButton","Demonstrates creating a custom control by using a DesktopCanvas control.","Desktop","All" "LinkLabel","Demonstrates creating a custom control by using a DesktopLabel control.","Desktop","All" "OKCancelContainer","Demonstrates a self-modifying control using a DesktopContainer. Alters the order of DesktopButtons depending on the current platform.","Desktop","All" "Built-In Dialogs","Demonstrates the built-in dialogs.","Desktop","All" "Input Dialog","Demonstrates a user input dialog.","Desktop","All" "Modal Dialog","Demonstrates a modal dialog.","Desktop","All" "DownloadContainer","Demonstrates the concept of control sets by creating contols in response to a user action.","Desktop","All" "Drag and Drop Pictures","Demonstrates dragging and dropping a picture to and from a Xojo application.","Desktop","All" "Drawing Fire","Draw and animate a simple fire effect. Uses a DesktopCanvas control and a Timer class.","Desktop","All" "Drawing Gradients","Demonstrates shadow, linear and radial gradient effects.","Desktop","All" "Drawing an Analog Clock","Draw and animiate an analog clock. Uses a DesktopCanvas control and a Timer class.","Desktop","All" "Drawing with Threads","Demonstrates drawing complex graphics using threads to improve performance.","Desktop","All" "GroupBox","Demonstrates DesktopGroupBox (and DesktopRadioButton).","Desktop","All" "HTMLViewer-ExecuteJavaScript Test","Demonstrates the DesktopHTMLViewer control. Also demonstrates the use of JavaScript.","Desktop","All" "Drag Between Cells","Drag cells within a DesktopListBox control.","Desktop","All" "Drag and Drop Between ListBoxes","Drag and drop rows between DesktopListBox controls.","Desktop","All" "Dragging Events","Demonstrates the DesktopListBox dragging events.","Desktop","All" "Dragging and Dropping Files","Drag files to a DesktopListBox control.","Desktop","All" "Export","Export the contents of a DesktopListBox to a text file. Also demonstrates the use of an extension method.","Desktop","All" "FileBrowser","Implements a simple file browser using the DesktopListBox control. Also demonstrates subclassing.","Desktop","All" "Grid","Implement a simple grid using a DesktopListBox control.","Desktop","All" "IconGrid","Demonstrate runtime addition and removal of DesktopListBox rows and columns.","Desktop","All" "Lazy Loading","Demonstrate a method for loading large amounts of data into a DesktopListBox control.","Desktop","All" "ListBox","Demonstrate DesktopListBox features.","Desktop","All" "Property List","Demonstrate the types rows available with a DesktopListBox control.","Desktop","All" "Source List","Implement a simple SourceList user interface using the DesktopListBox control. Uses expanable rows.","Desktop","All" "Creating a Contextual Menu","Demonstrates creating a contextual menu.","Desktop","All" "Font Menu","Demonstrates creating and selecting an item from a menu. Also demonstrates the System.FontAt function.","Desktop","All" "Open Recent Menu","Demonstrates how to create and maintain an Open Recents file menu.","Desktop","All" "Window Menu","Demonstrates how to use menus to create and switch between windows.","Desktop","All" "MoviePlayer","Create a simple movie player using the DesktopMoviePlayer control.","Desktop","All" "NotePlayer","Demonstrates playing sounds using the DesktopNotePlayer control.","Desktop","All" "Play Folder of Movies","Demonstrates how to play multiple movies using the DesktopMoviePlayer control.","Desktop","All" "ProgressBar","Demonstrates the normal and indeterminate types of DesktopProgressBar.","Desktop","All" "ScrollBar","Demonstrates horizontal and vertical DesktopScrollBar controls.","Desktop","All" "SegmentedButton","Demonstrates the DesktopSegmentedButton control. Example demonstrates adding and removing segments at runtime.","Desktop","All" "TextField Resizing","Demonstrates proportional resizing of the DesktopTextField.","Desktop","All" "Thread","Demonstrates using multiple instances of the Thread class in a desktop project. Also demonstrates using a Timer control to update the user interface.","Desktop","All" "Dynamic Desktop Toolbars","Demonstrates runtime modification of the DesktopToolbar control.","Desktop","All" "`Toolbar`","Demonstrates creating and interacting with the DesktopToolbar control.","Desktop","All" "TrayExample","Demonstrates using the System tray on Windows and Linux.","Desktop","Windows/Linux" "Using a Thread","Demonstrates using the Thread class to update a graphical user interface.","Desktop","All" "Using a Timer","Demonstrates using the Timer control to update a graphical user interface.","Desktop","All" "Access the Clipboard","Send and retrieve data using the Clipboard class.","Mobile","iOS" "Adding Controls Dynamically","Add and remove contols at runtime.","Mobile","iOS" "Available Fonts","Use a MobileCanvas to display a list of available fonts.","Mobile","iOS" "Chart","Demonstrate using the MobileChart control.","Mobile","iOS" "Using a SQLite in-memory database","Demonstrates using an in-memory SQLite database in an iOS application.","Mobile","iOS" "DatePicker","Demonstrate using the MobileDataTimePicker control.","Mobile","iOS" "Fonts","Display a short list of fonts. Uses a MobileLabel control to display each font .","Mobile","iOS" "HTMLViewer","Create a simple web browser using the MobileHTMLViewer.","Mobile","iOS" "ImagePicker","Create a simple picture viewer using the MobileImageViewer.","Mobile","iOS" "Location","Create a simple location display application using the MobileLocation control.","Mobile","iOS" "MessageBox","Demonstrate using the MobileMessageBox.","Mobile","iOS" "Motion","Demonstrate using the MobileMotion class. The iOSSimulator does not simulate device motion.","Mobile","iOS" "Other iOS Dialogs via Declares","Demonstrate miscellaneous iOS dialog boxes.","Mobile","iOS" "PDFViewer Control","Demonstrates displaying a PDF file via PDFViewer control.","Mobile","iOS" "Progress","Demonstrate using the MobileProgressBar control.","Mobile","iOS" "SQLiteDatabase","Demonstrate using the SQLiteDatabase class.","Mobile","iOS" "ScrollableArea","Demonstrate using the MobileScrollableArea control.","Mobile","iOS" "SegmentedButton","Demonstrate using the MobileSegmentedButton control.","Mobile","iOS" "Set the Badge Number","Demonstrates using the MobileApplication.IconBadgeNumber property. Also uses the NotificaionCenter module.","Mobile","iOS" "SharingPanel","Demonstrate using the MobileSharingPanel control.","Mobile","iOS" "Swiping","Demonstrate detecting swiping actions using the MobileCanvas control.","Mobile","iOS" "Switch","Demonstrate using the MobileSwitch control.","Mobile","iOS" "TabBar","Using the TabBar. Uses iOSLayout.","Mobile","iOS" "Threads and priority","Demonstrates the use of the Thread class on iOS devices.","Mobile","iOS" "`Toolbar`","Demonstrates using the `toolbar` on iOS devices. Uses the MobileToolbarButton control.","Mobile","iOS" "Touch Events in a Canvas","Demonstrates touch events using the MobileCanvas control.","Mobile","iOS" "iOSMobileTable Search Filters via Declares","Demonstrate the use of declares to create and use a Search field for an iOSMobileTable.","Mobile","iOS" "iOSTable","Demonstrate using the iOSMobileTable control.","Mobile","iOS" "Canvas Redraw Speed","Demonstrates server to client drawing speed. Uses the WebCanva control.","Web","All" "Chart","Demonstrates the WebChart control.","Web","All" "Communicating between Sessions","Demonstrates sending and receiving messages between different web sessions.","Web","All" "Container Control","Demonstrates using the WebContainer control.","Web","All" "Dynamic Container Control","Demonstrates modifying WebContainer controls at runtime.","Web","All" "Scrolling Container","Demonstrates a scrollable list of WebContainers.","Web","All" "WebGrid Container","Demonstrates creating a dynamic grid of WebContainers.","Web","All" "Creating Contextual Menus","Demonstrates creating and using contextual menus. Used the WebMenuItem control.","Web","All" "Custom ProgressWheel","Drawing a custom progress wheel control.","Web","All" "Custom ProgressWheel","Demonstrates a large customised WebProgressWheel.","Web","All" "Dialog Box","Demonstrates the use of (asynchronous) WebDialog control.","Web","All" "Downloading Files","Demonstrates download files from the Internet.","Web","All" "Downloading","Demonstrates dynamic and static downloading of file from a web server. Uses the WebFile class.","Web","All" "Dynamic Dialog Box","Demonstrates creating and using a dynamic dialog. Uses the WebDialog control.","Web","All" "WebListBox Cell Renderers","Demonstrates drawing pictures into the cells of a WebListBox.","Web","All" "Listbox with a Datasource","Demonstrates populating a WebListBox from an SQLite database.","Web","All" "Simple ListBox","Demonstrates a simple WebListBox.","Web","All" "Making a Session Timeout Due to Inactivity","Demonstrates how to control a session timeout. Uses the WebSession.UserTimeout property.","Web","All" "Managing User Login","Demonstrates managing user authentication.","Web","All" "HTMLViewerPrinting","Demonstrates printing from a web application. Uses the WebHTMLViewer.Print method.","Web","All" "Progress","Demonstrates the WebProgressBar control.","Web","All" "Providing a WebService","Demonstrates the provision of a web service via the WebApplication.HandleURL event.","Web","All" "Reconnecting to a WebSessions","Demonstrates how to re-connect a Shell called asynchronously to the WebSession that started it when the Shell returns.","Web","All" "ThreadProgress","Demonstrates using the Thread class to control a modal dialog.","Web","All" "Timer","Demonstrates the WebTimer control.","Web","All" "Updating WebCanvas Graphics via a Timer","Demonstrates controlling a WebCanvas via a WebTimer.","Web","All" "Using Google Fonts","Demonstrates how to access Google fonts via the IDE App HTML Header field.","Web","All" "Using Hashtags in URLs","Demonstrates using HashTags.","Web","All" "WebFileUploader","Demonstrates using the WebFileUploader class.","Web","All" "WebImageViewer SVG","Demonstrates a simple viewer for SVG image files. Uses the WebImageViewer control.","Web","All" "WebMoviePlayer","Demonstrates using the WebMoviePlayer control.","Web","All" "CustomButton","Demonstrates using the WebSDK to construct a customised button.","Web","All" "Web_SDK_Custom_Select_Control","Demonstrates constructing a complex web control (similar to WebPopupMenu).","Web","All" "Gravatar","Demonstrates constructing a control using Internet resources. Uses resources provided by Gravatar.com.","Web","All" "SDK Examples","Demonstrates constructing visual and non-visual controls using the WebSDK.","Web","All" "WebSDKIconConverter","Utility application to convert a picture into Base64 text.","Desktop","All" "Button Cell","Demonstrates embedding controls within a WebListBox.","Web","All" "Color Cell","Demonstrates drawing within the celss of a WebListBox. Uses the WebListboxCellRenderer class.","Web","All" "WebToolbar","Demonstrates using the WebToolbar control.","Web","All" "WebToolbarMenu","Demonstrates using the WebMenuItem class.","Web","All" .. collapse:: Printing .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "CustomerList","Demonstrates the use of the Combit OLE control to print lists and reports. Must be compiled as 32-bit code.","Desktop","Windows" "Highest Resolution Printing","Demonstrates calculating the highest available resolution for printing.","Desktop","All" "Mailing Labels","Demonstrates printing customised mail labels.","Desktop","All" "Printing Multiple Pages of Text","Demonstrates printing text in multiple pages.","Desktop","All" "Printing pictures at high resolution","Demonstrates printing pictures at a high resolution.","Desktop","All" "Printing To Screen or Printer","Demonstrates drawing graphics to the screen or to a printer.","Desktop","All" "Report Preview via Container Control","Demonstrates previewing an SQLQuery generated report.","Desktop","All" "Report Using Groups","Demonstrates printing 'grouped' reports.","Desktop","All" "Report from SQL Query","Demonstrates printing a report from the results of an SQLQuery.","Desktop","All" "Simple Example","Demonstrates printing a simple report.","Desktop","All" "Printing in Columns","Demonstrates printing text in one or two columns.","Desktop","All" .. collapse:: Sample Apps .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Chat","A simple web based chat application.","Web","All" "Android","A customer invoices management application.","Mobile","Android" "EEDesktop","A customer invoices management application.","Desktop","All" "EEWeb","A customer invoices management application.","Web","All" "EEiOS","A customer invoices management application.","Mobile","iOS" "2K","A version of the game 2048 using the Canvas control.","Mobile","iOS" "2K-Desktop","A tile sliding mathematical game.","Desktop","All" "2K-iOS","A tile sliding mathematical game.","Mobile","iOS" "2k-Web","A tile sliding mathematical game.","Web","All" "BlockAttack","An animated block swapping game that uses the Canvas control.","Desktop","All" "BlockAttack","A coloured block matching game.","Desktop","All" "FallingBlocks","Control falling blocks via keyboard controls.","Desktop","All" "GameInputExample","Display outputs from a game control device. Uses the GameInputManager and GameInputDevice classes.","Desktop","All" "Jewels","A simple symbol swapping game using the Canvas control.","Desktop","All" "JewelGame","A coloured shape matching game.","Desktop","All" "Mole","Whack-A-Mole game using the Canvas control.","Desktop","All" "Mole","Speed and reactions game.","Desktop","All" "Sliders-Desktop","A version of the numbered sliding tiles game using the Canvas control.","Desktop","All" "Sliders-iOS","A version of the numbered sliding tiles game using the Canvas control.","Mobile","iOS" "Sliders-Desktop","A sliding tiles game.","Desktop","All" "Sliders-iOS","A sliding tiles game.","Mobile","iOS" "SpaceRocks","A version of the classic `video` game Asteroids using the Canvas control.","Desktop","All" "SpaceRocks-Desktop","Shooting rocks in space game.","Desktop","All" "SpaceRocks-iOS","Shooting rocks in space game.","Mobile","iOS" "Tank","A version of the classic Tank Battle `video` game using the Canvas control.","Desktop","All" "Tank","Battling tanks game for two players.","Desktop","All" "Turtle","A Frogger-style game using the Canvas control.","Mobile","iOS" "Turtle-iOS","A road crossing game.","Mobile","iOS" "MapLocation","A simple web based map application. Requires a Google Map API key.","Web","All" "MathFacts","A simple web based mathematics (addition) problem generator.","Web","All" "SimpleBrowser","A simple web browser. Uses the DesktopHTMLViewer control.","Desktop","All" "ToDo - Desktop","A simple ToDo list application.","Desktop","All" "ToDo - Web","A simple ToDo list application.","Web","All" "XojoNotes - Desktop","A simple text note organiser.","Desktop","All" "XojoNotes - Web","A simple text note organiser.","Web","All" "XojoText","A simple text file browser.","Desktop","All" .. collapse:: Shell .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Accessing the Shell","Accessing the command line (shell) from a desktop application.","Desktop","All" "Acessing a Directory via the Shell","Demonstrates getting information from a shell.","Desktop","All" "Interactive Shell","Demonstrates how to create and use an interactive shell from a desktop application.","Desktop","All" "Using the Shell Asynchronously","Demonstrates how to create and use an synchronous shell from a desktop application.","Desktop","All" .. collapse:: Sound .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Sound Player","A simple sound file player.","Desktop","All" .. collapse:: Text .. csv-table:: :header: "Name", "Description", "Type", "Platform" :widths: auto "Displaying JSON","Demonstrates hierarchical JSON data displayed in a DesktopListBox.","Desktop","All" "Drag and Drop Text","Demonstrates dragging and dropping text. Uses the DesktopTextArea control.","Desktop","All" "Formatting JSON","Demonstrates a simple JSON formatter.","Desktop","All" "JSON","A simple JSON demonstrator.","Desktop","All" "Loading and Saving Styled Text","Demonstrates how to save and load Rich Text Format (RTF) text.","Desktop","All" "Search and Scroll","Demonstrates searching for text and scrolling to the result.","Desktop","All" "XML","Demonstrates a simple hierarchical display of XML data.","Desktop","All" "XMLReader","Demonstrates a simple formatted display of XML data.","Desktop","All" Debugging ========= .. toctree:: :maxdepth: 1 :name: sec-debugging Introduction Debugger Usage Code Profiler Exception Handling Remote Debugging Stack System Error Codes ============ Introduction ============ What is debugging? Debugging means removing errors, both logical and syntactical, from your programming code. Errors in programming code are referred to as **bugs**, which has an interesting story behind it. Back in the 1940 's, the United States Navy had a computer that occupied an entire warehouse. At that time, computers used vacuum tubes and the light from the tubes attracted moths. These moths would get inside the computer and short out the tubes causing a failure. Technicians would have to go in and remove these "bugs" to make the computer work again. Since this was a government project, everything had to be logged, so they would put down “debugging computer” in the log and the term has stuck. Debugging is a normal part of programming. In fact, whenever you click the Run button to run your project you are technically debugging. Even though debugging is often the part of programming that most programmers like the least, the Debugger makes it easy to track down your nasty bugs and squash them like a, well, bug. .. _/getting_started/debugging/introduction/types_of_bugs: Types of bugs ------------- There are two types of bugs you can make in your code: logical and syntactical. .. _/getting_started/debugging/introduction/logical_bugs: Logical bugs ************ These are bugs in your programming logic. You will know you have found one of these when your code compiles but does not produce the results you were expecting. These are also often indicated by an exception in your app. The debugger can help you find these types of bugs by letting you watch your code execute one line at a time so you can pinpoint where the problem is. Logical bugs are by far the most common source of bugs. .. _/getting_started/debugging/introduction/syntactical_bugs: Syntactical bugs **************** These are bugs where you have mistyped the name of a keyword, class, property, variable, method, etc. These are often called Syntax Errors. You may have also tried to use two values together that don't go together. For example, if you try to assign a String value to a variable or property of type Integer, you will get a Type Mismatch error because they are different data types. These type of bugs are caught by the compiler, so you will never be able to even run your project if you have these types of errors. .. _/getting_started/debugging/introduction/message_logging: Message logging --------------- One common technique that was commonly used before tools had integrated debuggers was to print messages to the screen. This can sometimes be useful even with Xojo. You can use the :doc:`MessageBox` method to quickly display a message to help with your debugging. But you should use this technique sparingly. Displaying messages like this can alter the processing of UI events and may make your app behave differently than it would without the message. An alternative (and better) technique is to write messages to the Message Log, which is a panel that is displayed at the bottom of the Xojo Workspace. You use the :ref:`System.DebugLog` method to write messages that display in the Message Log: .. code:: xojo System.DebugLog("Log message here.") These messages can also be viewed by OS logging tools, such as the Console app on macOS or the Microsoft DebugView tool on Windows. Although the Debugger is a far better way to monitor the progress of your running app, using the Message Log can be useful for deployed apps. .. _/getting_started/debugging/introduction/analyzing_the_project: Analyzing the project --------------------- Since a project cannot be compiled if it contains syntax errors, you have the option of analyzing the project as a preliminary step. Choose Project > Analyze Project or Project > Analyze Item, where Item is the currently displayed project item. Analyze Project checks for issues but does not build the project. Some issues that it identifies are syntax errors, unused local variables and parameters, and type conversion issues. .. note:: Analyze Project only checks code for the platform you are currently using. For example, Analyze Project will not see code within Pragmas for :doc:`TargetWindows` if you are running Xojo on Mac. Errors are indicated by a stop sign icon and will prevent you from being able to run or build your project. Warnings are indicated by a yellow warning triangle and do not prevent you from building or running your project. If errors or warnings are found they are listed in the Errors panel at the bottom of the Workspace. Expand each row that is displayed to see the individual issues. You can click on each issue to display the appropriate editor (usually the Code Editor) with the issue highlighted. You should fix any errors that are reported. And you should evaluate the warnings to see if you think they need to be fixed. Not all warnings need to be fixed. The Type and Location buttons in the Issues pane allow you to change how to view the Issues. The Type button groups the issues by the type of the issue. So all the “Unused local variables” issues would appear together. The Location button groups the issues by the object in which they occur. So all the issues for Window1 would appear together, for example. .. _/getting_started/debugging/introduction/analyze_item: Analyze item ************ The Analyze Item command, available from the Project menu or on the Code Editor toolbar, works the same as Analyze Project but instead of analyzing the entire project, it only analyzes the code displayed in the Code Editor. In the case of a Menu in a desktop project, Analyze Item checks for duplicate shortcuts. .. _/getting_started/debugging/introduction/filtering_types_of_warnings: Filtering types of warnings *************************** You can control the types of warnings that the Errors panel displays. Choose Analysis Warnings from the Project menu to display a window that lists all the types of warnings that can be found. Only the selected warnings are reported when you analyze. Unselect any warnings that you do not wish to know about. .. _/getting_started/debugging/introduction/warnings: Warnings ******** These are the available warnings: * Item1 is deprecated * Item1 is deprecated. You should use Item2 instead * Old-style constructor methods are no longer supported. You should use "Constructor" instead * Unknown pragma name * Converting from Item1 to Item2 causes a possible loss of precision, which can lead to unexpected results * Converting from Item1 to Item2 causes the sign information to be lost, which can lead to unexpected results * Performing a Item1 comparison on floating-point values can yield unexpected results due to their inexact binary representation. * Custom warning string * Item1 is an unused local variable * Item1 is an unused method parameter * Item1 is an unused event parameter * This property shadows one already defined by Item1 * This constant shadows one already defined by Item1 * Before 2014r3, this would have referred to the Item1, but now it refers to the Item2. * Using method Item1 from the 'Item2' library on this target is suspicious. * Show API 2 Desktop control deprecations .. _/getting_started/debugging/introduction/see_also: .. seealso:: :doc:`Debugger Usage` topic ============== Debugger usage ============== When you run your project from within Xojo, you are using the debugger. If there are no errors after compiling and building your app, it launches and the Workspace switches to the debugger tab. This is referred to as running the project in “debug mode”. The standard way to run your project in debug mode is to choose Project > Run from the menu or click the Run button on the toolbar. .. note:: If you ever have difficulty debugging your web app (such as it not starting), make sure the debug port is not in use by another app on the computer. When in doubt, change the :ref:`Debug Port` to another value. In addition, the App Name and Application Identifier need to be different. The Run Paused item in the menu, builds a debug version of your app but does not actually run it. You'll need to manually go to your OS file viewer and launch the app. This can be used by advanced users for times when you have to do some manually setup before the app should start, such as manually start some other service, install files, etc. The Run Remotely item in the menu can be used to remote debug your app on a different computer. Refer to Remote Debugging for more information. While you are using your app you will not normally see the debugger, but there are several ways it can be activated. .. _/getting_started/debugging/debugger_usage/activating_the_debugger: Activating the Debugger ----------------------- There are several ways the debugger can become active: you can manually activate it, your app can raise an exception, you can set a breakpoint or call the Break method. When the debugger is activated, the Xojo IDE becomes frontmost and the debugger tab is displayed, usually showing source code. From the debugger tab you are able to view variable values and review the call stack. .. _/getting_started/debugging/debugger_usage/manual_activation: Manual activation ***************** You can manually activate the debugger by clicking the Pause button on the Debugger toolbar while your app is running. If your code is currently running, then this displays the method that is running and highlights the next line of code that will execute. If your app was idle, then this drops you into the Event Loop where you can view global objects and their variables (such as App, Runtime and modules). .. youtube:: RgK23y6Lq4Y .. note:: Viewing global variables and objects (such as modules) in the Debugger is not currently supported for Android. .. _/getting_started/debugging/debugger_usage/app_error_(exception): App error (exception) ********************* Should your app raise an exception while you are running in debug mode (and Project > Break On Exceptions is checked), the debugger becomes active (this is called a Runtime Error). The source code where the exception was raised is displayed. This only occurs if you have Project > Break On Exceptions checked. When checked, the debugger is activated for all exceptions that are raised, even ones that are caught later by a Catch statement. Click Resume to allow your app to continue and run the Catch code. To temporarily alter the Break On Exceptions behavior, you can use the #Pragma BreakOnExceptions Off for a code block like this: .. code:: xojo #Pragma BreakOnExceptions Off Try ' your code Catch e As NilObjectException ' handle exception End Try #Pragma BreakOnExceptions Default ' Restore setting from Project menu .. _/getting_started/debugging/debugger_usage/breakpoint: Breakpoint ********** In your code you can set breakpoints. A breakpoint is an indicator that tells the debugger to activate itself when the line of code is reached. For example, you might want to set a breakpoint at the start of a method if you want to carefully review the code as it executes. To set breakpoints, you click on the “dashes” that appear in the gutter of the :doc:`Code Editor`. Each dash indicates a line of code that can have a breakpoint set. You can also set a breakpoint using the Project > Breakpoint > Turn On menu (:kbd:`⌘ \\` on macOS, :kbd:`Ctrl \\` on Windows and Linux). The same command turns off a previously set breakpoint on the line. If you want to turn off all breakpoints throughout your project, use the Project > Breakpoint > Clear All menu. To see all breakpoints in your project, use Project > Breakpoint > Show All menu to display the breakpoints in the Find panel. If you want to keep all your breakpoints but have Xojo ignore them temporarily, choose the Project > Breakpoint > Ignore All menu. .. _/getting_started/debugging/debugger_usage/conditional_breakpoint` Conditional breakpoint ********************** There will be times where you may want to stop at a breakpoint, but only if a specific condition occurs. For example, you could be in a long loop and you only want to stop at the 75th element. You set up a condition breakpoint in your source code using a combination of a conditional If statement and the :doc:`Break` command. This example will stop at the breakpoint when the loop counter reaches 75: .. code:: xojo For i As Integer = 1 To 100 If i = 75 Then Break Next The Break command is called when i reaches 75, activating the debugger. The Break command does not do anything in a built application so you do not have to remove them before you Build your app. It is only used when running your app in Debug Mode. Another way to be notified of the change of a value is to create a computed property and then set a breakpoint within its Setter. This will cause the debugger to appear any time the property is assigned a value. .. _/getting_started/debugging/debugger_usage/the_debugger_screen: The Debugger screen ------------------- Once you are in the debugger, you can control it using these command bar commands: Pause/Resume, Stop, Step, Step In, Step Out, Edit Code and Toggle Line Numbers. * **Pause/Resume**: Use Pause to pause a running app and activate the debugger. If you are in the debugger, the Resume button tells your app to continue running where it left off. You can also click the Run button on the main toolbar to Resume, select Project > Resume from the menu or you can use the :kbd:`⌘ R` (:kbd:`Ctrl R` on Windows and Linux) shortcut. * **Stop**: The Stop button immediately stops the running app. The app is quit immediately and no further code is run. You can also use the shortcut :kbd:`Shift ⌘ R` (:kbd:`Shift Ctrl R` on Windows and Linux). * **Step**: The Step button is used to run the code one line at a time. Each time you click Step, the highlighted code is executed and you remain in the debugger. If you click Step while on a method call, the method is called and you move to the next line of code after the method call. Step is the command you use most often while debugging. In addition to clicking the button, you can use Project > Step > Step Over menu command or the shortcut :kbd:`Shift ⌘ O` (:kbd:`Shift Ctrl O` on Windows and Linux). * **Step In**: The Step In button works like the Step button except when you reach a method call. Instead of calling the method and moving to the next line of code, Step In moves you to the first line of code in the method. In addition to clicking the button, you can use Project > Step > Step Into menu command or the shortcut :kbd:`Shift ⌘ I` (:kbd:`Shift Ctrl I` on Windows and Linux). * **Step Out**: If you are in a method, clicking Step Out, runs the rest of the code in the method and then stops when the method returns. In addition to clicking the button, you can use Project > Step > Step Into menu command or the shortcut :kbd:`Shift ⌘ T` (:kbd:`Shift Ctrl T` on Windows and Linux). * **Edit Code**: The Edit Code button allows you to jump to the Code Editor for the current method that is in the debugger. Here you can edit the code (which you can not do in the code display of the debugger). However, changes that you make to code in the Code Editor are not reflected in the currently running app. You'll need to quit and re-run the app to see the changes you made. * **Toggle Line Numbers**: Toggles the display of line numbers on or off. .. _/getting_started/debugging/debugger_usage/watching_variables: Watching variables ****************** The watch pane of the debugger allows you to view the values of variables. By default it shows you the currently declared variables for the method that is executing. You can change the values of variables that are Boolean, Integer, Double and String by clicking on the value (or selecting the pencil icon on the far right), entering a new value and pressing Return. .. note:: For Boolean variables, anything entered besides True or true is treated as False. With String variables, the pencil icon on the right changes to show a magnifying glass. Clicking this displays a text editor which lets you view the text or its binary/hex representation. It also allows you to make changes to the string using a larger editing area. For Text variables, you can display the text itself as well as its Unicode representation. For integers, you can change the display format by right-clicking on the value and selecting View As Decimal, Hex, Binary or Octal. For doubles, you can change the display format by right-clicking on the value and selecting View As Scientific, Decimal or Rounded. If the variable type is a class, then you can click on the type (the class name) to display its property values. You can then click on the properties to see their values. Constants do not appear in the debugger. At the top of the variables pane is a Popup Menu that allows you to navigate the variable hierarchy. You can use it to quickly jump back to the method variables after having displayed the string editor or class instance details. On some objects (such as Windows and RowSets), there is a special link at the top called “Contents”. Clicking this displays information about the object, such as the controls on a window or the fields in a RowSet. You can click on these items to view their properties. .. _debugger_filter: Displaying only specific variables/properties ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Filter field allows you to reduce the list of items in the Variables pane to just those whose names begin with, contains or ends with the text you enter. For example, entering just the letter ``m`` will display only those items that begin with that letter: .. image:: https://documentation.xojo.com/getting_started/debugging/images/debugger_filter.png :scale: 50 % You can choose Begins With, Contains or Ends With from the popup menu to the right of the Filter field. The default is Begins With. You can apply multiple filters as well by providing a comma-separated value. For example, ``c, m`` would display all items that begin with the letters ``c`` or ``m``. Alternatively, clicking the Pencil icon to the right of the Filter field allows you to select items by name rather than typing. .. image:: https://documentation.xojo.com/getting_started/debugging/images/debugger_filter_selector.png :scale: 50 % .. note:: The filters you enter are remembered from one debug session to the next. .. _/getting_started/debugging/debugger_usage/viewing_source_code: Viewing source code ******************* The source code for the current method (or event or computed property) displays in the debugger code viewer. The next line to execute is highlighted. In the code viewer, you can set additional breakpoints and watch the highlight move as you step through code. If you want to edit the code, click the Edit Code button on the command bar. Changes made to code do not take effect until the next time you run your project. .. _/getting_started/debugging/debugger_usage/viewing_the_call_stack: Viewing the call stack ********************** The call stack displays the calling hierarchy of your methods. If your main window Opening event calls a method named Initialize then the call stack shows Initialize at the top (since it is the current method) with Window1.Opening underneath it. The call stack is very helpful for viewing your code path and for figuring out the methods that call other methods. You can click on prior items in the call stack to view its source code. .. _/getting_started/debugging/debugger_usage/viewing_threads: Viewing threads *************** Each thread in your app is tracked separately in the debugger. Use the selector in the Stack section to see and show the threads that are currently running. Click on a specific item in the thread viewer to see the source code. ============= Code Profiler ============= The Code Profiler is used to track the length of time each method in your app takes to run. This information allows you to focus on performance optimizing the parts of your app that might be considered slow. When profiling for performance, remember the quote by famous computer scientist Donald Knuth: "The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming." This means that you should not worry about performance until you have a reason to worry about performance. The good news is that when you are ready to worry about performance, the Xojo Code Profiler provides an easy way to identify the areas of your code that take longer to run. .. important:: Code profiling is not currently supported for Android and iOS. .. _/getting_started/debugging/code_profiler/using_the_code_profiler: Using the Code Profiler ----------------------- You can enable or disable the Profiler using the Project > Profile Code menu. A check mark appears next to the menu when profiling is enabled. After enabling Profile Code, you run and test your apps as you normally would. The Code Profiler silently gathers information about the methods that are called and how long each one takes to execute. Be aware that your application runs slower than usual when profiling is enabled due to the overhead of tracking and timing everything to get the profiler data. When you quit your app, the Profiles section appears in the Navigator with an entry for the profile data. Each time you run your project, the profile results appear in this Navigator section as a separate item. This allows you to compare current results to prior results. You can remove profile data by right-clicking on it in the Navigator and selecting Delete from the menu. Click on a Profile Data in the Navigator to display the Profile Results list, which shows you a list of all the methods that were called while you were using your app. It displays the number of times each method was called and how much time was spent in the method. * Name: The class name and name of method. The thread is also displayed if your app uses threads. * Called: The total number of times the method was called. * Total: The total time (in milliseconds) that was spent in the method. * Average: The average time (in milliseconds) that was spend in the method. This is Total / Called. You can save the profiler data to a text file using the contextual menu. Right-click anywhere in the summary and choose “Save As...”. You can expand methods to see the other methods that they called. When a method is collapsed, the time shown represents the time used by the method and any methods that were called as a result of that method running. When a method is expanded, the time shown for the method represents the time used only by the method itself. Any called methods are shown below it and have their own time values. As you are optimizing your app, you can compare the current Profiler Data with previous Profiler Data to see if your changes are improving performance. Profiler data is not saved along with your project. Use the Save As function described earlier if you wish to retain the profiler data. Profiler data is only collected if your app quits normally. If it quits because of a crash (or you press the Stop button in the debugger), no profiler data is collected. For web apps you can close all the browser windows/tabs and wait for the IDE to detect that the app has terminated using code like this: .. code:: xojo App.AutoQuit = True App.SessionTimeout = 10 Alternatively you can add a button or action that calls the :ref:`WebApplication.Quit` method. Profiler data cannot be collected for Mac apps that have been sandboxed. The Code Profiler does not work with the Remote Debugger. If you need to use the Profiler to gather information about your app running on another platform, just install Xojo on the platform, copy your project over to it and run it from there with Profile Code enabled. .. _/getting_started/debugging/code_profiler/using_the_profiler_with_built_apps: Using the profiler with built apps ---------------------------------- You can choose to create a standalone build of your app with embedded profiling code. You can then send this app to the users so that they can help generate profile data for you to analyze. To do this, just build your app with Profile Code selected in the Project menu. When you build with Profile Code enabled, a dialog appears to remind you that profiling code will be embedded in the app. With this profiling code embedded, your app will log profiling data as it is being used. When it quits, the profile data is saved to a file called Profile.txt in the same folder as the app. Your user can then send you this file for you to analyze. To view saved profiler data, take advantage of the `open-source Profile-Reader project `_. .. note:: If your app is crashing, your profile data will not be saved. You can force it to be saved by calling the SaveProfile method. .. _/getting_started/debugging/code_profiler/controlling_the_profiler_from_code: Controlling the profiler from code ---------------------------------- When the Profile is enabled from the Project > Profile Code menu, you can selectively turn it off and back on from within your code. To turn profiling off, use the :doc:`StopProfiling` method and to turn it back on, use the :doc:`StartProfiling` method. If you want to only profile a small set of methods in your app, turn off profiling in the App.Opening event with StopProfiling and then turn it on at the start of methods you want to profile and off at the end of the method. .. _/getting_started/debugging/code_profiler/performance_analysis_tips: Performance analysis tips ------------------------- When reviewing the results, each column tells you useful information, but not all of this information indicates you have a performance problem. Here are some suggestions of things to look out for. .. note:: Pausing a thread does not reduce profile measurement time. .. _/getting_started/debugging/code_profiler/high_number_of_calls: High number of calls ******************** You should evaluate methods that have a high number of calls. Try to determine if the method needs to be called so often. Reducing the number of times a method gets called can improve performance. For example, perhaps you have a Refresh method that is called often. In reviewing this, you may realize that the Refresh method is calling other methods unnecessarily and can perhaps be simplified. Or perhaps you'll notice that Refresh is being called more often than necessary. .. _/getting_started/debugging/code_profiler/high_total_time: High total time *************** It is worth reviewing methods that have a high Total Time. Now it could be that the Total Time is high because the method is called frequently. But this still warrants a review. It could be that the time is high because the method take a long time to complete, but this will mean it also has a high Average Time. Regardless, these methods are worth reviewing to see if they can be improved. .. _/getting_started/debugging/code_profiler/high_average_time: High average time ***************** Methods with high Average Time can quickly degrade performance if they are called often. Average Time is Total Time / Calls, so you may already be reviewing these methods. Often, a simple improvement to a method that is called frequently can have a dramatic effect on the entire app. .. _/getting_started/debugging/code_profiler/optimization_techniques: Optimization techniques ----------------------- To re-iterate, you should not spend your time optimizing code unless there is a clear case that it needs to be optimized. Sometimes it is just more pragmatic to move long-running code to a thread so that the user does not notice how long it takes. There may also be situations where the time spent optimizing the code does not yield a significant enough improvement to warrant the time spent on it. But if you do run into situation where your code is running more slowly than needed, these techniques may help. .. _/getting_started/debugging/code_profiler/remove_unnecessary_loop_calculations: Remove unnecessary loop calculations ************************************ If you have a calculation that occurs on a loop condition, then the calculation is done each time through the loop. Sometimes this may be necessary because the value could change. But often, this value does not change (it is what is called invariant -- it never varies), so you can instead save the value in a variable so that it does not get recalculated each time. In this example, the tax rate is calculated each time through the loop. But the tax rate does not change while the loop is running, so the calculation is performanced unnecessarily: .. code:: xojo While (billAmount * TaxRate(state)) < 100 AddLineItemToBill Wend Instead, you can save the tax rate: .. code:: xojo Var taxRate As Double = TaxRate(state) While (billAmount * taxRate) < 100 AddLineItemToBill Wend .. _/getting_started/debugging/code_profiler/listboxes: ListBoxes ********* Populating ListBoxes with data is a common task. If you are filling a ListBox with lots of data (which itself may not be a great idea), you can first make the ListBox invisible before you add the rows and then make it visible after the rows have been added. This can prevent some unnecessary possible screen refreshes and resulting event calls, providing an overall speed improvement. .. code:: xojo ListBox1.Visible = False ' Do long-running AddRow loop here ListBox1.Visible = True .. _/getting_started/debugging/code_profiler/call_fewer_methods: Call fewer methods ****************** There is a small amount of overhead for each method call. In a loop, you may find that just putting the code directly into the loop, rather than it its own method will result in a speed improvement. This should be done judiciously as it can quickly lead to unreadable code. You might also consider using framework methods that can do multiple things in one method call, rather than multiple method calls. For example, with a ListBox using AddRow to add a blank row and then filling its columns using the Cell method is less efficient than just calling AddRow once with all the cell data: .. code:: xojo ' Inefficient MyListBox.AddRow("") MyListBox.CellTextAt(MyListBox.LastAddedRowIndex, 0) = "Col0" MyListBox.CellTextAt(MyListBox.LastAddedRowIndex, 1) = "Col1" MyListBox.CellTextAt(MyListBox.LastAddedRowIndex, 2) = "Col2" MyListBox.CellTextAt(MyListBox.LastAddedRowIndex, 3) = "Col3" ' More efficient MyListBox.AddRow("Col0", "Col1", "Col2", "Col3") .. _/getting_started/debugging/code_profiler/var_outside_loops: Var outside loops ***************** Xojo allows you to using the :doc:`Var` keyword to declare variables within code blocks, including loops. This can be handy for code organization and it is usually recommended. But you can get a speed improvement by declaring a variable outside the loop and re-using it within the loop instead. .. code:: xojo While someConditionIsTrue Var s As String = CallMethod Wend By writing it this way instead, your loop can save some time: .. code:: xojo Var s As String While someConditionIsTrue s = CallMethod Wend .. _/getting_started/debugging/code_profiler/loop_analysis: Loop analysis ************* As several of these tips have noted, improving loops is often the path to code optimization. Because loops repeat many times, something slow within a loop is magnified many times. So improving something within a loop can sometimes result in a signicant speed improvement. .. _/getting_started/debugging/code_profiler/use_a_different_algorithm_or_technique: Use a different algorithm or technique ************************************** Sometimes your algorithm is just slow and performance tuning will not help enough to matter. For example, if you are searching data sequentially no amount of performance tuning is likely to help significantly. It will likely be faster to first sort the data and then do a binary search on the results. Or it may make sense to put data into an in-memory database and use its capabilities for fast searching. ================== Exception handling ================== The debugger can help you verify that your code is working as you expect and it can help you find errors. Once you've found the source of errors, you want to make sure you handle them properly. Exceptions are a type of error that occur when something unexpected happens. These errors will crash your application if you do not handle them in some way. The act of causing an exception to occur is called raising an exception. All exception are subclasses of the :doc:`RuntimeException` class. When an exception is encountered in your code, you can choose to have the Debugger displayed at the line causing the exception. To do this, select Project > Break On Exceptions in the menu so that it has a checkmark next to it. Since exceptions are for unexpected behavior, if you are able to avoid an error by preventing the exception from occurring then it is more efficient to do so. One reason is that when testing, the :doc:`Debugger` is displayed for any event that is raised if you have "Break On Exceptions" enabled in the Project menu. For example, with a :doc:`Dictionary` you use the :ref:`Value` method to fetch a value based on a key. If the key is not found you'll get a :doc:`KeyNotFoundException`. But you can avoid that by using either the :ref:`Dictionary` (to apply a default if the key is not found) or the :ref:`Dictionary` methods. So if you do expect situations where a key may not be found then you can account for it with these methods. And since that is not something unexpected, relying on the KeyNotFoundException is unnecessary. .. _/getting_started/debugging/exception_handling/nilobject_exceptions: NilObject exceptions -------------------- The most common type of exception that occurs is a :doc:`NilObjectException`. This exception occurs when you attempt to use an object but don't have an instance of it. If a NilObjectException occurs in your built app, then a dialog appears: If you are running your app in debug mode from within the Xojo IDE, then first the Debugger appears showing you the line of code that caused the exception and you can see the exception in the Variables pane of the Debugger. When you Resume, the above dialog will be displayed if your app does not handle the exception. If your app handles the exception then the exception handler runs and your code continues. The simplest example of how you can get a NilObjectException is forgetting to use New to get an instance before you try to use a property or call a method on it: .. code:: xojo Var d As Dictionary d.Value("ID") = "Test" ' NilObjectException The above code raises a NilObjectException because you did not use New to create a Dictionary instance (or an object). The correct way to write this code is with the New command: .. code:: xojo Var d As New Dictionary d.Value("ID") = "Test" ' NilObjectException Another place to check for this is in the return value of methods that return an instance. Some methods return Nil in certain situations, such as FolderItem.ShowOpenFileDialog which returns Nil if the user clicks Cancel: .. code:: xojo Var f As FolderItem = FolderItem.ShowOpenFileDialog("") If f.Exists Then ' NilObjectException ' Do something End If To prevent these types of errors, which is preferred, you should always check if the value is Nil before you attempt to access it: .. code:: xojo Var f As FolderItem = FolderItem.ShowOpenFileDialog("") If f <> Nil Then If f.Exists Then ' Do something End If End If< .. _/getting_started/debugging/exception_handling/try_catch: Try Catch --------- Sometimes you may find that an exception is a normal part of processing. What you want to do in these situations is catch the exception so that you can deal with it appropriately rather than letting your app crash. The :doc:`Try` command is used for this purpose. For example, loading an XML file can raise an XMLException if the file is not valid XML. You can check for this exception by using a Try..Catch block: .. code:: xojo Var xmlFile As FolderItem = FolderItem.ShowOpenFileDialog("") If xmlFile <> Nil Then Try Var xml As New XmlDocument xml.LoadXml(xmlFile) Catch e As XmlException MessageBox("Not an XML file!") Return End Try End If ' Process the XML If no XMLException is raised then the code in after the End Try section runs. If an XMLException is raised, then the code in the Try block stops running and the code in the Catch block is run, which in this case displays a message and allows the app to continue running. When you are running in debug mode, the Debugger appears when the exception is raised, even if you have code to handle the exception (when Project > Break On Exceptions is turned on). Choose Resume to have your app continue running. If you'd like to avoid the Debugger from appearing in specific situations you can temporarily turn this behavior off by using a Pragma: .. code:: xojo #Pragma BreakOnExceptions Off Try ' your code Catch e As NilObjectException ' handle exception End Try #Pragma BreakOnExceptions Default ' Restore setting from Project menu .. _/getting_started/debugging/exception_handling/exception: Exception --------- The :doc:`Exception` command is a simplified version of :doc:`Try`. Rather than focusing on a specific block of code, Exception catches errors for the entire method. The Exception command goes at the end of the method and is called if an exception is generated anywhere in the method. The preceding example could be written like this using the Exception command: .. code:: xojo Var xmlFile As FolderItem = FolderItem.ShowOpenFileDialog("") If xmlFile <> Nil Then Var xml As New XmlDocument xml.LoadXml(xmlFile) End If ' Process the XML Exception e As XmlException MessageBox("Not an XML file!") Return Although this is simpler, it is not as obvious what is occurring (especially if there is even more code in the method) and it is also far less flexible since it only works on the entire method. In most cases you should use Try...Catch. .. _/getting_started/debugging/exception_handling/app.unhandledexception: App.UnhandledException ---------------------- There is a special event handler in the App object of your project that is called if an unhandled exception is not caught using Try...Catch or Exception. The event has one parameter, error, which is the exception itself. You can put code in this event handler to display a message to the user, capture log information or anything else you want. Return :doc:`True` to hide the default unhandled exception error dialog and attempt to allow you application to continue, but this is not recommended because your app may no longer be stable depending on what caused the exception. .. _/getting_started/debugging/exception_handling/android_apps: Android apps ************ Android apps cannot continue after the :ref:`UnhandledException` event occurs. The app will always quit. Returning :doc:`True` or :doc:`False` from this event has no effect. .. _/getting_started/debugging/exception_handling/desktop_apps: Desktop apps ************ This code in :ref:`App.UnhandledException` displays a friendlier message to the user and then quits the application: .. code:: xojo Var msg As String = "An error occurred. Please notify the author." MessageBox(msg) Quit Return True You can also display the runtime stack so it can be used to help pinpoint the location of the problem so you can fix it: .. code:: xojo Var msg As String = "An error occurred. Please notify the author. Stack: " MessageBox(msg + error.Stack.FromArray(EndOfLine)) Quit Return True .. _/getting_started/debugging/exception_handling/ios_apps: iOS apps ******** iOS apps do not display an error dialog for unhandled exceptions; the app just terminates. You'll need to add your own code to :ref:`App.UnhandledException` to display unhandled exceptions. .. _/getting_started/debugging/exception_handling/web_apps: Web apps ******** The default error dialog displays information about the error and provides a field for users to add additional information if :ref:`App.UnhandledException` is not implemented or returns :doc:`False`. This information is written to an errors.log file alongside the web app. If the dialog that is displayed, if the user clicked "Send" you can also get the information in the WebSession.JavaScriptError event handler so that you can log it yourself. Remember, if this event handler has been called and you return :doc:`False` (the default) it means your entire web app is going to stop running. Return :doc:`True` to hide the default error dialog and prevent the web app from quitting. .. _/getting_started/debugging/exception_handling/creating_your_own_exceptions: Creating your own exceptions ---------------------------- Since RuntimeException is a class like any other, you can subclass it to create your own exceptions. This allows you to raise your own exceptions that you have defined. If you create a RuntimeException subclass called InvalidXMLFileFormatException then you can raise it using this syntax: .. code:: xojo If incorrectXmlFileFormat Then Raise New InvalidXMLFileFormatException End If .. _/getting_started/debugging/exception_handling/see_also: .. seealso:: :doc:`Try...Catch`, :doc:`Exception` commands; :doc:`RuntimeException` class; :doc:`Debugger Usage` topic ================ Remote debugging ================ .. rst-class:: forsearch Remote Debugger When you run your project it runs on the same platform you are using. So if you develop on Mac, then clicking Run will run your project on macOS. Since Xojo is a cross-platform development tool, you are likely to want to also run your projects on other platforms for debugging purposes. You can do this using the Remote Debugger, which has two version: Desktop and Console. The desktop version is used to remotely debug to an OS that has a desktop UI. It can be used with desktop and console apps. The console version is used to remotely debug to an OS that does not have a UI (such as servers). It can debug console and web apps. .. _/getting_started/debugging/remote_debugging/remote_debugger_configuration: Remote debugger configuration ----------------------------- The Remote Debugger is a small app that lets you run your project on a different target platform. Xojo communicates with it so that when you run your project, it sends it to the target platform rather than running it locally. Normally when you run your project from within Xojo (Project > Run), it runs the debug build locally. If you have the Remote Debugger configured, you can instead have Xojo send and run your debug build on a remote computer, which is incredibly useful for testing cross-platform apps. This remote build still communicates with Xojo, so you can access the debugger and test just as if you were running it locally. .. _/getting_started/debugging/remote_debugging/remote_debugger_desktop: Remote debugger desktop *********************** On the remote machine, you need to run Remote Debugger Desktop, which is included with your Xojo installation in the Extras folder. Supported configurations are: * macOS: 64-bit (ARM and x86) * Windows: 32-bit and 64-bit (ARM and x86) * Linux: 32-bit and 64-bit (ARM and x86) .. note:: The x86 Remote Debugger for Windows runs in emulation mode on ARM. .. image:: https://documentation.xojo.com/getting_started/debugging/images/remote_debugging_remote_debugger_desktop.png Copy the version you need to the platform you are using. .. image:: https://documentation.xojo.com/getting_started/debugging/images/remote_debugging_remote_debugger_options.png You can only use the 64-bit Remote Debugger on 64-bit operating systems. A 64-bit Remote Debugger can run either a 32-bit or a 64-bit app. When using a 64-bit Remote Debugger, the build settings for the target are used to determine if a 32-bit or a 64-bit build is sent. A 32-bit Remote Debugger can only run 32-bit apps. Remote Debugger Desktop has an Options window that lets you configure its settings. * Name: The name of the machine. This appears on the Developer Machine in Xojo's Remote Debugger preferences. * Password: An optional password used to connect to the Remote Debugger. * Download Location: Click the Choose button to set the location where the app will be download to and run from. Be sure you have read/write access to this location. * Network Interface: If you have more than one network interface, you can choose the one to use here. Typically you'll leave this as "Default". * Port: The default port is 44553, which you'll need to make sure is open in the firewall for both UDP and TCP connections. * Max Connections: This specifies how many concurrent connections to the Remote Debugger are allowed. * Public: Allows the Remote Debugger to be displayed on the Developer Machine in Xojo's Remote Debugger preferences. After you have set the Options, click OK and leave Remote Debugger Desktop running. The "Launch executable after receiving" checkbox is used to disable the automatic running of your app after the Remote Debugger receives it. You may want to disable this if you have to manually set up specific things on the remote machine (copy files or folders, start a separate app, etc.) before your app should launch. If you are using a firewall on the remote machine, you need to make sure that port 44553 is open for both UDP and TCP connections. If you hold Shift when launching the Remote Debugger Stub, it creates a "Stub Log" file on the desktop that contains log information that can help you troubleshoot any Remote Debugger configuration issues. On Windows, the Remote Debugger Stub can be minimized into the System Tray. .. _/getting_started/debugging/remote_debugging/remote_debugger_console: Remote debugger console ----------------------- The console version can debug console and web apps on remote machines that do not have a desktop interface (such as servers). Currently it can only be used across a local network (or VPN). On the remote machine, you need to run Remote Debugger Console, which is included with your Xojo installation in the Extras folder. Supported configurations are: * macOS: 64-bit (ARM and x86) * Windows: 32-bit and 64-bit (ARM and x86) * Linux: 32-bit and 64-bit (ARM and x86) .. note:: The x86 Remote Debugger for Windows runs in emulation mode on ARM. Copy the version you need to the platform you are using. You can only use the 64-bit Remote Debugger on 64-bit operating systems. A 64-bit Remote Debugger can run either a 32-bit or a 64-bit app (macOS or Linux. You cannot remote debug 64-bit Windows apps at this time). When using a 64-bit Remote Debugger, the build settings for the target are used to determine if a 32-bit or a 64-bit build is sent. A 32-bit Remote Debugger can only run 32-bit apps. The ARM remote debugger does not automatically start the console app. You'll need to manually launch it from the Terminal after it has been sent to the Pi. Remote Debugger Console runs from Terminal or the command line. The first time you launch it, you are prompted for the settings: * Machine Name: The name of the machine. * Download Directory: The location to download the app to and run from. Be sure you have read/write access to this location. * IP Address: Specifies the IP address to listen on (must match an IP address available on the computer). * Maximum Connections: Sets the maximum number of connections. * Auto Launch: Have the remote debugger automatically launch the app being debugged. * Public: Indicates if the remote debugger is publicly visible. * Password: Specifies an optional connection password. These settings are saved in the RDS.config file in the same folder as the Console Remote Debugger. You can also provide these an other options via the command line. Use the “--help” argument to get a list of all available command line options. .. _/getting_started/debugging/remote_debugging/development_machine_configuration: Development machine configuration --------------------------------- .. image:: https://documentation.xojo.com/getting_started/debugging/images/remote_debugging_remote_debugger_ide_preferences.png On the development machine, you need to configure Xojo so that it can see the Remote Debugger. In Preferences, select Debugger. There you'll see a list of configured remote machines. You can also see this list by going to Project > Run Remotely > Setup. Click Add to add your remote machine. Depending on your network configuration, the remote machine may appear as a public remote machine. If it does, you can just click its name and then OK to add it. If it does not appear, you can enter the IP address (specified on the Remote Debugger App on the remote machine) and give it a name. If you are using a firewall on your development machine, you need to make sure that port 44553 is open for UDP and TCP connections and port 13897 is open for TCP connections. .. note:: Virtual machine software such as VMware Fusion, Parallels Desktop, VMware Desktop and Microsoft Virtual PC all work great with remote debugging, but you typically want to have their networking configured to use "Bridged Networking" so that they will have their own IP address. .. _/getting_started/debugging/remote_debugging/remote_debugging: Remote debugging ---------------- Now you can try running a project remotely. On the development machine, create a new desktop project. To run the project remotely, instead of selecting Project > Run (or clicking the Run button on the toolbar), choose Project > Run Remotely and click your remote machine name. Your project is compiled and linked as usual, but you will now see an additional step where this debug build is sent over to the Remote Debugger App on the remote machine. When the Remote Debugger App has received the debug build, it runs it. Interact with the running app on the remote machine. Any breakpoints you have set will jump to the debugger on the development machine. .. _/getting_started/debugging/remote_debugging/virtual_machines: Virtual machines ---------------- Microsoft makes available several `different versions of Windows `_ for testing purposes, which you can use with virtual machines to test your Xojo apps. .. youtube:: o7cR983OJqs Linux distributions are generally free and work fine within most virtual machines. You are allowed to run macOS 10.7 and later within a virtual machine, but only if the VM is running on a Mac. Here are some commonly used Virtual Machine products: * VirtualBox * VMware Fusion * Parallels Desktop * Virtual PC .. _/getting_started/debugging/remote_debugging/see_also: .. seealso:: :doc:`Debugger Usage`, Remote Debugging topics; :doc:`TargetRemoteDebugger` constant ===== Stack ===== The **stack**, aka **call stack**, is a block of memory allocated in each process to store various data whenever a method calls another method or returns. .. _/getting_started/debugging/stack/return_addresses: Return addresses ---------------- Each method is stored at a specific location in memory. Such location is called *address* or *pointer* (which corresponds to a :doc:`Ptr` in Xojo). Whenever you call a method, the calling method puts its return address on top of the stack. Depending on the call and the operating system, the calling method can also put some parameters' addresses on the stack. On the other side, the called method will (if necessary) read parameters from the stack and execute its code. When it encounters a :doc:`Return` statement or the end of the method, it reads from the stack the *return address*, i.e. the location in memory from which execution of the code should continue. Of course, that address is somewhere inside the calling method's code. .. _/getting_started/debugging/stack/stack_errors: Stack errors ------------ There may be different errors related to a bad use of the stack which usually lead to (1) your application crashing or (2) raising a :doc:`Runtime Exception`. 1. A bad value onto the stack can happen when using an invalid :doc:`Ptr`. It can cause different types of crash. #. When a method is calling itself (aka recursion), there is a limit in the number of times a method can call itself. See :doc:`StackOverFlowException`. .. _/getting_started/debugging/stack/see_also: .. seealso:: `Call stack `_ on Wikipedia. ================== System error codes ================== Some classes, including :doc:`FolderItem`, :doc:`IOException`, :doc:`TCPSocket`, :doc:`IPCSocket`, etc., use system error codes in addition to internally-defined codes. .. _/getting_started/debugging/system_error_codes/macos: macOS ----- Negative error codes come from the Mac OS frameworks. Most, but not all, Mac OS error codes can be found in a C header file named MacErrors.h. This file is most likely at /System/Library/Frameworks/CoreServices.framework /Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/MacErrors.h. Positive error codes are usually BSD error codes. A list of these codes along with cryptic descriptions can be found in a C header file named errno.h. There are usually several files with this name on a Mac OS system; the one you want is probably /usr/include/sys/errno.h. .. _/getting_started/debugging/system_error_codes/windows: Windows ------- Windows system error codes can be found at `here `_. Advanced features ================= .. toctree:: :maxdepth: 1 :name: sec-advanced_features Creating weak references to objects Inspecting your application structure from code with Introspection Looping through your data with For Each Managing raw data with the MemoryBlock class Setting default values for Xojo framework class properties =================================== Creating weak references to objects =================================== You create an object by using the New command. This creates a reference to the object and increases its reference count. You can then pass this reference around to other methods or assign it to other variables and properties. Each time a new reference to the object is added, the reference count for the object increases. There may be times when you want to get a reference to an object, but you do not want to increase the reference count. You do this using the :doc:`WeakRef` class. To get a weak reference to an object: .. code:: xojo ' object is an existing reference to an object created with New Var ref As WeakRef ref = New WeakRef(object) The Value property of the WeakRef returns Nil if the object is no longer available. If the object is still available, it returns the object (which you will likely want to cast to the appropriate type) with increasing the reference count. You are now responsible for managing the memory used by this Weak Reference. For example, this code declares an instance of a FolderItem that will go out of scope and have its reference counter decreased. A weak reference is assigned to the FolderItem instance. While the instance is available, its name is displayed. When the instance is removed from memory, the weak reference value become Nil, so you now know there are no more references to it: .. code:: xojo Var ref As WeakRef If True Then Var f As New FolderItem f.Name = "TestFile.txt" ref = New WeakRef(f) If ref.Value <> Nil Then ' This displays MessageBox(FolderItem(ref.Value).Name) Else MessageBox("f is Nil.") End If End If If ref.Value <> Nil Then MessageBox(FolderItem(ref.Value).Name) Else MessageBox("f is Nil.") ' This displays End If .. _/topics/advanced_features/creating_weak_references_to_objects/explaining_weak_references: Explaining weak references -------------------------- When you create an object, say a Dictionary, divorce in your mind the object from the variable or property that holds it. The object is out there somewhere and the variable is your *bridge* to it. .. code:: xojo Var goldenGate As New Dictionary You can assign that object to more than one variable/property, and that creates more bridges, but there is still only one object. .. code:: xojo Var goldenGate As New Dictionary Var brooklyn As Dictionary = goldenGate Var williamsburg As Dictionary = brooklyn When there are no more ways to access the object, that is, when all the bridges are gone, the object goes away. .. code:: xojo Var goldenGate As New Dictionary ' 1 bridge Var brooklyn As Dictionary = goldenGate ' 2 bridges goldenGate = Nil ' Well, one bridge left brooklyn = Nil ' All bridges gone, the object is destroyed This is known as ":doc:`reference counting`". When a variable refers to an object, there is a reference and the object sticks around. When there are no more references, the object is destroyed. But there are times when you want to be able to access an object without creating a reference to it. In other words, you want to see if an object still exists, and create a reference to it if it does, without forcing it to stick around. In this example, think of it as a wire to the object. If there are other bridges to the object, the wire will lead you to it too and let you build a bridge. If all the bridges are gone, the wire snaps and leads nowhere. For these times, you can use a :doc:`WeakRef`, or weak reference, that will let you get to an object without forcing it to stick around in memory if the rest of your code is done with it. .. code:: xojo Var d As New Dictionary ' the object has one reference Var w As New WeakRef(d) ' still one reference Var d1 As Dictionary = Dictionary(w.Value) ' Now two references d = Nil ' Back to one reference, w.Value <> Nil d1 = Nil ' No more references, w.Value = Nil .. _/topics/advanced_features/creating_weak_references_to_objects/weak_reference_examples: Weak reference examples *********************** So when might you want a WeakRef instead of merely assigning the object to another variable? .. _/topics/advanced_features/creating_weak_references_to_objects/circular_references: Circular references ^^^^^^^^^^^^^^^^^^^ The most common use is to prevent a :doc:`circular reference`, that is, an object that holds a second object where the second object also holds the first object. With a circular reference, you can lose access to an object that will never be destroyed, creating a memory leak. The most common scenario here is the parent/child relationship. Let's say you create a Tree class that can hold many Leaf classes in an array. From any Leaf, you want to be able to get back to its Tree. That is, the Tree is the parent and the many Leafs are its children. Consider this simple example where a Tree has a single Leaf: .. code:: xojo Var t As New Tree Var l As New Leaf t.Child = l l.Parent = t t = Nil l = Nil Even though you've set both variables to Nil, both still exist because each refers to the other. To prevent that, you'd have to change the destruction code to something like this instead: .. code:: xojo t.Child = Nil t = Nil ' The Tree object sill exists because l.Parent refers to it l = Nil ' No more references to the Leaf, and since it's ' gone, no more to the Tree either But there is an easier way. Instead of defining Leaf.Parent as a Tree, define it as a WeakRef instead. .. code:: xojo Var t As New Tree Var l As New Leaf t.Child = l l.Parent = New WeakRef(t) l = Nil ' The Leaf still exists because t.Child refers to it t = Nil ' Since t was the only reference, Tree goes away and takes Leaf with it A :ref:`Computed Property` in Leaf that handles the WeakRef mechanics makes the process painless. But either way, since the only "true" reference is of the Parent to its Child, the problem of the Circular Reference evaporates. .. _/topics/advanced_features/creating_weak_references_to_objects/queues: Queues ^^^^^^ As another example, imagine code that sends objects through queues. While an object is waiting to be processed something might happen that renders it invalid or unnecessary. How would you handle that? For example, suppose there is a UI where the user can select multiple items where each represents an object that is placed on a queue for processing. After the user has selected multiple objects, they change their mind and unselect a few. How do you remove them from the queue? You could search the queue for that object and delete it. But what if your code uses multiple queues? What if you might add more in the future? This can all be solved easily with a WeakRef. The main array holds your objects and each queue holds a WeakRef to that object. When it comes time to process an item in the queue, the code resolves it back to its original object. If it can't, that is, the WeakRef evaluates to Nil because there are no other references to it, the queue processor ignores it and moves on. In this scenario, you can remove the object from every queue at once by simply deleting it from the main array. Instantly there are no more references to the object, so no more object, and the queues simultaneously have empty placeholders in the slots that originally represented that object. Special thanks to `Kem Tekinay for his forum post on WeakRefs `_. .. _/topics/advanced_features/creating_weak_references_to_objects/see_also: .. seealso:: :doc:`WeakRef` class; :doc:`Memory Management` topic; `About WeakRefs `_ forum post ================================================================== Inspecting your application structure from code with Introspection ================================================================== Introspection is a way for you to get information about your application structure at runtime. For example, you could use it to get a list of the methods of a class instance, check for a specific method and then call that method. Introspection is a module containing the various methods that can act on class instances. For example, this code gets the type (name) of a class instance: .. code:: xojo Var f As New FolderItem Var oType As Introspection.TypeInfo oType = Introspection.GetType(f) MessageBox(oType.FullName) The above code is not really useful because you already know the type since your code declared it. But if you are writing a more generic method that does not know much about the code that is calling it, introspection starts to become much more useful. In fact, introspection is most useful when you are writing generic code that is designed to be used in a wide variety of places. This type of code is often called framework code. .. youtube:: v5leKed6CSc Some examples of when introspection can be useful: * A unit testing library could use introspection to run methods that end in "test". * A database framework could use introspection to get the type of properties on a class in order to create database columns for them. * A serialization library could use introspection to query a class so that its information can be saved and restored. A simpler, but still interesting use is in the App.UnhandledException event handler. Here, you usually want to display the type of exception that occurred. You would normally do this using a long Select...Case statement for each type of exception. The problem with doing it this way, is that it makes your app larger because all the related code for the exception is pulled into your app, even if you do not need it. It also leads to more complicated code and requires you to remember every exception name and update the code when new exceptions are added. Instead you can use introspection to get the type of the RuntimeException parameter of the UnhandledException event handler. This code displays the type of the runtime error followed by the stack trace: .. code:: xojo Var excType As Introspection.TypeInfo excType = Introspection.GetType(error) Var excName As String excName = excType.FullName Var stack As String stack = String.FromArray(error.Stack, EndOfLine) Var errMsg As String errMsg = excName + EndOfLine + EndOfLine + stack MessageBox(errMsg) Return True .. _/topics/advanced_features/inspecting_your_application_structure_from_code_with_introspection/see_also: .. seealso:: :doc:`Introspection` module ======================================= Looping through your data with for each ======================================= There are two built-in interfaces that you can use to implement the usage of For Each...Next to loop through data: :doc:`Iterator` and :doc:`Iterable`. .. _/topics/advanced_features/looping_through_your_data_with_for_each/creating_an_iterable_listbox: Creating an iterable ListBox ---------------------------- As an example use of Iterator and Iterable, you can create a version of the DesktopListBox control that lets you iterate through its rows using For Each...Next. Here's how you can implement it. .. youtube:: 3pc53ye3jjI Create a new class called ListBoxIterator and assign it the Iterator interface which adds empty MoveNext and Value methods. You'll add code to those in a moment, but first add a Constructor method to ListBoxIterator: .. code:: xojo Public Sub Constructor(lb As DesktopListBox) Data = lb InitialRowCount = Data.RowCount End Sub This constructor takes as a parameter a DesktopListBox and saves references to the ListBox and its initial count to properties on the class which you should add now: * Data As DesktopListBox * InitialRowCount As Integer = -1 In addition you'll want one other property to track the current row of the iterator: * IteratorRow As Integer You'll also want to add a computed property that checks to verify that the ListBox has not been modified while you are iterating through it. The computed property: * IsValid As Boolean has this code in its Get block: .. code:: xojo If InitialRowCount <> Data.RowCount Then Return False Else Return True End If With these properties in place you can now implement the two interface methods. First, start with MoveNext: .. code:: xojo ' Ensure ListBox has not been modified while iterating If Not IsValid Then Var exc As New IteratorException exc.Message = "ListBox row count cannot be modified within For...Each loop." Raise exc End If If IteratorRow < (Data.RowCount - 1) Then ' Move to the next row in the ListBox. ' Note the first row is "-1", so you have to call ' MoveNext once to get to the first row 0. IteratorRow = IteratorRow + 1 Return True Else ' No more rows in ListBox Return False End If And now implement the Value method which actually gets the data from the ListBox. You can actually return the data however you want. In the case of a ListBox, this code gets the :doc:`String` value for each column in the row, separates it by spaces, and returns it as a single :doc:`String` value. .. code:: xojo Var lbRow As String Var space As String For column As Integer = 0 To Data.LastColumnIndex lbRow = lbRow + space + Data.CellTextAt(IteratorRow, column) space = " " Next Return lbRow Now that you have created an iterator that can work with a DesktopListBox you need to create a DesktopListBox subclass that uses the iterator. Add a new class (called IterableListBox) and set its super to DesktopListBox and assign it the Iterable interface which adds the GetIterator method. In this method you need to set up and return the iterator you just created. You do that by calling New and passing in the ListBox itself: .. code:: xojo Var lbIterator As New ListBoxIterator(Self) Return lbIterator That is all you need on this subclass. Now you can use the subclass with For Each...Next. On Window1, drag IterableListBox to the layout and add some initial data using the Initial Value property in the Inspector. Now add a button to the Window and put this code in it to iterate through the values you've added to the ListBox: .. code:: xojo For Each row As String In IterableListBox1 MessageBox(row) Next Run your project and click the button where you'll get the contents of the ListBox displayed in a message box, one row at a time. .. _/topics/advanced_features/looping_through_your_data_with_for_each/see_also: .. seealso:: :doc:`Iterator`, :doc:`Iterable` classes ============================================ Managing raw data with the MemoryBlock class ============================================ A MemoryBlock is used whenever you need a container for any arbitrary binary data. A MemoryBlock object allocates a sequence of bytes in memory and manipulates those bytes directly. A MemoryBlock can be passed in place of a Ptr when used in a Declare call. For times when you need to directly manage a block of data in its raw (byte) form, you will need to use a MemoryBlock. When reviewing the API for creating Declare statements, you will find that some parameters require a pointer to a chuck of data. You can set up a memory block as the location for this data. A MemoryBlock can be initialized with a String value. The byte contents of the string are assigned as the raw data for the MemoryBlock. .. code:: xojo Var s As String = "Hello!" Var mb As MemoryBlock mb = s MessageBox(mb.StringValue(0, mb.Size)) .. _/topics/advanced_features/managing_raw_data_with_the_memoryblock_class/see_also: .. seealso:: :doc:`MemoryBlock` class ========================================================== Setting default values for Xojo framework Class properties ========================================================== This feature allows you to change the default property values used by built-in Xojo classes. For example, you could change the default Windows width and height from 600x400 to 800x600. These are the steps to override class default values: 1. Create a file named ``.defaults`` in either ``/Xojo/Overrides`` or ``/Xojo/Overrides`` #. The file contains simple key=value pairs, where the key is the property name on the class and the value is everything after the =. #. Integer properties can support different values by OS with the following syntax: Width=20|30|40 (macOS uses 20, Windows uses 30, Linux uses 40) #. String properties do not have a way to include an end-of-line For example, a file (named Window.defaults and located in ~/Documents/Xojo/Overrides) with the following key/value pairs changes the default width and height of a Window from 600/400 to 800/600: .. code:: xojo Width=800 Height=600 After restarting Xojo, when you add any new Windows to a desktop project, they will have a default width of 800 and height of 600. .. _/topics/advanced_features/setting_default_values_for_xojo_framework_class_properties/notes: Notes ----- CheckBox state can be set with ``state=``, where value = 0-Unchecked, 1-Checked, 2-Indeterminate (the values of the Inspector Popup menu for this field). Android ======= .. toctree:: :maxdepth: 1 :name: sec-android Installing Android Studio Removing Android Studio Publishing your app to the Google Play Store Publishing your app via a URL Differences between Android and iOS ========================================= Installing and Configuring Android Studio ========================================= To run, test and debug an Android project you create in Xojo requires that Google's Android Studio development tool be installed but you will not have to use it. .. _/topics/android/installing_android_studio/installing_android_studio: Installing Android Studio on Windows ------------------------- 1. `Download the .exe file. `_ 2. Double-click the .exe file. Installing Android Studio on macOS ------------------------- 1. `Download the DMG file. `_ 2. Double-click the DMG file and drag Android Studio to the Applications folder. 3. Navigate to the Applications folder and launch Android Studio. Installing Android Studio on Linux via the Package Manager ------------------------- .. note:: On 64-bit machines, download the required libraries first. .. code:: xojo For Debian/Ubuntu-based distros sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386 .. code:: xojo For Fedora/Red Hat-based distros sudo dnf install zlib.i686 ncurses-libs.i686 bzip2-libs.i686 1. Download the Linux zip file. `_ 2. Unpack the file to an appropriate location, such as '/usr/local/'. 3. In the terminal, navigate to 'android-studio/bin/' and execute 'studio.sh'. Setting Up Android Studio for the First Time ------------------------- 1. The first time you launch Android Studio, you will need to configure it. Android Studio will ask you if you would like to import Android Studio settings. Make sure *Do not import settings* is selected then click **OK**. 2. Android Studio will open a window that will take you through the initial setup. On the Welcome page, click **Next**. 3. On the Install Type page, leave it set to *Standard* and click **Next**. 4. On the Select UI Theme page, choose Dark (the default) or Light then click **Next**. 5. On the License Agreement page, in the list on the left, make sure android-sdk-license is selected then click **Accept**. 6. On that same page, click on android-sdk-arm-dbt-license to select it then click **Accept**. 7. Now click **Finish**. 8. Android Studio will spend a few minutes downloading various files. When it's done, click **Finish**. .. note:: As Google changes Android Studio, these steps may be not be 100% correct. .. seealso:: :doc:`Running/Debugging via the Android Emulator`:doc:`Setting up on-device debugging`, :doc:`Android QuickStart`, :doc:`Android Tutorial` ======================= Removing Android Studio ======================= In the unlikely event you should ever have to remove Android Studio, these are the steps for doing that. Keep in mind that as Google updates Android Studio, these steps may not longer be complete. macOS ----- 1. Open the Terminal app. 2. Paste in the following list of commands: .. code:: rm -Rf /Applications/Android\ Studio.app rm -Rf ~/Library/Preferences/AndroidStudio* rm -Rf ~/Library/Preferences/Google/AndroidStudio* rm -Rf ~/Library/Preferences/com.google.android.* rm -Rf ~/Library/Preferences/com.android.* rm -Rf ~/Library/Application\ Support/AndroidStudio* rm -Rf ~/Library/Application\ Support/Google/AndroidStudio* rm -Rf ~/Library/Logs/AndroidStudio* rm -Rf ~/Library/Logs/Google/AndroidStudio* rm -Rf ~/Library/Caches/AndroidStudio* rm -Rf ~/.AndroidStudio* rm -Rf ~/AndroidStudioProjects rm -Rf ~/.gradle rm -Rf ~/.android rm -Rf ~/Library/Android* rm -Rf ~/.emulator_console_auth_token 3. Press :kbd:`Return` Windows ------- 1. Open the Control Panel and under Programs, select **Uninstall** a Program. 2. Click on **Android Studio** and press **Uninstall**. If you have multiple versions, uninstall them as well. 3. In File Explorer, go to your user folder (%USERPROFILE%), and delete **.android**, **.AndroidStudio** and any analogous directories with versions on the end, i.e. .AndroidStudio1.2, as well as .gradle and .m2 if they exist. 4. Go to %APPDATA% and delete the JetBrains directory. 5. Delete the any **AndroidStudio*** directories that are in %LOCALAPPDATA%\Google and %APPDATA%\Google. 6. C:\Program Files and delete the **Android** directory. 7. Delete any remains of the SDK by going to %LOCALAPPDATA% and deleting the **Android** directory. 8. Android Studio creates projects in a folder %USERPROFILE%\AndroidStudioProjects, which you may want to delete. ============================================ Publishing your app to the Google Play Store ============================================ .. rst-class:: forsearch Signing Android apps are distributed through the Google Play Store. They can be made available to the general public or just to those within your organization. To publish your app, you will need a Google Play Store Developer account. If you don't already have one, `sign up for one here `_. Publishing your app involves signing it and then uploading it to the Google Play Store. Signing your app ---------------- The Google Play Store requires that all Android apps be signed with a signing certificate. This process provides a uniform way for anyone to determine the author of an app. You can sign all of your apps with a single certificate. If you are creating apps for other organizations, remember that the certificate indicates the author. If the organization for whom you are creating the app should be listed as the author, you should create a separate certificate for use when signing the apps you create for them. These certificates are stored in a file called a *keystore*. You can store all of your certificates in a single keystore or create multiple keystore files. How you choose to organize them is completely up to you. If you only create apps that will be published by you, you will likely need only one certificate stored in a single keystore file. To create a keystore file: 1. Launch Android Studio. 2. Choose **Build > Generate Signed Bundle/APK**. The Generate Signed Bundle/APK dialog box appears. 3. In the Generate Signed Bundle/APK dialog box, choose the **Android App Bundle** option. 4. Below the field for Key store path, click **Create new**. The New Key Store window appears. 5. For the **Key store path field**, click the **folder icon** and select the location where you want your keystore file stored. You can move it later if you'd like. 6. Choose a **password** and then confirm it. 7. Enter an identifying name in the **Alias field**. If this certificate is for you, it would be sensible to put your name in this field. If you are certificate is for an app you are creating for another organization, put that organization's name in this field. 8. Enter and confirm the **Key password**. This can be the same password you used earlier. 9. Fill in the Certificate fields. The Country Code is a two-letter code (such as ``US``). 10. Click **OK**. .. Important:: You cannot use a comma in the Certificate fields. This will generate a "Failed to create keystore" error. You may encounter this with other punctuation characters as well. 11. In the Generate Signed Bundle/APK dialog box, click **Next**. 12. Choose **release** then click **Create**. .. note:: The debug option is not needed as Xojo creates it automatically when you run your Android project from the IDE. The keystore file has now been created as well as a file titled private_key.pepk. Both of these files will be required for signing your app. .. important:: Your keystore file extension **must** be ``.jks`` in order for Xojo to sign your app. In order to sign your application, Xojo will need to know which certificate in the keystore file to use. To do this, you'll need to create a text file that indicates the keystore password, the Key password, the name of the Key alias and the name of the keystore file itself. The text file contents should be in the following format: .. code-block:: storePassword=keyStorePassword keyPassword=KeyPassword keyAlias=KeyAlias storeFile=KeyStoreFileName For example, if the keystore password and key password were both *mellon*, the key alias was *Gandalf* and the keystore file name was *MyKeys.jks*, the text file contents would be: .. code-block:: storePassword=mellon keyPassword=mellon keyAlias=Gandalf storeFile=MyKeys.jks This file can be named anything you wish but it must be stored in the same folder/directory that contains your keystore file. 1. To provide access to this file to Xojo so that it can sign your app after building it, in the Build Setting section of the Navigator, click on Android. 2. In the Inspector, click on the **...** button next to the Key Store Properties item. An open file dialog box appears. 3. Select the text file you created. When you build your app, Xojo will sign it with the certificate in your keystore file as designated in the keystore properties text file you created and provided to Xojo. Uploading your app to the Google Play Store ------------------------------------------- To upload your app to the Google Play Store: 1. Go to the `Play Console `_. 2. Create a record with details about your app. 3. Upload your app. 4. Make it available to beta testers only or release it if it's ready. More details on how to upload your signed app to the Google Play Store can be found `here `_. If you are creating an app that should be available only to those within a particular organization, you will need to add that organization to your Google Play Store app settings. More information on how to do that can be found `here `_. .. important:: The Non Release Version number must be incremented with each bundle you want to upload to the Google Play Store. .. seealso:: :doc:`Publishing your app via a URL` ============================= Publishing your app via a URL ============================= .. rst-class:: forsearch Signing Android apps can be distributed and installed directly via a URL. Publishing your app this way involves signing it and then uploading to a server so that you can provide the URL to end users for download. Signing your app ---------------- Google requires that all Android apps be signed with a signing certificate. This process provides a uniform way for anyone to determine the author of an app. You can sign all of your apps with a single certificate. If you are creating apps for other organizations, remember that the certificate indicates the author. If the organization for whom you are creating the app should be listed as the author, you should create a separate certificate for use when signing the apps you create for them. These certificates are stored in a file called a *keystore*. You can store all of your certificates in a single keystore or create multiple keystore files. How you choose to organize them is completely up to you. If you only create apps that will be published by you, you will likely need only one certificate stored in a single keystore file. To create a keystore file: 1. Launch Android Studio. 2. Choose **Build > Generate Signed Bundle/APK**. The Generate Signed Bundle/APK dialog box appears. 3. In the Generate Signed Bundle/APK dialog box, choose the **APK** option. 4. Below the field for Key store path, click **Create new**. The New Key Store window appears. 5. For the **Key store path field**, click the **folder icon** and select the location where you want your keystore file stored. You can move it later if you'd like. 6. Choose a **password** and then confirm it. 7. Enter an identifying name in the **Alias field**. If this certificate is for you, it would be sensible to put your name in this field. If you are certificate is for an app you are creating for another organization, put that organization's name in this field. 8. Enter and confirm the **Key password**. This can be the same password you used earlier. 9. Fill in the Certificate fields. The Country Code is a two-letter code (such as ``US``). 10. Click **OK**. .. Important:: You cannot use a comma in the Certificate fields. This will generate a "Failed to create keystore" error. You may encounter this with other punctuation characters as well. 11. In the Generate Signed Bundle/APK dialog box, click **Next**. 12. Choose **release** then click **Create**. .. note:: The debug option is not needed as Xojo creates it automatically when you run your Android project from the IDE. The keystore file has now been created. This file will be required for signing your app. .. important:: Your keystore file extension **must** be ``.jks`` in order for Xojo to sign your app. In order to sign your application, Xojo will need to know which certificate in the keystore file to use. To do this, you'll need to create a text file that indicates the keystore password, the Key password, the name of the Key alias and the name of the keystore file itself. The text file contents should be in the following format: .. code-block:: storePassword=keyStorePassword keyPassword=KeyPassword keyAlias=KeyAlias storeFile=KeyStoreFileName For example, if the keystore password and key password were both *mellon*, the key alias was *Gandalf* and the keystore file name was *MyKeys.jks*, the text file contents would be: .. code-block:: storePassword=mellon keyPassword=mellon keyAlias=Gandalf storeFile=MyKeys.jks This file can be named anything you wish but it must be stored in the same folder/directory that contains your keystore file. 1. To provide access to this file to Xojo so that it can sign your app after building it, in the Build Setting section of the Navigator, click on Android. 2. In the Inspector, click on the **...** button next to the Key Store Properties item. An open file dialog box appears. 3. Select the text file you created. 4. Make sure the Build for Google Play Store is **off**. When you build your app, Xojo will sign it with the certificate in your keystore file as designated in the keystore properties text file you created and provided to Xojo. The built app will have a .apk suffix. This is the file you will make available for download. The file ending in .idsig is unneeded. .. important:: Do not compress your app or change the format in any way. The raw .apk file must be what the user downloads in order for it to be recognized by Android for installation. Installing an app directly from a URL ------------------------------------- To download and install an from a URL: 1. Go to the Chrome browser on your Android device. 2. Enter the URL that points to the .apk file. 3. If this is the first time you've installed an app via Chrome, Android will ask you to give Chrome permission to do so. 4. If you haven't installed apps from this developer before, you will likely get some warnings before you can install the app. 5. Once the app is installed, you can launch it. .. seealso:: :doc:`Publishing to the Google Play Store` =================================== Differences between Android and iOS =================================== As much as Xojo abstracts you from the differences between platforms, there are nevertheless some differences that remain. Some are the result of the two platforms themselves being different and others are more temporary differences that will eventually be eliminated over time. While these differences are documented throughout the Xojo documentation, this :download:`guide` provides a detailed list of them. API design ========== .. toctree:: :maxdepth: 1 :name: sec-api_design API design and naming guidelines Moving to API 2.0 ================================ API design and naming guidelines ================================ This is a living document that will be updated periodically when necessary. .. _/topics/api_design/api_design_and_naming_guidelines/introduction: Introduction ------------ The purpose of this document is to provide guidelines for creating APIs and naming them such that new APIs are clear, easy to understand and most importantly, consistent with the rest of the Xojo framework. The goal of consistency is to make it possible for a user to guess the API of a class with which they have never worked rather than have to learn each one every time. This also makes understanding existing code faster as well. However, consistency should not reduce readability. .. _/topics/api_design/api_design_and_naming_guidelines/consistency: Consistency ----------- Before creating a new API, review the existing APIs to see if there's one that is similar to what you need for the new functionality. .. _/topics/api_design/api_design_and_naming_guidelines/case: Case ---- Class names are upper camel case. Example: ``TextField`` Class members are upper camel case. Example: ``TextField.SelectedText`` Parameters are camel case. Example: ``FolderItem.FromSaveInfo(saveInfo As String) As FolderItem`` Namespaces are upper camel case, when used. Example: ``Xojo.Core`` .. _/topics/api_design/api_design_and_naming_guidelines/general_naming: General naming -------------- **Names should provide the most intuitive identification not necessarily the most accurate. They should choose clarity over brevity.** The name should make it immediately obvious what the item is for even if that name is not entirely accurate. For example, previously the name of the event that is called when a button is clicked was called Action rather than Clicked or Pressed because it can be triggered without clicking/pressing the button. That makes Action more accurate but it's also not at all intuitive. In API 2.0, Action has been renamed Pressed which, while less accurate, is far more intuitive. It's obvious what the event is for and the exceptions (such as the fact that a button can be triggered without pressing it - via accessibility features for example) are easy enough to understand. Another example is the Volume class in the context of FolderItems. Most people have to learn what this means. In the API 2.0 we use Drive instead since most volumes are Hard Drives, Flash Drives or Solid State Drives (SSD). Volume is more accurate but not as intuitive since it also has a meaning relating to sound volume. **Names should not use abbreviations or truncations** unless they are so common that they are spoken that way. For example, Info is acceptable. Sel (for Selection) is not. Abbreviated Min and Max are not acceptable as part of compound framework terms. This does not apply to standard math functions, such as for ``Min`` and ``Max``, which will will not change. **Class names should be a noun.** For the case when there are two classes, one that can be modified and one that cannot, use the Editable prefix for the one that can be modified and no prefix for the one that cannot be modified. For example, ``Image`` and ``EditableImage``. **Method names should start with a verb. Follow with a noun for clarification, but do not include a noun if it is redundant.** For example ``Refresh`` or ``SetFocus``. **Functions (methods that return a value) should be named or prefixed with what they return.** ``DesktopFolder`` is acceptable. ``GetDesktopFolder`` is not. **Event names should be past tense if the event itself has already taken place when the user's code is called.** Example: ``TextField.TextChanged`` is acceptable because the TextField's value has already been changed. **Event names should be in the form noun verb.** For example ``SelectionChanged``. **Most controls should have a Pressed event.** Controls whose primary form of user interaction involves the user clicking or tapping the control should have a Pressed event that is passed a parameter when necessary to indicate what was pressed. **Properties (and Constants) in most cases, should be nouns and named after what they contain.** **Enumeration names should always be plural.** Name of members of type enumeration should always be singular. For example, the Timer class has ``RunModes`` as an enumeration and ``RunMode`` as a member of type ``RunModes``. **Omit needless words.** For example, ``System.GetNetworkInterface`` should just be ``NetworkInterface`` (and also because functions should be named what they return). TextField has both ``TextChanged`` and ``SelectionChanged``. **Group associated members.** When possible, if members are associated with each other, use a common prefix so they are listed together alphabetically. For example, ``DragEntered`` and ``DragExited``. However, do this only when other API guidelines can also be satisfied. **When a class member is specific to a particular platform, it should be prefixed with the name of the platform.** The following are the designated prefixes: Mac, Windows, Linux, iOS, Android, Web and Mobile. .. _/topics/api_design/api_design_and_naming_guidelines/boolean_properties: Boolean properties ****************** These prefixes are used with UI-related Boolean properties for situations when it is unclear how the UI may be changed. .. csv-table:: :header: "Name", "Description", "Example" :widths: auto "Allow","Makes a change to the behavior or interface possible but not necessarily immediately.",``AllowFocusRing`` "Has","Makes an immediate visual change to an element of the user interface of the object.",``HasCloseButton`` "Is","Optionally used for read-only Boolean properties (and functions) when needed for clarity.",``IsRectangle`` If Allow or Has results in awkward names, use something that is better. Otherwise Boolean properties do not have a prefix and are named after what they indicate. For example, ``Enabled`` or ``DarkModeIsSupported``. .. _/topics/api_design/api_design_and_naming_guidelines/lists: Lists ----- List items are accessed via a value from the list or a 0-based index. When an index is used, the preposition “At” is added which differentiates it from a method that looks up by value. Classes that provide access to a list should implement the following standard set of members. For non-generic methods, a noun should be included to help clarify the purpose of the method (as specified using ** below). For example, AddRow, AddButton, AddMenu, AddSegment, etc. .. csv-table:: :header: "Name", "Description", "" :widths: width: 50 "**Add**","Adds a single item to the end of the list.",``AddButton(*Hello*)`` "**AddAt**","Adds a single item to the list at the specified index. The index should always be the first parameter.",``names.AddAt(5, *Mary*)`` "**AddAll**","Adds all items passed to the end of the list.",``AddAllRows(myList)`` "**At**","Get or Set an item in the list using the specified index value.",``Var item As String = ColorSegment.SegmentAt(2)`` "**FirstIndex**","The index of the first item.",``Var first As Integer = Me.FirstSegmentIndex`` "**IndexOf**","Returns the index of the first position where the item passed is found in the list.",``Var index As Integer = allCustomers.IndexOf(*Fred*)`` "**Count**","Returns the number of items in the list.",``Var count = Me.ButtonCount`` "**TagAt**","Accesses the tag for the item specified by the index(s) passed.",``nameList.RowTagAt(5) = *Hello*`` "**RemoveAt**","Removes the item specified by the index passed.",``mainToolbar.RemoveButtonAt(5)`` "**RemoveAll**","Removes all items from the list.",``RemoveAllSegments`` "**LastIndex**","The index of the last item.",``Var last As Integer = Me.LastSegmentIndex`` "**LastAddedIndex**","The index of the last item added by Add, AddAt or AddAll.",``Var last As Integer = Me.LastAddedRowIndex`` List classes should also always implement the Iterable and Iterator interfaces to allow looping with For Each...Next statements. For two dimensional lists (probably only for multi-column listboxes), the *At* method should take row and column indexes as parameters and could return the entire item (as an array of the appropriate type) if no column index is passed. For list-type containers that have multiple things that are counts (row count, column count, for example) then be specific for all the types of count: RowCount, ColumnCount. For user interface controls that are list-oriented (ListBox, PopupMenu, TabPanel, Toolbar, SegmentedControl, etc.) where the user can select an item from the list, the following members should be implemented: .. csv-table:: :header: "Name", "Description", "Example" :widths: auto "**Selected**","Returns the item currently selected. If no item is selected, an OutOfBoundsException is raised.",``Var value As String = Me.SelectedRow`` "**SelectedIndex**","Allows you to get or set the selected item. If you call this as a function and nothing is selected, an OutOfBoundsException is raised.",``Var index As Integer = Me.SelectedRowIndex`` ,,``Me.SelectedIndex = 5`` "**SelectedCount**","Returns the number of items selected.",``If Me.SelectedSegmentCount > 0 Then`` **Avoid hidden functionality:** Avoid negative indexes for special behaviors. Instead add methods that provide the functionality. For example, ``Listbox.CellTextAt(-1, -1)`` is bad as it should really raise an OutOfBoundsException. Instead, a ``Listbox.Contents`` method that returns a two-dimensional array would be better. With no parameters this is the entire ListBox, with one parameter this is the specific row. .. _/topics/api_design/api_design_and_naming_guidelines/errors: Errors ------ Errors should raise exceptions rather than return error codes. However, the exceptions raised can have members that provide error codes. Methods should not return booleans to indicate that they succeeded or not as that boolean value is really a simple error code. Instead, the function should throw an exception if it fails. .. _/topics/api_design/api_design_and_naming_guidelines/enumeration_usage: Enumeration usage ----------------- Use enumerations in place of integer constants when the value is unimportant and they are not used as part of a calculation. For example, bit flags would need to be constants because they are used to calculate actual Integer values. ================= Moving To API 2.0 ================= When you create applications with Xojo, you use the Xojo framework. This consists of classes and modules that provide things like windows, access to files, and more. When you use things like FolderItem and MsgBox, you 're using the APIs that make up the Xojo framework. API 2.0 is our effort to make the way in which you write code easier and faster by introducing new APIs that are more intuitive and consistent. This document will provide an overview of API 2.0 and explain how you can move to it when you are ready. .. note:: There's no need to rush to change code to API 2.0. We expect that most of you will upgrade parts of your code only when doing so provides some advantage like a bug fix or new functionality. The APIs that have been replaced with new ones will likely be around for many, many years. For more information, check out our `Your Path Forward with API 2.0 `_ blog post about transitioning to API 2.0. .. _/topics/api_design/moving_to_api_2.0/overview: Overview -------- **Creating Variables** - The Dim keyword is short for “dimension” and is left over from the original BASIC language of the 1960 's. While you may have become accustomed to it, it's not intuitive for most people. Now you can use the Var keyword, which is more obviously short for "variable" and also commonly used in other languages. **Adding/Removing Rows** - Working with lists of data is now more consistent. Adding rows to arrays, a ListBox, a PopupMenu, a ComboBox and more is done using an AddRow method. Removing rows is done with RemoveRow or to remove all rows, the aptly named RemoveAllRows method. **Working with Indexes** - All indexes are now 0-based. Methods ending with the word “At” signify that the first parameter will be an index. For example, AddRowAt indicates a row will be added at the index you provide. RemoveRowAt removes a row at the supplied index. The index will always be the first parameter. **Data Looping** - This is a very common operation. The easiest way to do this is with an iterator which passes through a list of data without requiring you to keep track of the index to access the current item. For example, when looping through an array, you must make sure to remember to start at 0 and then continue to the last row of the array. Such a loop might look like this example: .. code:: xojo For i = 0 To students.LastIndex If students(i) = "Sally" Then SallyCount = SallyCount + 1 Next With an iterator, it's assumed you will be looping through the entire array and you create a variable to access each row: .. code:: xojo For Each name As String In students If name = "Sally" Then SallyCount = SallyCount + 1 Next The following classes or properties now support iterating: * Dictionary * Arrays * RowSet * URLConnection.ResponseHeaders * OpenFileDialog.SelectedFiles The ability to iterate through lists will be added to more of the framework in future releases. **Avoiding Literal Mistakes** - Often when writing code you must deal with numbers that are returned by functions where each possible value has a different meaning. This can result in code that is difficult to read as the purpose of the magic value is not immediately obvious. Enumerations solve this problem by providing an obvious and meaningful keyword that replaces the magic value. For example, in previous versions of Xojo when setting the text alignment of a TextField, you might have previously written the code to center-align the text like this: .. code:: xojo TextField1.Alignment = 2 It's not obvious what 2 means in this case. The replacement for TextField.Alignment is TextAlignment. This property is an enumeration. As a result, using the enumeration is required which makes the code more readable: .. code:: xojo TextField1.TextAlignment = TextAlignments.Center It's now absolutely clear what the code is going to do. Also because the old Alignment property is an integer, should you have chosen to use a literal value (2 for example), a typo (typing 22 by mistake) would not be caught by the Xojo compiler or by any kind of error when you app runs. Enumerations solve this problem because the only way to assign values is via the enumeration itself. Enumerations are also easy to auto-complete as in almost all cases, the name of the enumeration is the plural form of the name of the property with which it's associated. For example the property TextAlignmment is an enumeration of type TextAlignments. **Handling Errors** - In the past, many functions in Xojo returned error codes when a function could not succeed. While errors are usually the exception not the rule, good software design dictates that your code be prepared for this possibility. Take this example (written how you would write it prior to API 2.0) that connects to a database to find a record, updates it and then returns the shipping status: .. code:: xojo If db.connect Then Var rs As RecordSet rs = db.SQLSelect("SELECT ShipStatus WHERE TrackingNumber = " + TrackingNumber) rs.edit If db.Error Then MsgBox("The record cannot be updated due to an error.") Else Var today As New Date rs.Field("LastChecked").StringValue = Today.SQLDate rs.Update End If Var ShipStatus As String = rs.Field("ShipStatus").StringValue rs.close Return ShipStatus Else MsgBox("Database Error: " + db.ErrorMessage) End If Notice the IF statements required to do the error checking. With API 2.0, nearly all error checking is handled with exceptions. This cleans up the code nicely and puts all the error checking in one place. .. code:: xojo Try db.connect Var rs As RowSet rs = db.SelectSQL("SELECT ShipStatus WHERE TrackingNumber = " + TrackingNumber) rs.EditRow rs.Column("LastChecked").StringValue = DateTime.Now.ToString rs.SaveRow Var ShipStatus As String = rs.Column("ShipStatus").StringValue rs.close Return ShipStatus Catch error As DatabaseException MessageBox("Database Error: " + error.Message) End Try It's cleaner, easier to read, all of the error handling is in one place and it's 25% less code! With API 2.0, nearly everything that used to return an error code has a new API 2.0 method that throws an exception. Put your code into a Try Catch structure like you see above and you can clean up your code quite a bit. **Updating Your User Interface From a Thread** - When you have a long operation that must run but don't want your app to be unusable while it does, you put that long-running code into a :doc:`Thread`. If this long operation was initiated by the user, it's good practice to provide the user with some feedback such as a :doc:`DesktopProgressBar` or :doc:`DesktopProgressWheel`. In previous versions of Xojo, updating these controls from the :ref:`Run` event of a :doc:`Thread` was complicated because (for reasons too complex to discuss here) threads cannot be used to directly manipulate any part of your apps user interface. This process, however, has been made a lot easier in API 2.0. You still can't update your user interface controls from a thread's :ref:`Run` event, but you can update them from the new :ref:`Thread.UserInterfaceUpdate` event. To trigger this event, you simply call :ref:`Thread.AddUserInterfaceUpdate` from within the code in your :ref:`Thread.Run` event (likely inside a loop) and pass to it the data you need to update your user interface controls. For example, if you're updating a :doc:`ProgressBar`, perhaps you'd pass along a number between 1 and 100 representing the percentage of the long operation that is complete. This will cause the :ref:`Thread.UserInterfaceUpdate` event to execute and receive the information you passed it. From this event you can then update your user interface. See the :ref:`Thread.UserInterfaceUpdate` event for an example. .. _/topics/api_design/moving_to_api_2.0/improvements: Improvements ------------ There are a number of under-the-hood improvements to classes as well. For example: **FolderItem** - On macOS, the FolderItem class has been rewritten from scratch using the latest and greatest Mac APIs to ensure compatibility now and in future macOS releases. **URLConnection** - The new URLConnection class (which is part of API 2.0 but was added in Xojo 2019r1), replaces the HTTPSocket class and provides support for HTTP 1.1 and above. It also now relies on the underlying OS's implementation so as operating systems are updating, your apps that use URLConnection will automatically improve. Should the user have a proxy configured, your app will automatically take advantage of it. **Easier, More Secure SQL** - the new :ref:`Database.SelectSQL` and :ref:`Database.ExecuteSQL` methods have built-in support for prepared statements without the need for you to directly use the :doc:`PreparedSQLStatement` classes. .. _/topics/api_design/moving_to_api_2.0/transitioning: Transitioning ------------- **Existing Projects** - Transitioning to API 2.0 can be done at your leisure. Even though many of the functions your projects are using today are now deprecated, they won't be going away any time soon. The first step is to make sure your project runs as-is in 2019r2 prior to making any changes. If it does, then you are ready to transition to API 2.0. Desktop projects created before 2021r3 will continue to use the legacy desktop controls. However, if you wish to use any of the new Desktop-prefixed controls in a project you created prior to 2021r3, you can do so by using the Inspector to change the Super of that control to its Desktop equivalent. For example, a PushButton would be changed to :doc:`DesktopButton`. Desktop projects can also be updated automatically to use the new desktop controls. To update: * The entire project, choosing Project > Update Controls to API 2.0. * A class (including the Application class), right-click on it and choose "Update to ". * A Window and all of its controls, right-click on the window in the Navigator and choose "Update to DesktopWindow". * A ContainerControl and all of its controls, right-click on the ContainerControl in the Navigator and choose "Update to DesktopContainer". * A specific control on a window, right-click on the control and choose "Update to ". * The menubar or a specific menu or menu item, right-click on the menubar or item and choose "Update to "Update to DesktopMenuItem". **New Projects** - New projects will only show API 2.0-compatible items. That means you will only see API 2.0-compatible events in the New Event Handler dialog box, see only API 2.0-compatible methods and properties in auto-complete and only see API 2.0-compatible items when you browse the Documentation. You can go directly to a deprecated item in the Documentation by typing the complete name in the Search field. For example, typing "FolderItem.Item" will take you to the Item method even though it's been deprecated. These deprecated items will remain accessible both in the framework and in the Documentation for many years to give you plenty of time to transition. New desktop projects created in 2021r3 or later will use the new Desktop-prefixed controls. If you prefer to use one of the legacy controls, simply drag out the control you wish and then change its Super property in the Inspector. For example, :doc:`DesktopButton` would be changed back to PushButton. **Updating Desktop Control Subclasses** - If you decide to update a class in your project that subclasses a desktop control, you will need to review any event definitions you have added as some may need to be renamed. For example, if you added an event definition called "Action" to a subclass of Pushbutton and then called that from the PushButton's Action event handler, after converting the project to use the new API 2.0 desktop controls, because the Action event has been changed to Pressed, your code won't compile because it's looking for a Action event that no longer exists. The solution is to change your event definition name to Pressed and update the code in the Pressed event to call Pressed instead of Action. **Auto-Complete** - The first place you will likely notice API 2.0 is when using Auto-Complete. Projects created in versions of Xojo prior to 2019r2 will still show methods, properties and other items that have been deprecated along with their API 2.0 replacements. Projects created in 2019r2 or later will not. .. image:: https://documentation.xojo.com/topics/api_design/images/moving_to_api_2.0_auto-complete_api_2.0.png **Finding Things To Update** - Xojo won't tell you which classes, properties and methods you're using that can be replaced by their API 2.0 counterparts until you ask it. You do this by using the Analyze Project feature. You'll first need to go to Project->Analysis Warnings and check the warning for "Item1 is deprecated. You should use Item2 instead." Once you do this you can select Analyze Project from the Project menu to analyze the entire project or you can click the Analyze Item button in the Code Editor toolbar to analyze a single item. This will produce a list of items in that class or module that can be updated. Depending on the size of your project, the list of things that need to be updated could be quite long. Don't be overwhelmed by this. You project will continue to run as-is so you can take your time and update your project as you need or want to do so. **When You're Ready to Start** - There's no rush your existing code will continue to function for many years. You can update one function, one class or perhaps the entire project at once. It's up to you. **The Nitty-Gritty Details** - For more details on specifics of transitioning, see the :doc:`Updating Older Projects` page. .. _/topics/api_design/moving_to_api_2.0/see_also: .. seealso:: :doc:`Updating Older Projects` topic Application deployment ====================== .. toctree:: :maxdepth: 1 :name: sec-application_deployment Apple requirements Desktop Web Android iOS Apple Requirements ================== .. important:: Xcode and Apple Certificates are needed only **if you plan to build/distribute iOS apps** and/or **distribute macOS** apps through the Mac App Store. .. toctree:: :maxdepth: 1 :name: sec-apple_requirements Installing Xcode and Apple certificates Signing your macOS application Publishing macOS and iOS Apps to the App Store from Xojo ======================================= Installing Xcode and Apple certificates ======================================= .. important:: Xcode and Apple Certificates are needed only if you plan to build iOS apps and/or distribute macOS apps through the Mac App Store. Because Apple only provides some necessary iOS OS development components as part of Xcode, you will have to install Xcode in order for Xojo to be able to run and build iOS apps. You will not have to use Xcode to do any development. You will also need Xcode installed if you wish to codesign your macOS apps in order to make them available in the Mac App Store. These are the parts of Xcode that are used by Xojo: * Xcode Preferences are used to install iOS and macOS certificates on your Mac. * Xcode Devices and Simulators window is used to transfer your Xojo-build iOS apps to devices for testing. * Xcode iOS Simulator is used to run Xojo iOS apps in the debugger. Note that Xojo will start the iOS Simulator for you automatically. .. _/topics/application_deployment/apple_requirements/installing_xcode_and_apple_certificates/download_and_install_xcode: Download and install Xcode -------------------------- You can download and install Xcode from the Mac App Store. After installing Xcode you'll need to run it so that it installs necessary iOS development components that are used for building. .. note:: In some cases you may also need to go to the Xcode Preferences, select the Locations tab and choose the latest version of Xcode for the **Command Line Tools** setting. .. _/topics/application_deployment/apple_requirements/installing_xcode_and_apple_certificates/installing_apple_certificates: Installing Apple certificates ----------------------------- You will need an Apple Development certificate to be able to build iOS apps. You will need an Apple Distribution certificate to be able to distribute iOS and/or macOS apps via Apple's App Stores. .. important:: If you have not already done so, you will need to create an `Apple Developer account `_ before you will be able to create an install the appropriate certificates. Once you have Xcode installed, you will need to use it to create and download your Apple certificates. To install these certificates: 1. Launch **Xcode**. 2. Select **Xcode > Preferences**. 3. Click on **Accounts** in the Preferences window toolbar. 4. Click the **+** button in the lower-left corner of the window to add your Apple Developer account. 5. In the dialog that appears, choose **Apple ID** and click **Continue**. 6. In the dialog that appears, enter your **Apple ID** and password then click **Next**. Your Apple Development account will appear in the list. 7. Click **Manage Certicates**. A dialog appears showing any certificates you already have installed. If you've never done this before, that list will be empty. 8. Click the **+** button in the lower-left corner and choose Apple Development to create an Apple Development certificate. The certificate then appears in the list. 9. Click the **+** button again but this time choose Apple Distribution to create an Apple Distribution certificate. The certificate appears in the list. 10. Click **Done**. Whew! You're done. Apple certificates tend to last a year. You will only have to repeat these steps (without having to add your account again of course) when a certifcate expires. .. _/topics/application_deployment/apple_requirements/installing_xcode_and_apple_certificates/see_also: .. seealso:: :doc:`System Requirements`, :doc:`Testing and debugging your iOS apps`, :doc:`On-Device testing during development`, :doc:`Submitting to the iOS App Store` topics ============================== Signing your macOS application ============================== .. rst-class:: forsearch Signing A feature called GateKeeper was added with the release of OS X 10.8 Mountain Lion in 2012. With this feature new apps that are downloaded or copied to a Mac with OS X 10.8 or newer, but that are not digitally signed using an Apple Developer Certificate, display an error when run: "App" can't be opened because it is from an unidentified developer. On older versions of macOS, this error can be overridden in System Preferences (Security & Privacy), by changing the "Allow applications downloaded from" setting to "Anywhere". Unfortunately, the "Anywhere" option is no longer available with macOS 10.12 Sierra. Alternatively, you can right-click on the app in Finder and click Open in the menu to indicate, "I'd really like to run this app, thank you very much." Note that this only matters for new apps that you transfer to a Mac running macOS 10.8 or later. You'll be able to run the apps you create on your developer machine without this warning. You'll only run into this warning when you copy the app to another Mac, either by making it available for download or by copying it via a USB stick, the network or anything else. So even though you don't technically need to sign your Mac applications in order to avoid this warning, you are probably going to want to. The truth is that most people will just leave the setting at the default and will not know that when they get the warning message that they can right-click on the app to open it. You could try explaining all this to them, but either way it is going to be a hassle for your users. Odds are they just won't bother with your app. The solution is to code-sign your app. .. important:: If you do not sign your macOS app, Xojo will sign it for you with ad-hoc credentials. While this will allow to distribute the app to others, your app must be signed with your own App Store credentials be accepted by Apple for inclusion in the Mac App Store. .. _/topics/application__deployment/apple_requirements/signing_your_mac_application/getting_an_apple_developer_account: Getting an Apple developer account ---------------------------------- To code sign your apps you need to sign up for the `Apple Developer Program `_, which costs $100 a year. .. _/topics/application__deployment/apple_requirements/installing_xcode_and_apple_certificates: Installing Xcode and Apple certificates --------------------------------------- Before you can distribute apps via the Mac App Store, they need to be codesigned. To do that, you will need to :doc:`download and install Xcode` which you will use to create these certificates. It's not difficult to do and you only need to do it once. .. _/topics/application__deployment/apple_requirements/signing_your_mac_application/code_signing_your_app: Code signing your app --------------------- Now you are ready to code sign your application. To do this, you'll need your Apple Development certificate ID: 1. Launch the **Keychain Access** app. 2. Click the **My Certificates** button. 3. In the list that appears, double-click the item that begins with *Apple Development*, followed by your name. 4. In the window that appears, look for the row titled *Common Name*. 5. Select all the text on this line **after** ``Apple Development:``. That is your Apple Development certificate ID. 6. Select **Edit > Copy**. 7. Back in Xojo, go to **Build Settings** in the Navigator and expand the **macOS** item. 8. Click **Sign**. The Inspector shows the properties for signing. 9. In the **Developer ID** field, paste in your Apple Development certificate ID. Your app will now be signed automatically when you build it. Now you can compress/package your app and transfer it to another computer for installation. .. note:: If you are making your application available to the general public, you will want to notarize it first. This step will prevent the user from having to right-click and choose Open to launch your app for the first time. Utilities such as App Wrapper (see below) provide this functionality. Code signing must be done as the absolute last step. If you modify anything inside your application bundle (such as Info.plist) after you code sign, you will invalidate the signature and you'll have to code sign again. For a Build Step, this means it must be the last item after the Build item. .. note:: If you are building your Mac app from Windows or Linux and have already added your certificate to the project, Xojo will produce a script you can run on a Mac to sign your app. This script is saved in the same directory as your project file. For more information about code signing from Apple, refer to the `macOS Code Signing In Depth Technical Note `_ at the Apple Dev Center. .. _/topics/application__deployment/apple_requirements/signing_your_mac_application/3rd_party_alternative: 3rd party alternative --------------------- For more complicated code signing situations you might want to consider a 3rd party code signing tool, such as `App Wrapper `_. ======================================================== Publishing macOS and iOS Apps to the App Store from Xojo ======================================================== .. rst-class:: forsearch Signing Starting with Xojo 2025r1 you will be able to publish your macOS and iOS apps to the App Store Connect website directly from the Xojo IDE. App Store Connect is the place where developers have to create their app records as part of the process so they can be finally available at the Mac App Store and/or iOS App Store, always these are approved by the App Reviewing process done by Apple. Once an App record is created at App Store Connect, every new app build (new version) uploaded from the Xojo IDE will be available there. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/first_things: First things, First! -------------------- But before we look into how to use the new Publish feature from the Xojo IDE itself, let's see some requirements and previous processes to better understand how everything this works and fits together. First of all, the requirements. Very probably you already meet these, but is always a good idea to review them: * A paid `Apple Developer membership `_ (around US $99/yr). * **Xcode** needs to be installed on your Mac, preferably the latest one (as of this writing, Xcode 16.2, what means running macOS Sequoia 15.2 on your computer); but it also will work if you are running Xcode 13 or later on, for example, macOS Ventura. * You have, at least, the **Developer ID Application**, **Apple Distribution** and **3rd Party Mac Developer Installer** certificates installed on your Mac Keychain. * An explicit **App ID** (Identifier) has been created for your app at `developer.apple.com `_ website * A **Provisioning Profile** has been created at `developer.apple.com `_ website, so the uploaded build can be available for users to test under `TestFlight `_. * Verify you don't have any pending agreement waiting for you to be "agreed" both at `developer.apple.com `_ and `appstoreconnect.apple.com `_ website. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/handle_certificates: Handling Certificates --------------------- The best way to make sure you have the right Certificates installed on your Mac Keychain is to handle these from Xcode itself. Open Xcode, go the the Preferences > Accounts and make sure you are logged using your developer.apple.com credentials: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/1-ManageCertificates.png :align: center Then, click the "Manage Certificates..." button and a new window will show you the already installed Certificates (including the expired ones or those whose private key is missing, for example), and also will let you to download and add the missing ones, using for that the dropdown menu with the "+"" icon in the lower-left area of the dialog. Once the required certificates are installed on your Mac, and in order to keep your Keychain as clean as possible, I suggest you to open the Keychain app and delete from your computer all the installed certificates that had been revoked, that had expired or have their private key missing, or that are duplicates, for example. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/handle_app_id: Handling App ID --------------- The App ID, Identifier, or "bundle Identifier" of the app, is something you are familiar with every time a new macOS or iOS app is created from the Xojo IDE: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/2-Identifier.png :align: center But we need to create that same App ID at `developer.apple.com `_ website; so go ahead and use your Apple Developer credentials to access the Apple Developer portal. Next, click the "Identifiers" entry under the "Certificates, IDs & Profiles" header: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/3-Identifiers.png :align: center .. note:: You need to create a new App ID and follow these steps for every new macOS or iOS app you want to distribute through the Mac or iOS App Store. In the page displayed as result of the previous action, click the "+" button next to the Identifiers header in order to register a new Identifier: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/4-NewIdentifier.png :align: center In the next page, make sure the "App IDs" is selected and click the "Continue" button: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/5-New_AppID.png :align: center In the new page, make sure the "App" option is selected and click the "Continue" button: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/6-New_AppID-B.png :align: center Finally, you will arrive to the really interesting part where you have to type the explicit Bundle ID (``A``) matching exactly the one used under Application Identifier when the project was created in the Xojo IDE. Also important, make sure that the App ID Prefix value matches the Team ID value (``B``) of the certificates installed on your computer Keychain by Xcode! .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/7-New_AppID-C.png :align: center Of course, select any Capability and/or App Services your app may require. For the purpose of our example, none of these are selected. Next, click the "Continue" button. This action will bring the summary page where you can review the entered data and Capabilities/App Services selected. If everything is ok, click on the "Register" button. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/8-New_AppID-D.png :align: center Once registered, the new Identifier will be listed among the available ones under the Identifiers section: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/9-Registered_Identifier.png :align: center .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/handling_provisioning_profiles: Handling Provisioning Profiles ------------------------------ TestFlight is the Apple Service allowing developers to get feedback from users (and/or team mates) when an app is still under development, prior is publicly available under the Mac/iOS App Store. So, when a new app build (version) is published from the Xojo IDE, it will be also available to be tested by users via the TestFlight service. But in order that to happen, the app itself needs to have embedded what is known as a `Provisioning Profile `_; and these need to be created also from the developer.apple.com website as we previously did see in the App ID section. There are several types of provisioning profiles (more on this a bit later), but I will focus here on two of them: Development and Distribution. Simplifying, the main difference among these is that Development profiles include the information about the devices an app can be installed into, so they are generally used for internal testing on the user devices or, in the case of the iOS apps, when these are run on a physical device using the "Run On Device" option from the Xojo IDE (thus, also on the registered user devices). In this case we will focus on the creation of a **"Distribution Provisioning Profile"**, so the apps published from the IDE are elegible for TestFlight testing when they are uploaded from the Xojo IDE. Once logged into developer.apple.com website, select the "Profiles" entry under the "Certificates, IDs & Profiles" section. Then, click the "+" button next to the Profiles header in the resulting page: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/11-Profiles-B.png :align: center On the next page, make sure you select the "Mac App Store Connect" option under the Distribution header, because we are going to create a profile for the example macOS app used through this example (for iOS apps you should select the "App Store Connect" option, highlighted in blue in the screenshot), and click the "Continue" button: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/12-Profiles-C.png :align: center On the next page, make sure you select the App ID created in the previous section (in our example it is "com.aprendexojo.chess"). Observe how the App ID is prefixed with the Team ID we choose during the creation of the App ID (in our example "BW7PU32485"); that is: BW7PU32485.com.aprendexojo.chess. Also, make sure that the "Mac" option is selected under "Profile Type" instead of "Mac Catalyst”: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/12-Profiles-D.png :align: center Click on the "Continue" button and, in the next page, make sure to select the same Distribution certificate you are going to use when the Xojo app is built (i.e: the Apple Distribution certificate installed on your Mac). If you have several Distribution certificates installed on your Mac: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/13-Profiles-E.png :align: center Click the "Continue" button and, in the next page, give a meaningful name to the Provisioning Profile, so you are able to easily distinguish it later among other generated provisioning profiles: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/14-Profiles-F.png :align: center When done, click on the "Generate" button and, after a couple of seconds, you will see the Provisioning Profile summary with the "Download" button enabled. Click on it in order to download the Provisioning Profile to your Mac: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/15-Profiles-G.png :align: center .. note:: Provisioning Profiles for iOS: When it is about iOS apps, you will need to create both Development and Distribution provisioning profiles. When creating the Development provisioning profile, make sure to include all the registered devices you want to use for installing and testing the app directly from Xojo (Run On Device option, from the IDE). Also, once these Provisioning profiles are downloaded to your Mac, double-click on them so Xcode gets them installed in the right location (at the time of this writing: Library > Developer > Xcode > User Data > Provisioning Profiles). .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/adding_distribution_provision_profile: Adding the Distribution Provision Profile to your Xojo Project -------------------------------------------------------------- Move the just downloaded macOS Distribution Provision Profile to a better location more related with your Xojo project, and rename it as "embedded.provisionprofile". Next, open your Xojo project and add a new "Copy Files" step selecting the macOS item under Build Settings and using the contextual menu to select the "Add to 'Build Settings' > Build Step > Copy Files" option: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/16-CopyFiles.png :align: center Next, use the "Add file" button from the Copy Files toolbar to select your "embedded.provisionprofile" file, using the following values in the associated Inspector Panel: * **Name:** Distribution Profile * **Applies To:** Release * **Architecture:** Any * **Destination:** Contents Folder .. note:: For Xojo iOS projects, the provisioning profiles will be applied automatically when building or publishing the app, from those installed by Xcode double-clicking on them when generated and downloaded from developer.apple.com. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/app_store_connect_creating_the_record: App Store Connect: Creating the Record for the App -------------------------------------------------- You need to create an App Record for every macOS or iOS app meant to be distributed through the Mac or iOS App Store. In order an app can be uploaded from the Xojo IDE, it is not crucial to fill-in every required field from the several available sections (you can do that at your own pace); but it is, at least, to have an App Record created for it! This is done using your developer credentials to access the appstoreconnect.apple.com website. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/17-ConnectNewRecord.png :align: center Once logged, select the Apps icon and click the "+" button on the next page and select the "New App" option in order to create a new App Record: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/18-ConnectNewRecord-B.png :align: center The previous action will bring a dialog where you have to enter the essential app information so the record can be created: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/19-ConnectNewRecord-C.png :align: center * **Platforms:** Select the "macOS" option (or "iOS" in order to create a new app record for your Xojo iOS apps!). * **Name:** Make sure to enter the same name used in your Xojo project for the app (Build Settings > macOS > Mac App Name or Build Settings > iOS > iOS App Name). Apple can be "picky" about it during the app reviewing process if they differ, because that name will be also the one used in the App Store listing. * **Bundle ID:** Select the App ID you created for the app when followed the steps under the "Handling App ID" section. * **SKU:** Type any arbitrary SKU value that makes sense for you so you can track uniquely this app. * **User Access:** If you are a "solo" developer, it doesn't make much difference the what option you decide to choose. If it is not the case, you can get more control about what members of the Development team can access the app selecting the "Limited Access" option. Once you are confident with the provided information, click on the "Create" button so the new app record can be created. (It may be the case you get an error if something else has registered an app with the same name. If that is the case, you need to choose a new one for your app!) .. note:: App Record values as the App Name and the Bundle ID can be changed later, if needed, from the General > App Information section from the App Record page. Once the App Record has been created, there is a lot of required information to fill in so the app can go through the App Store Review Process and be publicly listed in the Mac / iOS App Store when approved. But, as I said before, you can add it at your own pace. The important thing, by now, is that once the record is created you have everything setup to start uploading your app builds (versions) from the Xojo IDE. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/publishing_mac_apps_from_the_xojo_ide: Publishing Mac Apps from the Xojo IDE ------------------------------------- .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/mac_general_information: General Information ^^^^^^^^^^^^^^^^^^^ Open your Desktop Xojo project in the IDE and select Build Settings > macOS. Then, make sure you have the right/expected values in the associated Inspector Panel for the following fields: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/20-XojoPublish.png :align: center * **Mac App Name:** Matches the one entered for the App Record at appstoreconnect.apple.com * **Bundle Identifier:** Matches the App ID created for the app. * **Category:** Select the category that better fits into your app, among the available ones. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/mac_app_store_connect_setup: App Store Connect Setup ^^^^^^^^^^^^^^^^^^^^^^^ In order the IDE can upload the app to App Store Connect, it requires an app-specific password. You can add it clicking on the App Store Connect > Setup button. If you created such app-specific password in a previous release of Xojo (under Build Settings > Sign > Notarization > Setup), then it is not required to do it again. Also, remember that this setup only needs to be done once, for all your Desktop (macOS) and iOS projects. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/mac_signing_and_sandboxing: Signing and Sandboxing ^^^^^^^^^^^^^^^^^^^^^^ Select Build Settings > macOS > Sign in the project browser in order to access the associated Inspector Panel: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/21-XojoPublish-B.png :align: center * **Developer ID:** Type (or paste) the full string from the Apple Distribution certificate installed on your Mac. In this example it is (without the quotes): "Apple Distribution: Francisco Javier Rodriguez Menendez (BW7PU32485)". Also this certificate should match the same selected when the Distribution Provision Profile was created, and the Team ID (the value between the parenthesis) should match the same selected when the App ID (Identifier) was created for the app at developer.apple.com. * **Sandboxing:** Apps uploaded to the App Store Connect do require to have Sandboxing enabled. So switch-on that option and click the associated "Edit" button in order to enable the required sandboxed features for the purpose of your app. In our example, we only enabled the ability to read/write the selected user files. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/22-XojoPublish-C.png :align: center .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/mac_shared_settings: Shared Settings ^^^^^^^^^^^^^^^ Select Build Settings > Shared in the project browser in order to access the associated Inspector Panel: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/23-XojoPublish-D.png :align: center If you are going to publish the final (release) version of your app, after it has been thoroughly tested, then you probably would want to set the Stage Code value to "Final". Also, make sure to enter the short version string under the Version field, and the copyright information for the app under the Copyright field. .. note:: Did you forget something? Every time you click on the the Publish button (or select the equivalent "Build and Publish to App Store Connect" menu item from the Project menu), the IDE will run a "check list", so if something is needed to be set in the IDE, prior to uploading the app to App Store Connect, the errors will be shown in the IDE's Error Panel pointing the "what" and "where" to fix them! .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/macOS_ErrorPanel.png :align: center .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/mac_app_icon: App Icon ^^^^^^^^ Nothing new here apart from building your macOS app for a regular or "web based" distribution. That is...your app needs an icon in the expected sizes! But when it is about publishing to the Mac/iOS App Store, this requirement is even enforced and we catch it even before we start the app building process to save you compilation and uploading time just to find that an error happened as result. So, make sure you add all the required sizes selecting the App item in the project browser and, then, clicking the Appearance > Icon option in the associated Inspector Panel. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/24-XojoPublish-E.png :align: center That action will open the Icon Editor where you can drag & drop the different icon files for every size or just paste them from your preferred image editor. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/mac_publishing: Publishing! ^^^^^^^^^^ Click on the Publish button. After the "check list" passed without throwing errors, you will see a confirmation dialog. Click the "OK" button to start the process and uploading your app new build to App Store Connect. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/PublishButton.png :align: center .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/ConfirmationDialog.png :align: center If everything went ok, you will see a "Success" dialog at the end of the process; otherwise, if there is some error during the several steps involved in the process, an error message dialog will provide more information about the detected problem and the process will be interrupted bringing you back to the IDE. In both cases, either if your new app build has been successfully sent to App Store Connect or not, you can find the created Log file in the same folder of the built app; so, in case of errors, you can open such Log file in order to find all the received information about the problem(s) so you have more hints in order to fix them before trying it again. For example: .. code:: plain 2025-01-23 12:54:35.030 *** Error: [ContentDelivery.Uploader.6000028E01C0] The provided entity includes an attribute with a value that has already been used (-19232) The bundle version must be higher than the previously uploaded version: ‘1.0.6’. (ID: d422b9bf-049f-4263-af43-8357c2fe5f00) In this case, the Log file entry is telling us that we tried to publish a build having the same version number than one of the already uploaded builds to App Store Connect. So if this new build has, in effect, changes or new features, the way to fix this problem is simply increasing the version number (also for the short version string) before publishing it. .. note:: Is very common for complex Xojo macOS projects to add build steps under the Build Settings > macOS section, as for example those in charge of copying additional required resources to the compiled app bundle. If that is the case, make sure that the Sign build step to be the last one in the list! (That is, the last one to be executed during the build process); otherwise, if “something” else is copied into an already signed app, such action will invalidate the sign of the app… and will lead to errors when publishing the app (and probably even when running the final build even locally). .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/SignAsLastStep.png :align: center .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/publishing_ios_apps_from_the_xojo_ide: Publishing iOS Apps From the Xojo IDE ------------------------------------- Publishing iOS apps from the Xojo IDE is not much different from what we already did see for macOS apps. In this case, when you click the Publish button the first thing done by the IDE is running a “Checklist” and show any of the detected errors (as for example missing icons, or the lack of a previously created app record at `appstoreconnect.apple.com `_), and display them in the Errors Panel of the IDE if that is the case. But before everything that can happen, make sure you have Build Settings > iOS > Code Signing > Build for set to the “AppStore” value; otherwise, even the Publish button will be disabled in the Xojo IDE Toolbar. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/iOSBuildSettings.png :align: center Also, make sure you have a registered App ID at developer.apple.com website that matches the one entered under the Bundle Identifier field. Also, both a Development and Distribution profile needs to be created, downloaded and properly installed on your Mac. Finally, make sure there is a Team selected. If there are no errors during the Publish process, the new iOS app build will be available on the `appstoreconnect.apple.com `_ website as we did see previously for published macOS apps. If there are errors during the upload process, then these will be shown in a dialog and, as we did see for macOS apps, also available in the generated Log file that is saved under the same folder used for the app build. .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/ios_testing_with_testflight: Testing with TestFlight ^^^^^^^^^^^^^^^^^^^^^^^ When you create a new app record in App Store Connect and access to it, you will see one of the tabs in the upper area of the page is named "TestFlight". Click on it, and you will see all the uploaded builds of your app that are elegible to be tested: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/25-TestFlight.png :align: center As you can see, there is a warning icon associated with our just uploaded app build (``A``). That is because Apple requires a bit of additional information from you about the Encryption Export Regulations compliance of the app; so click the associated "Manage" link to access the dialog where you can make your choice about it: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/26-TestFlight-B.png :align: center Once the requirement has been completed, the build status will change to "Ready to Submit" and, as you can see there, it is also telling you that that build will be available for your testers for the next 90 days before expiring. Probably time enough before you send new test builds for them, anyway. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/27-TestFlight-C.png :align: center .. note:: If your apps meet the “None of the algorithms mentioned above” option, then it is possible to surpass this extra step of “Missing Compliance” for new uploaded builds adding an additional key/value pair for the generated .plist file. You can do that using the Plist Editor in the Xojo IDE —Build Settings > macOS > Property List > Edit, on Desktop projects; or Build Settings > iOS > Property List > Edit for iOS projects—, with the following values: ITSAppUsesNonExemptEncryption set to :doc:`False`. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/PropertyListEditor.png :align: center For every one of your apps, it is possible to create as many tester groups as you need. Initially there is only one entry: "Internal Testing". You can create there the groups to add any of the members of your Apple Development Team to any of the groups you decide to create. Clicl on the "+" icon to create the first of these groups. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/28-TestFlight-D.png :align: center Give the new group a name and disable the "Enable automatic distribution" checkbox. Then, click on the "Create" button. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/29-TestFlight-E.png :align: center Once the new internal testing group has been created, you will be able to assign any of the uploaded (and not expired) builds of your app to it, and also add the members for that group (remember, those under your Apple Developer Team!): .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/InternalTestingBuildAndInvite.png :align: center But having Internal testing groups is not of much help when you are a solo or small development Team. The good news is that, as soon you did create the first, mandatory, Internal group, a new option will be added to the TestFlight barside: External Testing. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/ExternalTestingSidebar.png :align: center In this case, you will be able to invite up to 10,000 members to test it. Probably the main difference compared with the Internal groups, is that once you select the build to be tested in any of the external groups, it needs to go through the Beta App Review process. That is, won't be immediately available for your testers until the review process is completed; but this process only is required for the first build, so successive ones will be available immediately as it happens for the ones of the Internal groups. When it is about inviting members for a external group you have several choices, from creating and sharing a public link to manually adding them or even importing them from a .csv formatted file. .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/ExternalTestingPublicLink.png :align: center .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/ExternalTestingInvite.png :align: center .. _/topics/application_deployment/apple_requirements/publishing_macos_and_ios_apps/ios_testflight_from_the_user_side: TestFlight from the User Side ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Both for Internal Testing groups and External Testing groups, when the members receive an Invitation to join (by email or using the public link), they will be able to see some generic information about the app to be tested, and also the main required step in order to be able to test it: install the TestFlight app for macOS through the provided link (in case it is not installed already on the user’s Mac): .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/TestFlightInvitationByEmail.png :align: center Once the TestFlight app is installed on the Mac, it will bring the “Install App” dialog, so the invited testing member only needs to click on it in order to install the app under the usual Applications folder: .. image:: https://documentation.xojo.com/topics/application_deployment/apple_requirements/images/TestFlightInstallApp.png :align: center .. seealso:: :doc:`Property List Editor` topic Desktop application deployment ============================== .. toctree:: :maxdepth: 1 :name: sec-desktop Linux macOS Windows =============================== Desktop app deployment on Linux =============================== Linux apps also have a variety of deployment options. The simplest is to use GZip and let the user put the app wherever they want. You can also use a tool such as InstallJammer that creates a generic installer that works with a variety of Linux platforms. Lastly, you can create separate installers for each Linux distribution. Common installer formats are deb (used by Debian and Ubuntu) and RPM (RedHat Package Maker) used by RedHat. .. _/topics/application_deployment/desktop/linux/generic_installer: Generic installer ***************** `InstallJammer `_ is an open-source product that has a simple user interface for creating an installer that works on a variety of Linux distributions. Unfortunately, this tool is no longer being actively developed. However, it still works well and might be a good choice if you do not use Linux often enough to master creating dedicated installers. .. _/topics/application_deployment/desktop/linux/debian_installer: Debian installer **************** Debian installers are used by Debian-based Linux distributions, such as Ubuntu or Linux Mint. They can be installed by the Synaptic Package Maker or from the terminal. You create Debian installers using the dpkg-deb terminal app. Its usage is far more involved than can be discussed here, but this tutorial describes how you can create a Debian package: * `Debian Binary Package Building HOWTO `_ .. _/topics/application_deployment/desktop/linux/redhat_installer: Redhat installer **************** The Redhat installer format (RPM) is used by Redhat-based Linux distributions. You can create RPM installers using the rpmbuild terminal app. Its usage is far more involved than can be discussed here, although the Fedora Project does have a good walkthrough: * `How to create an RPM package `_ .. _/topics/application_deployment/desktop/linux/redhat/installers/zip: Zip *** A Zip file is an archive of your app. A zip is easy to download and most users understand what they are. They can usually be unzipped by double-clicking on them, which reveals the app itself. The app can then be manually copied to where the user wants it. macOS ===== .. toctree:: :maxdepth: 1 :name: sec-macos-desktop Desktop app deployment on macOS Publishing Mac Apps from the Xojo IDE =============================== Desktop app deployment on macOS =============================== .. rst-class:: forsearch Signing What you need to do to prepare your app for deployment depends on who will be using it: just you, others you know personally or the general public. Yourself only ************* If the app you build will only be used on your own Mac, you can simply press the Build button and use the built app. Others you know personally ************************** If you are going to distribute the app to others you know personally (friends, family or colleagues), there are two options: * Require that the user give the app permission to launch * Protect your app app from malware and modification Require the user to give the app permission to launch ----------------------------------------------------- If you do nothing and the user of your app is running macOS 15 or later, when they launch your app, they will be told that it cannot be opened because Apple could not verify that it's free of malware. In this case, the user can give macOS permission to launch the app by doing the following: 1. Open Settings. 2. Choose Privacy & Security. 3. Find the message "appname" was blocked to protect your Mac. 4. Click the **Open Anyway** button. 5. When the confirmation dialog box appears, click the **Open Anyway** button. 6. Assuming they have administrative access, use Touch ID to authenticate or click the **Use Password** button to enter your username and password. Once they complete these steps, the app will launch. However, they will need to go through these steps every time you send them an update. Protecting your app from malware and modification ------------------------------------------------- To avoid other users having to explicitly give your app permission to run, Apple requires that you sign your app so that they and the user can identify you as the developer and take steps to protect your app against malware and modification (which could be another form of malware). This is done in five steps. Fortunately two of these steps only ever have to be done once and the other three are very simple: Downloading your Apple Mac development certificates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The first step is to get your Apple Mac Development Certificate. This is used to identify you as the developer of the app. Fortunately this is a one-time process. To notarize your app, Apple requires that you have an Apple Developer account. You can get such an account at `Apple's developer website `_. Apple charges $99USD per year for this. This process, once completely, will allow you to complete the process Apple requires for any macOS app you make with just a single click in Xojo. To download your Apple Mac Development Certificate: 1. Log in to `Apple's developer website `_. 2. In the Program resources pane, find the Certificates, IDs and Profiles section. If this does not appear, it's because you have not yet paid Apple the yearly developer fee. 3. In that section, click on **Certificates**. 4. On the Certificates page, click on the row that has your name in the Name column and **Development** in the Type column. 5. Click the **Download** button. Your Mac Development Certificate is downloaded to your Mac. 6. Look in your Downloads folder for a file with the name **development.cer** and double-click it. 7. This will launch the Keychain Access app. You may need to authenticate in order to open it. 8. In the side panel under Default Keychains, click on **Login**. 9. In the main panel, click on **My Certificates**. 10. In the list of certificates that appears, click to select the the one whose name beings with **Developer ID Application:**. You have successfully downloaded our Apple Mac Development Certificate. Getting your App-Password ^^^^^^^^^^^^^^^^^^^^^^^^^ Later on in the process you will need a password from Apple so that Xojo can connect to Apple's servers. This is the second of the one-time processes. Once you have this app-password, you should never need to access it again. It will be used for all of the Xojo apps you build and sign. To get this password: 1. Go to `Apple's developer support page `_ for specific instructions on how to get your app-specific password. 2. Follow those instructions. When you have your app-specific password, copy it to the Clipboard and return to Xojo. 3. Paste this somewhere for now so you'll have it when you need it. Again, you'll only need it once. Signing your app ^^^^^^^^^^^^^^^^ Apple requires that the it be possible to identify the developer that created an app. You do this by digital signing your app. To sign an app: 1. Open the Keychain Access app. 2. In the side panel under Default Keychains, click on **login** to select it. 3. In the main panel, click on **My Certificates**. 4. In the list of certificates that appears, click to select the the one whose name beings with **Developer ID Application:**. 5. In the pane above the list that now displays the information about the selected certificate, select all of the text of the first line that begins with **Developer ID Application:**. Make sure you select all the way through the closing parenthesis. 6. Copy this text to the Clipboard. 7. In your Xojo project, in the Navigator, go to Build Settings. 8. Open the Mac build item and click on **Sign** to select it. 9. In the Inspector, Paste the text you copied into the Developer ID field. When you build your app, it will now be signed with your Apple Developer ID. Protecting your app from malware ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When your app is running, it is possible for malware already on the computer to attempt to breach the memory in which the app is running and make changes. To project against this, by default macOS gives your app has no external access. This means you can't read and/or write files, access the network, the camera, etc. You then indicate what external access your app needs so that it can unlock just those parts. This access permission is done when you build your app. To add access permission: 1. In your Xojo project, in the Navigator, go to Build Settings. 2. Open the Mac build item and click on **Sign** to select it. 3. In the Inspector, click the **switch** next to Hardened Runtime to enable it. 4. Click the **Edit** button. The Hardened Runtime dialog box appears. .. image:: https://documentation.xojo.com/topics/application_deployment/desktop/macos/images/hardened_runtime_dialog.png :scale: 40% 5. Click the checkboxes for anything that your app may require. It's rare that you will need to select any of these but you need the Hardened Runtime option turned on so that macOS will prevent malware from attempting to access your app through these external access points. 6. Click the **Save** button. Protecting your app from modification ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Another form of protection against malware that Apple requires is that you notarize your app. By doing so your app is submitted electronically from Xojo to Apple where it is scanned for malware. This part of the build process can take a few minutes depending on the size of your app and your Internet connection. This step also will prevent the app from launching in the future if it is every modified after being built. Now that you have an Apple Development Certificate, the actual process of notarizing your Xojo app is easy: 1. In your Xojo project, in the Navigator, go to Build Settings. 2. Open the Mac build item and click on **Sign** to select it. 3. In the Inspector, in the Developer ID field, copy the **10 digit alphanumeric string between the parenetheses** to the Clipboard. 4. In the Inspector, click the **switch** next to Notarization to enable it. 5. Click the **Setup** button. The Notarize App-Password dialog box appears. .. image:: https://documentation.xojo.com/topics/application_deployment/desktop/macos/images/notarization_setup_dialog.png :scale: 40% 6. Paste the **Developer ID** you copied to the Clipboard into the **Team ID** field. 7. Copy your app-password from the Apple webpage or from where you store it into the **Password** field. 8. Last but not least, enter your **Apple ID** into the **Apple ID** field. 9. Click the **OK** button. .. _topics.application_deployment.desktop.macos.simple_steps: Simple steps for building your Mac apps ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now that you have completed the one-time setup, building signed and protected apps you can distribute will be simple: 1. In your Xojo project, in the Navigator, go to Build Settings. 2. Open the Mac build item and click on **Sign** to select it. 3. Paste in your Developer ID into the Developer ID field. 4. Switch on **Hardened Runtime**. 5. If necessary, click the **Edit** button and select any options that apply to your app if any then click the **Save** button. 6. Switch on **Notarization**. Once you have done these 6 steps for a project, the information is saved for that project. You will not have to repeat them for this project again. With Notarization enabled, when Xojo is done building your app, it will send it to Apple so that it can do its requires checks. The Xojo IDE will indicate it is doing this during the build process. .. image:: https://documentation.xojo.com/topics/application_deployment/desktop/macos/images/notarizing_build_message.png :scale: 40% .. important:: You only need Hardened Runtime and Notarization enabled for builds you are going to send out to end users. You can save yourself time when building by disabling these options when doing your own testing. The general public ****************** Apps you distribute to the public (whether through your own website, services like DropBox and iCloud or through the Mac App Store) must be signed, the Hardened Runtime options must be selected and the app must be notarized. Under the presumption that you have been through the processes documented above before, to prepare a project to be built for distribution, complete the :ref:`simple steps for building your Mac apps` above. Distributing your app through the Mac App Store ----------------------------------------------- If you choose to distribute your app through the Mac App Store, there's one extra step you will need to take to make it secure enough for Apple to accept it. You need to set sandbox the app. Sandboxing means that the app will have no access to the outside world unless you specific request it. To sandbox your app: 1. In your Xojo project, in the Navigator, go to Build Settings. 2. Open the Mac build item and click on **Sign** to select it. 3. In the Inspector, switch on Sandboxing. 4. Click the **Edit** button. The Sandboxing dialog box appears. .. image:: https://documentation.xojo.com/topics/application_deployment/desktop/macos/images/sandboxing_dialog.png :scale: 40% 5. Select any options that represent parts of the OS your app is using then click the **Save** button. .. note:: Should you select a sandboxing option that has a matching option in the Harden Runtime dialog box (Audio Input, Calendar, Camera, Location or Photos), the matching option will be selected for you automatically. Below is a guide to the Xojo classes that relate to the various sandboxing options. .. csv-table:: :header: "Sandboxing Option", "Related Xojo Classes" :widths: auto "Incoming Connections (Server)", ":doc:`URLConnection`, :doc:`TCPSocket`, :doc:`EasyTCPSocket`, :doc:`SSLSocket`" "Outgoing Connections (Client)", ":doc:`URLConnection`, :doc:`TCPSocket`, :doc:`EasyTCPSocket`, :doc:`SSLSocket`" "Camera", ":doc:`Barcode`" "Printing", ":doc:`PrinterSetup`" "Bluetooth" "Audio Input" "USB", ":doc:`SerialConnection`, :doc:`SerialDevice`" "Contacts" "Location" "Calendar" "User Selected File", ":doc:`FolderItem`" "Download Folder", ":doc:`FolderItem`" "Pictures Folder", ":doc:`FolderItem`" "Music Folder", ":doc:`FolderItem`" "Movies Folder", ":doc:`FolderItem`" .. Important:: Make sure to consider any plugins you are using or declares you are making as they may require access to be granted as well. Notes ***** User entitlements ----------------- The User Entitlements item allows you to add any additional entitlements your app requires that are not listed in the Harden Runtime dialog box. File formats when distributing apps yourself -------------------------------------------- To distribute it yourself, you will first need to put it in an easily downloadable form (zip file, disk image or installer) and then make it available from a website or server such as DropBox or iCloud. .. _topics.application_deployment.macos.zipfile: Zip file ^^^^^^^^ A Zip file is an archive of your app. A zip is easy to download and most users understand what they are. They can usually be unzipped by double-clicking on them, which reveals the app itself. The app can then be manually copied to the Applications folder. You can create a Zip by right-clicking on your app in the Finder and selecting Compress. .. _topics.application_deployment.macos.diskimage: Disk image (DMG) ^^^^^^^^^^^^^^^^ A disk image is a file that simulates a disk or drive. After downloading the file (and double-clicking it), it appears in the sidebar of the Finder as a drive. This is called “mounting” the disk image. When the user clicks on the drive in the Finder sidebar, they see your app and typically drag it to the Applications folder to install it. To create a disk image, you can use the Disk Utility app included with macOS or try one of the many specialized disk image creation tools such as DMG Canvas. A disk image is probably the most common way to install Mac apps, but keep in mind that some users (especially those new to Mac) may find the concept of mounting a drive and dragging a file to the Applications folder very confusing. Also consider that if the user mistakenly tries to run the app directly from the disk image, it may not behave as expected because the disk image is read-only. .. _topics.application_deployment.macos.installer: Installer ^^^^^^^^^ You can also use an actual installer to install a Mac app, but it is not common for apps distributed outside the Mac App Store. To create an installer you can use the PackageMaker tool (included with the Mac Development tools) or you can use the free `Packages installer `_. An installer gives you more control over permissions and other settings, but they can be much more difficult to create than a simple disk image. Also, remember that an installer on Mac (a pkg file) is actually a bundle so you also have to distribute it in a disk image or a Zip. Mac application structure ------------------------- Generally, Mac apps consist of a single App file, called the Application Bundle. You can embed other files into the Application Bundle because it is technically a folder that macOS treats as a file. In Finder, you can right-click on any App and select “Show Package Contents” to see the actual contents of the Application Bundle as a folder. Of course, your app can still have separate files not in the Bundle, in which case you want to make sure that your app is in a folder of its own. Since apps are technically a folder, you have to include them in some sort of container in order to distribute them, such as disk images (DMG), installers and even simple Zip files. .. important:: If you choose to do this, remember that if you are going to be signing the app, you will need to use a :doc:`Build Step` to copy the files into the Application Bundle **before** it's signed. .. _/topics/application_deployment/desktop/macos/validating_the_apple_id: Validating the Apple ID ----------------------- If you are selling your app, you may want to prevent users from copying the purchased app to another computer and running it there (without having to log into their Apple ID). To do this you also need to verify the Apple ID. Verifying an AppleID requires calling a Cocoa API. The code is far too involved to include here, but the `open-source MacOSLib project `_ has classes that you can use for this. ================================= Desktop app deployment on Windows ================================= On Microsoft Windows, apps are deployed using installers. Any installer tool will work on your apps, including: Inno Setup, Advanced Installer, NSIS, InstallForge, CreateInstall and InstallShield. * :doc:`Sample Inno Setup Script for 32-bit apps` * :doc:`Sample Inno Setup Script for 64-bit apps` Note: These installer tools all have to be run on Microsoft Windows in order to create a Windows installer. However, Inno Setup does work with WINE to allow it to be run on macOS or Linux. Regardless, you should always test your installers on actual Windows systems. .. _/topics/application_deployment/desktop/windows/installers: Installers ********** When you create your installer, you need to tell it to include all the files necessary to run the app. At a minimum, this includes the EXE file and the contents of its associated Libs and Resources folders. For example, an app called Sliders would create a file called "Sliders.exe" and a folders called "Sliders Libs" and "Sliders Resources". The Libs folder contains DLLs for libraries, plugins and other associated files needed by your app. If your app has other support files or folders, such as a Resources folder, then make sure that your installer includes them as well. .. _/topics/application_deployment/desktop/windows/setup.exe_or_msi: Setup.exe or MSI ^^^^^^^^^^^^^^^^ Most installer tools allow you to create your installer as a Setup.exe file or as an MSI (Microsoft Installer) file. Either work fine, but MSI files have the advantage of being the current recommended method from Microsoft and can be used by IT departments for better control of installations. Choose what works best for your customers. .. _/topics/application_deployment/desktop/windows/location_and_shortcuts: Location and shortcuts ^^^^^^^^^^^^^^^^^^^^^^ Windows apps are installed to the Program Files folder. On 64-bit systems, 32-bit apps are installed to the Program Files (x86) folder. Windows users expect to have easy access to your application, so this means you should create easily accessible shortcuts. Your installer tool should provide you with the option of creating a shortcut for the user on the Desktop and in the Start Menu. .. _/topics/application_deployment/desktop/windows/windows/installers/zip: Zip *** For very simple distribution, you can Zip the application and its supporting files (such as the Libs folder). You can create a Zip by right-clicking on the parent folder in Windows Explorer and selecting Send To -> Compressed (zipped) Folder. Once unzipped, the application can be run from any location. Although this can be useful for testing purposes, it is not recommended for proper Windows application deployment. .. _/topics/application_deployment/desktop/windows/microsoft_redistributable_files: Microsoft redistributable files ******************************* Your applications require the :doc:`Windows Universal Runtime`. Your installer should include the necessary redistributable in order for you app to work properly. .. _desktop_app_deployment.submitting_your_app_to_the_windows_app_store: Submitting your app to the Windows App Store ******************************************** Microsoft provides `information `_ on what is required to submit your app to the Windows App Store. Web === .. toctree:: :maxdepth: 1 :name: sec-web Deployment Overview Deployment Details Deploying Web Apps on IIS Deploying Web Apps on Linux =================== Deployment overview =================== There are two ways to deploy web apps created with Xojo: Standalone and Xojo Cloud. You can deploy your web apps to Virtual Private Servers (VPS) with proper configuration. **We do not recommend shared hosting services** as they typically do not work well due to a general lack of configurability and because they can create potential security issues. For the simplest hosting and deployment experience with built-in SSL, one-click deployment and database support, you should consider using :doc:`Xojo Cloud`. .. note:: Deploying a web app as a CGI is no longer supported as of Xojo 2020r1. .. _/topics/application_deployment/web/deployment_overview/server_configuration_help: Server configuration help ------------------------- Xojo does not provide support for configuring your own web server to run Standalone Xojo web applications. If you require easy, one-click deployment of your Xojo web apps consider using Xojo Cloud. These topics give an overview of what is required, but many things can vary between web servers. .. _/topics/application_deployment/web/deployment_overview/launching_your_app_on_your_server: Launching your app on your server --------------------------------- A Standalone web app is an app that you manually run on your server. Once you transfer the app to the server, you have to start the app (usually from the command line) and leave it running in order for people to access the web app. In addition, a Standalone web app is accessed through a port, which you specify when building the app. Essentially, a standalone web app consists of both the web server and your web app. To start a web app, use the command line. In its simplest form, you can just type the name of the web app: .. code:: plain ./MyWebApplication You can also change many settings using command-line options. This command starts the web app on a specific port: .. code:: plain ./MyWebApplication --port=9191 For more information about command-line options, refer to the :doc:`Deployment Details` page. A deployed web app would be accessed with a URL such as this that includes the port: .. code:: plain https://www.mywebsite.com:8080 .. _/topics/application_deployment/web/deployment_overview/xojo_cloud: Xojo Cloud ---------- Xojo Cloud is for those that want the fastest and easiest way to deploy web applications. Xojo Cloud is a fully managed, highly secure hosting environment for your Xojo web apps and has these features: * Excellent security with free SSL support * One-click deployment directly from Xojo * Automatic Load Balancing to support the more concurrent users * Domain and Subdomain support * Remote Server and Application Monitoring * SFTP support * MySQL * PostgreSQL * `SQLite` * Remote Database Access * Unlimited app deployments (CPU, disk space and memory permitting) Refer to the :doc:`Xojo Cloud` topic for more information. .. _/topics/application_deployment/web/deployment_overview/platforms: Platforms --------- If you decide not to use Xojo Cloud, your web app can be compiled for any of the OS platforms supported by Xojo. When it comes to web servers, Linux is the most commonly used operating system, followed by Windows and then macOS. To build your web app, click the Build button in the toolbar. If it is not enabled, make sure you have also selected one of the platforms listed below. Build is disabled if only Xojo Cloud is selected. .. _/topics/application_deployment/web/deployment_overview/linux: Linux ***** In complete opposition to the situation on the desktop, the majority of web servers use some form of Linux. The two most common types of hosting are shared and VPS (Virtual Private Server). Shared hosting usually costs less, but is also often rather restricted. Most shared hosting providers work best with static web sites or pre-configured tools (such as WordPress) and do not allow general purpose apps to run on them. They are rarely a good choice for a Xojo web app. Your best choice is to use a VPS (Virtual Private Server) to host your Xojo web apps. A VPS gives you your own server, usually running the Apache web server, with its own specs running inside of a Virtual Machine (VM). With a VPS you have complete control over the server and can configure it to run Xojo web apps. .. _/topics/application_deployment/web/deployment_overview/windows: Windows ******* Windows web servers primarily use IIS (Internet Information Server), but they can also run Apache. Windows servers are far less common than Linux servers. There are also fewer hosting companies offering Windows servers and they usually cost more. If you are using a Windows server, it is recommended that you run a standalone Xojo web app configured to start as a service. If you wish to use IIS, it is recommended you use IIS as a reverse proxy to a Xojo standalone web app. * :doc:`Deploy Web App to IIS` .. _/topics/application_deployment/web/deployment_overview/mac: Mac *** Mac web servers typically use Apache and setup is mostly the same as it is for Linux. There are even fewer Mac servers in use than Windows, but Mac servers can be simpler to configure. There are no known hosting companies that offer Mac web server hosting, but there are several that offer colocation services for your own Mac hardware. If you want to install a web server on macOS, consider using `MAMP `_. .. _/topics/application_deployment/web/deployment_overview/test_apps_for_deployment: Test apps for deployment ------------------------ To help you test deployment of Xojo apps on your web server, here are several versions of a simple compiled app for you to try. Use them to verify that your web server provider will be capable of running Xojo web apps. With both examples, connect on port 8080: .. csv-table:: :header: "OS Type", "Operating Systems", "Download" :widths: auto "32-bit","Linux, Windows, Raspberry Pi",`Download `_ "64-bit","Linux, Windows, Mac",`Download `_ .. _/topics/application_deployment/web/deployment_overview/troubleshooting: Troubleshooting --------------- If you are having difficulty getting a web app running on your server, perhaps these tips will help: * Verify that you compiled your web app for your Server OS and not your desktop OS. * Check the permissions for the files and folders containing your web app. * Ensure that the web app and the libraries in the Libs folder are all set to be executable. * Always check your server logs to see if they have additional information. Xojo does not provide support for configuring your web server for use with Xojo web apps. If you require easy, one-click deployment of your Xojo web apps consider using :doc:`Xojo Cloud`. .. _/topics/application_deployment/web/deployment_overview/see_also: .. seealso:: For more information about web app deployment: * :doc:`Web Apps` * :doc:`Web App Deployment Details` * :doc:`Xojo Cloud` * :doc:`Deploy Web App to Linux` * :doc:`Deploy Web App to IIS` ================== Deployment details ================== Web apps can be deployed on your own server or on Xojo Cloud. In addition to differences in how they operate, they are also deployed differently. .. _/getting_started/application_structure/web/deployment_details/starting_and_accessing_your_web_application: Starting and accessing your web application ------------------------------------------- When you create a web app, you get a single application that consists of both your app and a standalone web server. This web server listens on the port specified in the Shared Build settings. To start your web app, you launch the app from the command line, terminal or ssh connection. This command runs a web app on macOS or Linux: .. code:: plain ./mywebapp You can run multiple web app on the same server, but each web application has to be listening on a unique port and must have unique Application Identifiers. To make it easier to launch on a specific port, you can include the port as a command-line parameter: .. code:: plain ./mywebapp --port=8080 If you have the port set at 8080 for example, then you can access the web server using the URL followed by the port like this: .. code:: plain http://www.myserver.com:8080 For more specific information about Linux, refer to :doc:`Deploying Web Apps on Linux`. .. _/getting_started/application_structure/web/deployment_details/command_line_parameters: Command Line parameters *********************** You can use command-line parameters to change settings when you launch the web app. .. note:: Assign values to the parameters using the = separator, not a blank space. For example, to specify a secure port: **./mywebapp --secureport=8100** For full details refer take a look at the Command-line parameters at :doc:`WebApplication` page. .. _/getting_started/application_structure/web/deployment_details/using_port_80: Using port 80 ************* In order to navigate to a web app without specifying the port, you need to use port 80, the standard port used by web browsers. However, macOS and Linux do not allow you to use port 80 (or any port lower than 1024) unless you have root access. This means that on macOS and Linux you need to use the sudo command to start your web application if you are using port 80: .. code:: plain sudo ./mywebapp Note: The sudo command prompts you for your user name and password. .. _/getting_started/application_structure/web/deployment_details/background_processing: Background processing ********************* When you run your web app directly from the command line, it will continue running as long as you don't quit the command/terminal window or log out of the account. This technique does not work well on true servers or remote servers. In these cases you want to run the web app as a background process. On Windows this means, you would run the web app as a Windows Service. On Linux it means you would run it as a Daemon. On macOS, you can run it as a daemon or use the preferred launchd command to start it as a daemon. .. _/getting_started/application_structure/web/deployment_details/deploying_with_ssl: Deploying with SSL ****************** In order to deploy a secure web app with SSL, you first need an SSL certificate. You can use a self-signed certificate for local testing, but always purchase a real certificate for publicly available web sites. The certificate file used by Xojo is a text file containing the components of your SSL certificate. Generally, a self-signed certificate will have two components (the certificate and key file) while a purchased certificate will likely have three or more (certificate, key, CABundle or intermediates). Create the text file with the same name as your app (minus the app extension if any) and add the ".crt" extension. Then paste the contents of your certificate files in this order: 1. Certificate #. CABundle #. Private Key Start each file on a new line. This Xojo certificate file must be placed next to the built web app when it runs. This can be done using a Copy Files Build Step with Build Automation. Now you can start your web app and tell it to listen for connections on a secure port. This uses the secureport and maxsecuresockets command-line parameters described in the preceding section. The secure port must be different than the (non-secure) port selected in the Shared Build Settings. It also must be different than a port specified with the port command-line parameter. For example, if you want to launch your web app on secure port 8081, use this command: .. code:: plain MyWebApp --secureport=8081 Once you have launched the app, you can connect to it in the browser like this (for a web app running locally): .. code:: plain https://127.0.0.1:8081 If you also want to prevent unsecured access to the web app, you can set the number of unsecured sockets to 0: .. code:: plain MyWebApp --secureport=8081 --maxsockets=0 Refer to :doc:`SSL for Web Apps` for more information. .. _/getting_started/application_structure/web/deployment_details/windows_service: Windows service *************** To run a web app as a Windows Service, you use the sc command from the Command Line to install the app as a service: .. code:: plain sc create MyWebAppSvc type= own start= auto binpath= c:\\Path\\To\\Exe\\mywebapp.exe Now you can go to the Services Manger in the Control Panel and see the “MyWebAppSvc” service listed. Use Services Manager to Start, Stop or Pause the service. .. _/getting_started/application_structure/web/deployment_details/running_in_the_background: Running in the background ************************* To make your web app run as a background process on macOS or Linux, you can launch it from the command line like this: ./MyWebApp & When you now run the web app from the terminal, it will immediately return you to the terminal prompt because the app is now running in the background. On Linux, you can also use `systemd `_. If you don't want to deal with this level of complication, consider using :doc:`Xojo Cloud`. This also works on macOS, but Apple prefers that you use the launchd command to start daemons. Using launchd means you have to create a Property List file with the specific settings. For more information, refer to Apple's documentation on this: * `Creating Launchd Jobs `_ Xojo does not provide support for configuring your web server for use with Xojo web apps. If you require easy, one-click deployment of your Xojo web apps consider using :doc:`Xojo Cloud`. .. _/getting_started/application_structure/web/deployment_details/troubleshooting: Troubleshooting *************** Unfortunately, not all servers are going to be properly configured to run Xojo web apps. You may find that you have to change the configuration yourself. Below are some common areas to check. Step-by-step instructions are not possible because every installation of Apache is different, having configuration files in different places with different web server users and different permissions. Use these tips as guidelines, but you 'll still need an understanding of Apache and how it has been installed on the OS you are using. * **OS**: Verify that you built and uploaded your web app for the correct OS. Although you may be developing and testing on macOS, if you are using a Linux server you need to remember to make sure that you create a Linux build to upload. * **Web Server Logs**: Your web server logs may have additional useful information. The location of these logs varies depending on the Linux distribution and version. On macOS and Windows, consider using the logging option in Command Line Parameters above. * Check the permissions for the files and folders containing your web app. * Ensure that the web app and the libraries in the Libs folder are all set to be executable. Xojo does not provide support for configuring your web server for use with Xojo web apps. If you require easy, one-click deployment of your Xojo web apps consider using :doc:`Xojo Cloud`. .. _/getting_started/application_structure/web/deployment_details/iis_(microsoft_internet_information_services): IIS (Microsoft Internet Information Services) --------------------------------------------- Included with Windows is a web server called IIS. This web server is ideally suited for running simple HTML web sites, .NET web applications and other specific Microsoft web products. Xojo recommends using IIS as a reverse proxy to a your web app. More information is here: * :doc:`Deploying Web Apps on IIS` Xojo does not provide support for configuring your web server for use with Xojo web apps. If you require easy, one-click deployment of your Xojo web apps consider using :doc:`Xojo Cloud`. .. _/getting_started/application_structure/web/deployment_details/xojo_cloud: Xojo Cloud ---------- If all this setup and configuration is too much to bother with, you should consider hosting your web app using Xojo Cloud. This service allows you to deploy a web app directly from Xojo by clicking the Deploy button on the toolbar. With Xojo Cloud, you just check the "Xojo Cloud" target in Build Settings for your web project and then select it to specify the name of the application and the Xojo Cloud server on which to deploy. Then you simply click the Deploy button in the toolbar and your app is built and uploaded to your Xojo Cloud server. Refer to the :doc:`Introduction to Xojo Cloud` topic for more information. ========================= Deploying web apps on IIS ========================= Internet Information Services (IIS) is the name of the web server included with most versions of Microsoft Windows. The suggested way to use Xojo web apps with IIS is to configure IIS as a `reverse proxy `_ to a Xojo standalone web app. Alternatively, you can instead not use IIS at all and run the Xojo standalone web app as a background service on Windows. .. note:: Xojo does not provide support for configuring IIS (or any other web server) for use with Xojo web apps. If you require easy, one-click deployment of your Xojo web apps consider using :doc:`Xojo Cloud`. .. _/getting_started/application_structure/web/deploying_web_apps_on_iis/community_guidance: Community guidance ------------------ Although not supported by Xojo, the topics linked below were created by Xojo users to describe how they have configured IIS to work with Xojo web apps and may be useful: * `Windows Testing and Deployment of Stand-alone Xojo Web Apps `_ (Axis Direct Ltd) * `IIS Reverse Proxy Configuration `_ .. _/getting_started/application_structure/web/deploying_web_apps_on_iis/see_also: .. seealso:: :doc:`Run Web Apps in the Background` =========================== Deploying web apps on Linux =========================== .. _/topics/application_deployment/web/deploying_web_apps_on_linux/introduction: Introduction ------------ Web apps are often much simpler for your users and customers to use than desktop apps. They don't have to worry about installing any software or dealing with updates because now you do. Instead of your customers deploying your software on their computers, you have to deploy the software on your web servers. You can deploy your web apps to Virtual Private Servers (VPS) with proper configuration. Shared Hosting typically does not work due to general lack of configurability. For the simplest hosting and deployment experience, you should consider using :doc:`Xojo Cloud`. Additional information here: * Web App Deployment Overview * Web App Deployment Details .. _/topics/application_deployment/web/deploying_web_apps_on_linux/deployment_steps: Deployment steps ---------------- 1. Build your web app for Linux. #. Connect to your web server using the SFTP client of your choice. #. Navigate to the folder where you want your web app. #. Upload your web app (including the Libs and Resources folders) using **Binary mode**. #. Verify execute flag. Now you need to run your web app on the server. You'll need to connect to the server in order to do this, using ssh (Secure Shell). Secure Shell (and the ssh command) are available from the terminal or command line or Windows, macOS and Linux. 1. Connect to the server using ssh (secure shell) with this command: ssh login@mywebsite.co #. Enter the password when prompted. #. Navigate to the folder where you uploaded your web app. #. Start the app using this command: ./TestApp You can now access your web app in your web browser using the domain name and port. If "TestApp" was compiled to use port 8080, you can access it using a URL like this: http://www.mywebsite.com:8080 Note that if you exit from ssh your web app will terminate unless you daemonize it. Use this code in the App.Opening event to daemonize a standalone web app: .. code:: xojo Call Daemonize On macOS, the use of Daemonize is `discouraged by Apple `_. You should use `launchd `_ instead. .. note:: Xojo does not provide support for configuring your web server for use with Xojo web apps. If you require easy, one-click deployment of your Xojo web apps consider using :doc:`Xojo Cloud`. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/load_balancing: Load balancing ************** Using load balancing software such as HAProxy or NGINX with standalone web apps can be a way to greatly increase concurrent users and improve performance. Watch this XDC video for more information: `Load Balancing and Other Techniques for Enterprise Web Apps `_ .. note:: Xojo does not provide support for configuring your web server for use with Xojo web apps. If you require easy, one-click deployment of your Xojo web apps consider using :doc:`Xojo Cloud`. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/troubleshooting: Troubleshooting --------------- Things don't always go this smoothly, however. This section has some tips for you as you troubleshoot. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/32-bit_libraries: 32-bit libraries **************** By default, 64-bit version of Linux do not include the 32-bit libraries that are needed by 32-bit Xojo web apps. Please refer to the System Requirements for details on how to apply the 32-bit libraries to your Linux distribution. Also make sure that the libicu library is available. You can avoid this step by building your web app as a 64-bit app. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/dependencies: Dependencies ************ Not all Linux installations have the necessary dependencies for Xojo apps pre-installed. Refer to the System Requirements for details. To determine which libraries are used by Xojo, you can use the ldd command: .. code:: plain ldd Xojo .. _/topics/application_deployment/web/deploying_web_apps_on_linux/web_logs: Web logs ******** If you get an "Internal Server Error" or any error, you need to check the web server log for specifics. A common source of problems is incorrect permissions. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/permissions: Permissions *********** Your web app needs to have the correct permissions enabled so that the web server can run the web app. This is done using the chown and chmod commands. The necessary permissions vary by the Linux distribution and Apache installation. Permissions of "755" are often a good starting point. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/executable_settings: Executable settings ******************* When uploading your web app to the server, ensure the app and its libs folder are uploaded in binary mode, everything else in ASCII mode. Many FTP clients get this correct automatically, but some do not. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/unable_to_locate_a_library_in_libs_folder: Unable to locate a library in libs folder ***************************************** First, ensure you have uploaded all the contents of the Libs folder in Binary mode. Check the Options FollowSymLinks in your Apache configuration. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/internal_server_error: Internal server error ********************* This can mean any number of things. Check your web server logs. .. _/topics/application_deployment/web/deploying_web_apps_on_linux/see_also: .. seealso:: :doc:`Deployment Overview`, :doc:`Deployment Details` topics Android ======= .. toctree:: :maxdepth: 1 :name: sec-android Publishing your app to the Google Play Store Publishing your app via a URL iOS === .. toctree:: :maxdepth: 1 :name: sec-ios Advanced Build Settings Deployment overview On-Device testing during development Meeting Apple's privacy requirements Preparing your iOS device for development testing Property List Editor Submitting to TestFlight Submitting to the iOS App Store Publishing iOS Apps From the Xojo IDE ======================= Advanced Build Settings ======================= The Xojo IDE provides an additional tab in the Inspector for advanced build settings in iOS projects. You can access it by selecting "iOS" under the "Build Settings" section in the Navigator, then clicking the gear icon in the Inspector. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/IDE_iOS_Advanced_Build_Settings.png Entitlements ------------ Allows the user to provide the XML formatted file with the Capabilities / entitlements not found under the Capabilities section. Capabilities ------------ App Groups ********** An app group allows multiple apps developed by the same team to access one or more shared containers. Registering App Groups: https://developer.apple.com/help/account/identifiers/register-an-app-group Apple Pay ********* Enable and configure this capability when your iOS app is going to process Apple Pay payments. When configuring this capability, it is necessary to add the list of merchant IDs for which your app can accept Apple Pay payments. These are typically in reverse domain notation, starting with the string merchant. For example ``merchant.com.yourcompany``. Associated Domains ****************** Associated domains establish a secure association between domains and your app so you can share credentials or provide features in your app from your website. For example, an online retailer may offer an app to accompany their website and enhance the user experience. Shared web credentials, universal links, Handoff, and App Clips all use associated domains. Associated domains provide the underpinning to universal links, a feature that allows an app to present content in place of all or part of its website. Users who don't download the app get the same information in a web browser instead of the native app. More information about how to configure this capability: https://developer.apple.com/documentation/xcode/configuring-an-associated-domain Background Modes **************** There are a limited number of background execution modes your app can support that enable it to run when in the background, such as playing audio, receiving location updates, or processing scheduled tasks. For apps that adopt one or more of these modes, the system launches or resumes the app, in the background, and affords it time to process any related events. The modes are: * Audio, Airplay and Picture in Picture (Audio) * Location updates (Location) * Voice over IP (VOIP) * External accessory communication (External Accessory) * Uses Bluetooth LE accessories (Bluetooth Central) * Acts as a Bluetooth LE accessory (Bluetooth Peripheral) * Background fetch (Fetch) * Push notifications (Remote Notification) * Background processing (Background Processing) More information about how to configure this capability: https://developer.apple.com/documentation/xcode/configuring-background-execution-modes Camera Access ************* In iOS the user must explicitly grant permission for each app to access the camera and microphone. Before your app can use a capture device for the first time, the system presents an alert with an app-specific message that you specify, to ask the user to grant your app access to the capture device. For example, this capability is required when using the :doc:`MobileImagePicker` control or the :doc:`Barcode` class. Custom URL Schemes ****************** Custom URL schemes provide a way to reference resources inside your app. Users tapping a custom URL in an email, for example, launch your app in a specified context. Other apps can also trigger your app to launch with specific context data; for example, a photo library app might display a specified image. URLs must start with your custom scheme name. Add parameters for any options your app supports. For example, a photo library app might define a URL format that includes the name or index of a photo album to display. Examples of URLs for such a scheme could include the following: * myphotoapp:albumname?name="albumname" * myphotoapp:albumname?index=1 URLs are received by your app in the :ref:`App.HandleURL` event. Data Protection *************** Data protection allows an app that accesses sensitive user data to use the built-in encryption available on some devices. When your applications designates a specific file as protected, the system stores that file on-disk in an encrypted format In addition to enabling this capability, the provisioning profiles created for the app (Distribution or Development) also are required to include the com.apple.developer.default-data-protection entitlement. For use with Declares. File Sharing ************ When you enable File Sharing for an iOS app, you can use the macOS Finder to sync files to or from the app's Documentation folder on the device. In order to enable File Sharing, click on iOS under Build Settings in the Xojo IDE Navigator then in the Inspector, click the File Sharing switch. Build your app and deploy it to a device. Now any files that your app creates in the SpecialFolder.Documents folder are now accessible through the Finder when your device is connected to the computer via USB. Also indicates whether your app shares its Documents folder through iTunes. Game Center *********** For gaming apps, this allows players to connect to the Game Center service, enabling them to interact with their friends, view leader boards, or play head to head in your game. .. Note:: Toggling this switch also indicates that the device must be capable of running GameKit. Health Kit ********** Allows your app, with user permission, to store and retrieve personal health information. Home Kit ******** Allows your app to interact with HomeKit accessories and create home configurations. iCloud ****** Enables your app to store data and documents in iCloud. It requires adding the relevant entitlements to the profiles and also accessing the files using Declares. (Relevant thread in the Forum: https://forum.xojo.com/t/store-file-to-icloud/72825/20) Inter-App Audio *************** Allows your app to send audio and to receive audio from other inter-app audio-enabled apps. .. Note:: This was deprecated by Apple in iOS 13. Keychain Sharing **************** Sharing keychain items between multiple targets of the same app, or between different apps that belong to the same developer, relies on the concept of an access group — a collection of targets that all share a common keychain group. When a particular target wants to make a keychain item accessible to the rest of the access group, it specifies the shared keychain group when writing that item to the keychain, which can be useful for apps that need to share account credentials and other sensitive information. Location ******** Allows you to add messages that will be displayed when asking users for access to their location. There is one message each for When In Use and Always. Maps **** A routing app, an iOS app that provides point-to-point directions, requires Maps capability to make those directions available to Maps and other apps. Maps capability allows an app to provide specific directions beyond what the Maps app supports, including subway routes, hiking trails, and bike paths. Personal VPN ************ With the Personal VPN feature in macOS and iOS, your app can create and manage a VPN configuration that uses one of the built-in VPN protocols (IPsec or IKEv2). The user must explicitly authorize your app the first time it saves a VPN configuration. More info for configuration: https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.networking.vpn.api?language=objc Photos Access ************* Allows you to add a message indicating why your app needs access to the user's Photo Library. This is used with the :doc:`Image Picker` control. Push Notifications ****************** Specifies whether the device should use the development or production Apple Push Notification service when registering for push notifications. Xojo will automatically set this value based on whether or not you are building for the App Store. This works with Xojo's :doc:`NotificationCenter` class. Shortcut Items ************** Allows you to specify the default set of shortcut items. The editor accepts a comma delimited list of SFSymbols, UIApplicationShortcutIconTypes and/or images from your project. Time-Sensitive Notifications **************************** This priority is just like Active for Notifications, except that it can break through system controls such as notification summary and Focus if it has been allowed by the user. This priority should only be used when the notification requires immediate attention. For example, medication reminders, account security and package delivery alerts. Related Documentation: https://documentation.xojo.com/api/user_interface/notifications/notificationcontent.html User Authentication ******************* Allows your app to use Touch ID/Face ID. Enable this when using Xojo's :doc:`UserAuthentication` class. Wallet ****** Allows your app to add items to and delete items from the Wallet. The Wallet app on iOS allows users to organize their passes — tickets, gift cards, loyalty cards, boarding passes, and the payment cards they use with Apple Pay. By integrating with PassKit (Apple Pay and Wallet), your app can access any related passes and allow the user to manage them. Wireless Accessories ******************** Allows your app to configure MFi Wi-Fi accessories which are devices compatible with HomeKit, AirPlay Audio, Find My and CarPlay. Privacy ******* Apple did introduce a privacy policy that requires the inclusion of a privacy manifest file in new and updated apps created for iOS, iPadOS, tvOS and VisionOS distributed through the AppStore. if your apps only relies on the Xojo provided iOS framework functionality, then you don't need to do anything at all. When building or running the iOS app, Xojo will create and add the required **PrivacyInfo.xcprivacy** file for you. If you are using Declare calls or rely on iOS third party libraries or plug-ins, then you should check with the developer to know if any of the calls made fall into the Privacy Categories listed by Apple (keep in mind that these can change over time), and select those under the Privacy Editor. Related Blog Post: https://blog.xojo.com/2024/06/26/ios-privacy-editor/ ======================= iOS deployment overview ======================= There are several ways to deploy iOS apps to devices, which are briefly outlined below. .. _/topics/application_structure/ios/ios_deployment_overview/ad-hoc_deployment_to_devices: Ad-Hoc deployment to devices ---------------------------- With Ad-Hoc Deployment, you build your iOS apps using an iOS Development Profile and manually copy the built app to the device. To create this profile you need to be a member of the `Apple Developer Program `_ (currently $99/year from Apple) or you can use the free provision profile provided by Apple. You can deploy apps built in this manner on up to 100 devices. For details on the steps to do this, refer to :doc:`On-Device Testing During Development`. .. _/topics/application_structure/ios/ios_deployment_overview/testflight: TestFlight ---------- For beta testing purposes, you can also use Apple's TestFlight server to allow up to 1000 users to install a beta version of your app. Use of TestFlight requires that you are a member of the `Apple Developer Program `_. For details on the steps for using TestFlight, refer to the :doc:`Submitting to TestFlight` topic. .. _/topics/application_structure/ios/ios_deployment_overview/app_store: App store --------- To get your app in the hands of end users, you will typically want to use the App Store. To do so, you will build your app using an iOS Distribution Profile. You will have to be a member of the `Apple Developer Program `_. For details on the steps to submit to to the App Store, refer to the :doc:`Submitting to the App Store` topic. .. _/topics/application_structure/ios/ios_deployment_overview/volume_purchase_program: Volume purchase program ----------------------- The Volume Purchase Program provides a way for you to distribute apps to businesses or educational institutions without having to publish your app to the App Store. * `Volume Purchase Program for Business `_ * `Volume Purchase Program for Education IT `_ .. _/topics/application_structure/ios/ios_deployment_overview/enterprise_deployment: Enterprise deployment --------------------- Businesses can use the iOS Developer Enterprise Program to get tools and resources for developing proprietary, in-house iOS apps that you can distribute to your employees. These apps are distributed outside of the App Store. For more information, visit Apple's `iOS Developer Enterprise Program `_ page. .. _/topics/application_structure/ios/ios_deployment_overview/see_also: .. seealso:: :doc:`On-Device Testing During Development`, :doc:`Submitting to TestFlight`, :doc:`Submitting to the iOS App Store` topics ==================================== On-Device testing during development ==================================== .. rst-class:: forsearch Signing Each time you create a new iOS app you wish to install on an iOS device for testing, you'll need to create a provisioning profile for that app. Below are the instructions to do so. .. _/topics/application_structure/ios/testing_during_development/requirements: Requirements ------------ To create a provisioning profile, you will need to have :ref:`a version of XCode supported by Xojo`. .. _/topics/application_structure/ios/testing_during_development/logging_in_to_xcode: Logging in to XCode ------------------- XCode needs to be made aware of your Apple ID. You will need to add it to XCode in order to login via your Apple ID. This is a one-time only operation. Once you do it the first time, you will never have to do it again for future iOS projects. 1. If you do not have an Apple ID you can use, you can create a new one at `appleid.apple.com `_. #. Start Xcode and open Settings (Xcode menu > Settings). #. Select the Accounts tab and click the "+" button to add your Apple ID. This adds a Team Name which should be selected by default. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/accounts.png 4. With your Team Name selected, click Manage Certificates. In the dialog that appears, click + button then choose Apple Development. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/apple_development.png 5. This creates the Signing Identity. You should see the name of your Mac with today's date. When finished, click the Done button. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/signing_certificates.png .. _/topics/application_structure/ios/creating_a_provisioning_profile_for_your_app: Creating a provisioning profile for your app -------------------------------------------- For each app you create, you need to have XCode build a provisioning profile. 1. Plug in the iOS device (via USB) that you will use to test your app during development. #. Create a new Xcode project: Choose File > New Project and select "iOS App" in the dialog and the click the Next button. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/choose_template_dialog.png 3. For the Product Name, enter the na me you will use (or have used) for your Xojo app. #. For the Organization identifier, enter the first part of the Bundle Identifier (i.e. com.example). Click the Next button. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/choose_options.png 5. Select a folder to save the project. Make sure the Source Control checkbox is not checked since you're not going to use this project for anything. Click the Create button to create the project. #. In the Signing & Capabilities section that appears, verify that the Bundle Identifier exactly matches what you will use (or have used) for your Xojo app. The next steps will only create a provisioning profile for a single app, so this Bundle Identifier is important. And you'll need to follow steps 5 and later for each new app you want to deploy. #. In the Team drop-down, make sure your Apple ID is selected. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/signing_and_capabilities.png 8. To the right of the project name near the top center of the window, make sure that the iOS device you have plugged in is selected. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/device_name.png 9. If it is not, click to select your device from the list. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/device_list.png If your device does not appear in this list, you'll need to first :doc:`configure the device for development`. 10. Now click the Fix Issue button that is in the General area. This will send the profile over to the device. You are now done with this Xcode project, so you can close it. Keep Xcode running as you'll want to use its Devices window to transfer the app you build with Xojo to the device. .. _/topics/application_structure/ios/testing_during_development/configuring_your_xojo_project: Configuring your Xojo project ----------------------------- With Xcode configured, you can now configure you Xojo project to use the Bundle Identifier and Team. 1. Start Xojo and open your iOS project (or create a new one). #. In the iOS Build Settings, enter the Bundle Identifier that you used in the Xcode project. It must match exactly. #. In the Team drop-down, select the Apple ID you used with the Xcode project. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/xojo_ios_build_settings.png 4. Make sure Development is chosen in the Build For popupmenu. .. _/topics/application_structure/ios/testing_during_development/building_your_xojo_ios_project: Building your Xojo iOS project ------------------------------ Now that your Mac and iOS device are configured with the provisioning profile, you can now build your iOS app using Xojo. If you had Xojo open during this process, you should close and re-launch it so it can correctly identify the newly created Provisioning Profile. Click the Build button in Xojo to build the app. This code-signs your apps and compiles it for ARM using LLVM. This may take a little longer than running in the Simulator. When your app is built you will get the app bundle (this is what gets installed on the device) and a .dSYM file (the debugging symbols). It is important that you retain both of these files for builds you deploy. The dSYM file is used for analyzing crash reports that you may receive from your users and without it, it will be difficult to determine the cause of the crash. For more information, refer to the Apple document `Understanding and Analyzing iOS Application Crash Reports `_. If you submit device crash logs in a bug report, you will also need to include the dSYM file in order for us to interpret them. .. _/topics/application_structure/ios/testing_during_development/deploy_to_device: Deploy to your device for testing --------------------------------- There are three ways to transfer the built app to the device you wish to use to test during development. You can debug on device from the Xojo IDE, use XCode or Apple's Configurator app. On-Device debugging ******************* In addition to using the iOS Simulator, Xojo allows you to run your iOS app from the IDE directly to your device. You can learn how to do that :doc:`here`. Using XCode to transfer your app to your device *********************************************** 1. Ensure your iOS device is plugged into the Mac via USB. #. If the Devices window is not displayed, select it from the Window menu. #. Click on the iOS device in the sidebar. #. Click the "+" button (In the Installed Apps section) and select the app you previously built (or drag the app into the Installed Apps section). This immediately installs it on the iOS device. #. On the device, tap on the app to launch it. Using Apple's Configurator app to transfer your app to your device ****************************************************************** `Apple Configurator `_ is a free download from the Mac App Store. This tool is primarily used for enterprise deployment of apps, so it can do much more than just deploy built apps to devices. Some people might prefer using it instead of the Xcode Devices window (you'll still have to set up your device using Xcode as described above, however): 1. Ensure your iOS device is plugged into the Mac via USB. #. Run Apple Configurator. #. On the "All Devices" view, double-click your iOS device. #. Click on Apps in the Navigator on the left. This displays all the apps on the device. #. Click the Add button in the toolbar, select "Apps" from the menu and in the dialog, click "Choose from my Mac...". #. Select the app you previously built. Click OK to copy the app to the device. #. On the device, tap on the app to launch it. Getting crash logs ------------------ You can retrieve crash logs from your iOS device using the Devices and Simulators window in Xcode. Plug your device in via USB and select it in the list of devices. Then click the "View Device Logs" button. You'll see a list of crash logs (it may take a few minutes to download if you have a lot of them). Find your app in the list and click it to see the log. When submitting Feedback cases with crash logs, wait to ensure that the symbols for your log are loaded before you send the log. `iOS Console `_ is also useful to reading iOS console logs. .. _/topics/application_structure/ios/testing_during_development/troubleshooting: Troubleshooting --------------- * If you are unable to Run/Build from Xojo, check your Xcode Command Lines Tools setting. Start Xcode, open Settings and click the Locations tab. Check the Command Lines Tools setting to make sure it has a selection that matches the version of Xcode you have installed. * If after following the steps on this page you still can't deploy to your device, it's likely due to certificate expiration or profile installation problems and is something even users of Xcode have to deal with. Try removing all profiles are located at ~/Library/Mobile Device/Provisioning Profiles/ and then remove all expired certificates using Keychain Access app. ==================================== Meeting Apple's privacy requirements ==================================== Apple makes protecting a user's privacy a priority. As a result, they require developers to be very clear about why they need access to APIs and data that they deem private. When you use `these specific APIs `_, Apple requires an explanation of your app's need to use them. While you do not have to worry about any APIs in the Xojo Mobile framework, you do have to provide Apple with information about the privacy APIs that are being called in any 3rd party libraries you are using. This is done via the Privacy Manifest Editor. If you are using a third party library, the author of that library should provide you with the privacy manifest information you need. Using the Privacy Manifest Editor --------------------------------- The Privacy Manifest Editor creates a file that will be included in your built iOS app that provides Apple with the information they are looking for. To access the Privacy Manifest Editor: #. In your iOS project, click on iOS in the Build Settings section of the Navigator. #. In the Inspector, click on the Advanced tab. #. In the Privacy section of the Inspector, click the Edit button. This reveals the Privacy Manifest Editor. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/ios_privacy_manifest_editor.png :scale: 50% :align: center The information you provide is essentially a key/value pair. It consists of a type and a reason. A single card for a specific type/reason combination is created by default. 4. Choose a Type from the Type combobox or enter a Type if the one you need to provide isn't on the menu. 5. Click the + button to add a reason. This will add a new Reason row with a combobox you can use to select a reason. As with the Type, if the reason you need to specify is one Apple added quite recently and thus is not in the menu, you can enter it manually. 6. If you need to add additional Type/Reason combinations, click the Add Type button. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/ios_privacy_manifest_editor_example.png :scale: 50% :align: center Using the Privacy Manifest Editor settings in other projects ------------------------------------------------------------ If you have other iOS projects that require the same privacy information, you can share this information: 1. Click the Save button in the Privacy Manifest Editor. This will display a Save As dialog box allowing you to choose a location to save the manifest file. 2. Open your other iOS project, open the Privacy Manifest Editor and then click the Open button and select the manifest file you saved. ================================================= Preparing your iOS device for development testing ================================================= While the easiest way to test your iOS project is in the iOS Simulator, you can of course also test it by :doc:`debugging straight to your device`. There are times, however, when this is inconvenient. You may need to test your app over the course of hours or days and wish to do so without being tied to your Mac. This is where the ability to deploy a version of your app while still in initial development is useful. To do so, your iOS device needs to be prepared to accept and run non-release builds of your iOS apps. 1. Plug your iOS device into the Mac you use for development. 2. Launch XCode. 3. Choose Window > Devices and Simulators. 4. On your device, go to the home screen and wait for it to ask you if you wish to trust your computer. Tap **Trust**. 5. Enter your device passcode. 6. On your device, go to Settings > Privacy & Security. 7. Scroll to and then tap on **Developer Mode**. 8. Switch on Developer Mode then tap **Restart** to restart your device. 9. After your device restarts, enter your device passcode. 10. Your device will ask you to confirm that you wish to turn on Developer Mode. Tap **Turn On**. 11. Once again enter your device passcode. 12. Wait a moment for XCode to notice your device again and then prepare it for deployment. 13. When the preparing message in the Devices & Simulators disappears, your device is ready for deployment. You can now use XCode or Apple's `Configurator `_ to deploy test builds to your device. .. seealso:: :doc:`On-device testing during development` and :doc:`Debugging straight to your iOS device` topics ==================== Property List Editor ==================== .. rst-class:: forsearch plist Xojos Property List Editor is available for both Desktop (macOS) and iOS projects under Build Settings > macOS and Build Settings > iOS. This editor simplifies the process of adding custom entries that your app may require, beyond those automatically included by Xojo. Some projects require additional entries in the generated *Info.plist* file. Previously, the only way to include these entries was to create the file manually using an external text editor, then drag and drop it into the project's Navigation area. This allowed its contents to be merged with the entries automatically generated by Xojo in the final *Info.plist* file within the app bundle. Now, the Property List Editor in the Xojo IDE provides a simpler way to add these entries. Once added, you can even export the contents to an external file, making it easy to reload them later for other projects that require the same set of entries. This saves time by eliminating the need to manually re-enter them. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/Plist_Editor_1.png What about projects that already reference an external *Info.plist* file? No worries—Xojo will automatically merge its contents with the entries added via the Property List Editor. If the same key exists in both the external file and the Property List Editor, the value from the Property List Editor will take precedence, overriding the one in the external file. As for the types of data that can be added to the Property List Editor, the expected options are offered: **For collections:** - Dictionary - Array **For primitive values:** - Number - String For primitive value entries, the Editor allows you to convert them to any of the other two supported primitive types. For example, if you add a Number entry, you can later select it and convert it to a String or Boolean type as needed. .. image:: https://documentation.xojo.com/topics/application_deployment/ios/images/Plist_Editor_2.png Of course, the entries added through the Property List Editor are applied and saved to the project file in addition to any changes made using the Property List Editor. The next time you open the project in Xojo, you'll find the previously applied Info.plist entries already in place. .. tip:: If you're using the new Publish feature to send your macOS apps to App Store Connect, you can simplify Apple's encryption compliance process by adding a new Boolean entry in the Property List Editor with the following values: - **Key:** ITSAppUsesNonExemptEncryption - **Value:** False By doing this, you won't need to manually go through the “Manage” option for Apple's encryption compliance on the App Store Connect website—provided your app does not actually use encryption that requires disclosure. Summary ------- Whether you're developing macOS or iOS projects, the integration of the Property List Editor in the Xojo IDE streamlines the process of managing additional *Info.plist* entries. You no longer need to manually create and import external files —now, you can add, edit, and reuse entries directly within the IDE. This not only saves time but also ensures consistency across multiple projects! .. seealso:: :doc:`Publishing iOS Apps From the Xojo IDE` topic ======================== Submitting to TestFlight ======================== .. rst-class:: forsearch Signing When your app is ready for testing, you can submit it to TestFlight to make it available to your beta testers. These steps should help get you started, but most of the specifics are defined by Apple, so you may want to also review the official `Apple Test Flight Guide `_. To build for the TestFlight, you need to be a member of the Apple Developer Program and have the correct certificates, identifiers and a distribution provisioning profile. You will be using Xcode and the iOS Development Portal to set this up. Do not submit apps to TestFlight until you have tested on an actual device by following the steps in the :doc:`On-Device testing during development` topic. The steps for submitting to TestFlight are primarily the same as submitting to the App Store itself. Follow the steps for configuring certificates, identifiers and profiles in the :doc:`Submitting to the iOS App Store` topic to configure your system and build your app. The only major difference is that you will add an App entry in the TestFlight section of App Store Connect for your app. .. _/topics/application_deployment/ios/submitting_to_testflight/add_app_to_app_store_connect: Add app to App Store Connect ---------------------------- If you have not already created the app details in App Store Connect, you'll need to go do that now. 1. Visit http://appstoreconnect.apple.com and log in using your Apple ID. #. Click "My Apps" and click the "+" button to add a new iOS app. #. Here you will need to fill in the App Information for your app. Now you are ready to build your Xojo app for the App Store so you can upload it. .. _/topics/application_structure/ios/submitting_to_testflight/build_xojo_app: Build Xojo app -------------- You build your Xojo app for TestFlight the same way you build it for the App Store: 1. In Xojo, set "Build for App Store" property to ON in the iOS Build Settings. #. Select the appropriate Code Signing team and optionally specify any entitlements (on the Advanced Inspector tab). #. Verify that the bundle ID matches your App ID. For example: com.mycompany.myapp. #. In Shared Build Settings, ensure that all version fields are correct and that "Short Version" has a value. If you have to submit an update to the App Store, you will need to increase the version information. #. Click Build. This creates an IPA (iOS App Archive) file. .. _/topics/application_structure/ios/submitting_to_testflight/upload_app: Upload app ---------- You can now upload the built app to App Store Connect. 1. Launch Application Loader (from Xcode, in the Xcode menu, select Open Developer Tools > Application Loader), select "Deliver Your App" and click Choose. #. Follow the steps to select the IPA file from your Xojo build and send it to App Store Connect for verification. #. If there were errors, you'll need to correct them, rebuild and resubmit. .. _/topics/application_structure/ios/submitting_to_testflight/submit_to_testflight: Submit to TestFlight -------------------- Once your app is verified and uploaded, your final step is to go back to App Store Connect and add the build to TestFlight: 1. Click "My Apps" and select your app from the list. #. Click the TestFlight tab and choose either Internal Testing (App Store Connect team members) or External Testing (anyone can test, but Apple has to do a review of your app first). #. Select the build that was just uploaded and Save. #. Add the people you want to the list of testers. #. Click "Save" or "Start Testing". Your testers need to install the `TestFlight app from the App Store `_ so that they receive notifications that your app is available to test and so they can install it. =============================== Submitting to the iOS App Store =============================== .. rst-class:: forsearch Signing When you have finished developing and testing your app, you can submit it to the App Store to make it available to everyone. These steps should help get you started, but most of the specifics are defined by Apple, so you may want to also review the official `Apple App Distribution Guide `_. Note that Apple constantly changes these steps so although some of the specifics might be a little different, the overall process should be similar. .. important:: To submit to the iOS App Store you must be using the current version of Xojo. This section describes the steps to deploy your iOS App to the App Store. You can also watch the webinar for guidance: `Deploying iOS Apps `_. To build for the App Store, you need to be a member of the `Apple Developer Program `_ and have the correct certificates, identifiers and a distribution provisioning profile. You will be using Xcode and the iOS Development Portal to set this up. Do not submit apps to the App Store until you have tested on an actual device by following the steps in the :doc:`On-Device testing during development` topic. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/ios_distribution_certificate: iOS Distribution Certificate ---------------------------- 1. In Xcode Preferences, select the Accounts panel, select the account for your Apple ID #. Click the "Manage Certificates" button. #. Click the "+" in the left corner and select "iOS Distribution" to create the necessary certificates. #. Click "Done" to close the window. Next, you can create an App ID at the iOS Dev Center. If you are part of a team, it is possible that the Team Administrator has already created the iOS Distribution certificate. In this case, you should not recreate them as that will revoke the previously created ones. Instead, ask your Team Administrator to export the iOS Distribution certificate and send it to you so you can install it in your Keychain. Without this certificate any apps you submit to the App Store will be rejected by Application Loader. Refer to `Apple's App Distribution Guide `_ for information about exporting certificates. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/identifiers: Identifiers ----------- This step is optional, since you can use the same App ID you have already been using for device deployments. 1. Log in to the Apple Developer Portal at https://developer.apple.com/account. #. Select "Certificates, Identifiers & Profiles". #. Select "iOS, tvOS, watchOS" In the popup menu on the left. In the Identifiers section, click App ID and then the "+" to register a new Application ID. Create an app-specific App ID of the form: "com.mycompany.myapp" which can only be used with a single app. Regardless, the App ID must match what you have specified as the Bundle Identifier in the iOS Build Settings for your project. #. Click Continue and then Submit. Now you can create your Distribution Provisioning Profile. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/generate_distribution_provisioning_profile: Generate distribution provisioning profile ------------------------------------------ In the Provisioning Profile section, click Distribution. 1. Click "+" to add a provisioning profile. #. Select "App Store" in the Distribution section and then Continue. #. Select the App ID. If you created one above, then use the one you created. Otherwise use the one you've been using for device deployment. Click Continue. #. Select the Certificate and then Continue. #. Enter a name for the profile and then Continue. #. Click Done. You do not need to download the profile file. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/install_the_provisioning_profile_to_xcode: Install the provisioning profile to Xcode ***************************************** 1. In Xcode, go back to the Preferences, select Accounts and highlight your account. #. Click "Download Manual Profiles". Now you are ready to add the app details to App Store Connect. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/add_app_to_app_store_connect: Add app to App Store Connect ---------------------------- If you have not already created the app details in App Store Connect, you'll need to go do that now. 1. Visit http://appstoreconnect.apple.com and log in using your Apple ID. #. Click "My Apps" and click the "+" button to add a new iOS app. #. Here you will need to fill in all the information on the various pages, such as app name, description, screenshots, icon (1024x1024), rating, price, etc. You can save and go back to make updates. #. When everything is correct, the app status should show as "Prepare for Submission". Now you are ready to build your Xojo app for the App Store so you can upload it. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/build_xojo_app: Build Xojo app -------------- 1. In Xojo, set "Build for App Store" property to ON in the iOS Build Settings. #. Select the appropriate Code Signing team and optionally specify any entitlements (on the Advanced Inspector tab). #. Verify that the bundle ID matches your App ID for example com.mycompany.myapp. #. In Shared Build Settings, ensure that all version fields are correct and that "Version" field has a value. If you are submitting an update because Apple rejected a previous submission, you must increment the Non-Release Version number. Enabling Auto-Increment Version in the Inspector will handle this for you automatically. #. Click Build. This creates an IPA (iOS App Archive) file which is what you will upload. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/if_you_use_build_steps: If you use build steps ********************** On iOS, there is an additional step added by default that is called "Sign". Any Copy File Build Steps that you add should be before the "Sign" step so that the files you have copied into the app bundle are properly signed for submission to the App Store. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/build_an_app/troubleshooting: Troubleshooting *************** If you have switched to Xcode 9 you may find you get build errors at the linking stage with text indicating an error with xcodebuild or provisioning profiles. If this occurs for you, try revoking your certificate and recreating both it and any provisioning profiles as described above. Your best option is to use a newer version of Xcode. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/upload_app: Upload app ---------- You can now upload the built app to App Store Connect. 1. Install `Apple Transporter `_, which is used to upload the built app to App Store Connect. #. Select the "+" or click the "ADD APP" button. #. Follow the steps to select the IPA file from your Xojo build and send it to App Store Connect for verification. #. If there were errors, you'll need to correct them, rebuild and resubmit. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/submit_for_review: Submit for review ----------------- Once your app is verified and uploaded, your final step is to go back to iTunes Connect: 1. Click "My Apps" and select your app from the list. #. Select the build that was just uploaded and Save. #. Update any other fields that Apple requires. #. Click "Submit for Review". Be patient. It can take as long as several days before an app is approved or you hear back from the reviewer. If your app reviewers finds issues, you'll need to fix them, rebuild, upload and resubmit. Visit http://appreviewtimes.com to track estimates of recent App Store review times. If you need to resubmit a build to App Store Connect, you will need to increase the Bug Version number in your Xojo project's Shared Build Settings. .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/submit_for_review/troubleshooting: Troubleshooting --------------- Below are some solutions for errors that you may get when uploading using Application Loader. .. csv-table:: :header: "Error", "Message", "Suggestion" :widths: 10, 33, 33 "ITMS-90475","Invalid Bundle. iPad Multitasking support requires launch story board in bundle 'com.acme.myapp'.","Remove Launch Images project item. Use a Launch Screen instead." "ITMS-90023","Missing required icon file. The bundle does not contain an app icon for iPad of exactly '167x167' pixels, in .png format for iOS versions supporting iPad Pro.","Delete App Icon project item and then add it back (using Insert -> App Icon). Now you can add all the app icons, including the iPad Pro 167x167 icon." "","Distribution profile is required to submit to the app store.","Verify that you have installed the iOS Distribution public certificate on your computer. Usually Xcode does this for you, but you may also want to try manually downloading it from the Apple Developer Portal (Certificates->Production) and double-clicking it to ensure it gets installed into the Keychain." "ITMS-90035","Invalid Signature. A sealed resource is missing or invalid. Make sure you have signed your application with a distribution certificate, not an ad hoc certificate or a development certificate.","Ensure you have correctly installed the iOS App Distribution certificate as shown above." "ITMS-90164","Invalid Code Signing Entitlements. The entitlements in your app bundle signature do not match the ones that are contained in the provisioning profile.","Ensure you have correctly installed the iOS App Distribution certificate as shown above." "ITMS-90179","Invalid Code Signing. The executable 'Payload/MyApp.app/MyApp' must be signed with the certificate that is contained in the provisioning profile.","Ensure you have correctly installed the iOS App Distribution certificate as shown above." If you are still having troubles, you may want to remove all provisioning profiles from your computer, recreated them at the iOS Dev Center and re-download everything again. The provisioning profiles on your computer are located here: * ~Library/MobileDevice/Provisioning Profiles .. _/topics/application_structure/ios/submitting_to_the_ios_app_store/see_also: .. seealso:: :doc:`On-Device testing during development`, :doc:`Submitting to TestFlight` topics Application structure ===================== .. toctree:: :maxdepth: 1 :name: sec-application_structure Android App icons Coding guidelines for 64-Bit apps Console apps Desktop iOS The App Object Web Android ======= .. toctree:: :maxdepth: 1 :name: sec-android Android Apps Adding permissions Copy files to an Android device ============ Android apps ============ Android apps run on Android devices including phones and tablets. .. _/topics/application_structure/android/android_apps/android_project_overview: Android project overview ------------------------ .. image:: https://documentation.xojo.com/topics/application_structure/android/images/android_apps_android_project_items.png When you create your first Android project, the following project items are added automatically: * :doc:`App`: The App object works similarly to how it works for desktop and web projects. In the App Inspector, you can set the Default Screen to use for phones and tablet devices. * Default Phone Screen: Specifies the initial screen displayed when the app is started on phone-sized devices. * Default Tablet Screen: Specifies the initial screen displayed when the app is started on tablet-sized devices. * Screen1: This the main layout where controls are placed. It is roughly equivalent to a Window or WebPage in desktop and web projects. * App Icon: This item is where the various size app icons for the app are placed. To prevent an app from working on a phone, select "None" for the Default Phone Screen App property. If you do not want the app to run natively on a tablet, select "None" for the Default Tablet Screen App property. If Default Tablet Screen is "None", then the "Not supported on tablets" message is displayed. To have the app run on both phone and tablets, it should have a both default screens specified, although they can refer to the same screen. .. _/topics/application_structure/android/android_apps/app_object: App Object ---------- The App object works similarly to how it works for desktop and web projects. For an Android project, the App object is a subclass of :doc:`MobileApplication`. You use it to specify the default screen layouts for the devices. You can add events, properties and methods to the App object. Use the App prefix to refer to public properties and methods elsewhere in your code. For example, to refer to the property UserName added to the App object in a class of the project, you would write: .. code:: xojo App.UserName = "Billy" .. _/topics/application_structure/android/android_apps/event_handlers: Event Handlers ************** The Application class has several event handlers. Opening and Closing are supported as they are in desktop and web projects. Mobile applications also have some :ref:`unique event handlers`. .. _/topics/application_structure/android/android_apps/properties: Properties ********** * Default Phone Screen: The Screen to display when a phone-sized device is used. Set this to "None" to prevent the app from working on a phone. * Default Tablet Screen: The Screen to display when an tablet-sized device is used. Set this to "None" when you do not want a native tablet app. The app will still run on a tablet, but will do so by running the phone screen in "scaled" mode. .. _/topics/application_structure/android/android_apps/screens: Screens ------- In the Inspector for App, you can specify the default screens to use when the app is run on a phone or a tablet. A Screen is a project item that specifies the type of layout and supported orientations. Only one screen is added by default with new projects: Screen1. When you click on a Screen you are shown a preview of how it might look on the device. .. _/topics/application_structure/android/android_apps/build_settings: Build Settings -------------- The Build Settings section of the Navigator contains the build-specific settings for your app. .. _/topics/application_structure/android/android_apps/shared: Shared ****** The Inspector for Shared settings contains these properties: * Major Version (MajorVersion): The Major version for your app. Version numbers are usually written as 1.2.3.4, where “1” is the major version. * Minor Version (MinorVersion): The Minor version for your app. Version numbers are usually written as 1.2.3.4, where “2” is the minor version. * Bug Version (BugVersion): The Bug version for your app. Version numbers are usually written as 1.2.3.4, where “3” is the bug version. * Non Release Version (NonReleaseVersion): The Non Release (build) version for your app. Version numbers are usually written as 1.2.3.4, where “4” is the non release version. * Auto Increment Version Info: When ON, the Non Release Version is increased by one each time you do a Build (but not when you Run). * Optimization Level: There are these choices: * Default: Optimized for fast build times. * Moderate: A balance between build times and math-related code execution time. * Aggressive: Longer build times, but code is more optimized for faster math-related code execution time. * Destination: Specifies the path where the app is located when you run it in Debug mode. If not specified, then the app is placed alongside your project file (or in a folder alongside the project file on Windows and Linux). .. _/topics/application_structure/android/android_apps/android-specific: Android-specific **************** .. image:: https://documentation.xojo.com/topics/application_structure/android/images/android_apps_android_build_settings_inspector.png This section of the Build Settings contains Android-specific settings, including: * Android App Name: The actual name for your Android app. This is the name that appears on the home screen in Android and is the name of the actual app file. * Bundle Identifier: The bundle identifier is used by Android as a unique descriptor for your app. It is usually specified as a reverse domain name, such as com.xojo.myapp. A bundle identifier is required for Android apps. * Supports Dark Mode: Indicates whether Android should switch your app to Dark Mode when the OS switches to Dark Mode. If you are doing any drawing and have made sure your app will respond properly when in Dark Mode, turn this option on. * Build For Play Store: This allows you to indicate how the app will be deployed so that Xojo can output the appropriate format for the Play Store. * Key Store Properties: .. tip:: Choosing Build For Play Store, will cause Xojo to create a signed bundle file that can be submitted to the Play Store. Advanced build settings ^^^^^^^^^^^^^^^^^^^^^^^ There are a several advanced settings available on the Advanced tab. .. image:: https://documentation.xojo.com/topics/application_structure/android/images/android_buildsettings_advancedtab.png .. _/topics/application_structure/android/android_apps/sdkversions: SDK Versions ^^^^^^^^^^^^ When using declares to call into the OS, set the Minimum and Maximum Android SDK versions via the Advanced tab. .. _/topics/application_structure/android/android_apps/dependencies: Dependencies ^^^^^^^^^^^^ By clicking on the Advanced (Gear) tab in the Inspector you can set the Dependencies for your app. This is only needed if you are linking in libraries and/or using declares. .. note:: Each dependency must be on a separate line. .. _/topics/application_structure/android/android_apps/permissions: Permissions ^^^^^^^^^^^ By clicking on the Advanced (Gear) tab in the Inspector you can set the Permissions for your app. This is only needed if you are linking in libraries and/or using declares. .. note:: Each permission must be on a separate line. .. _/topics/application_structure/android/android_apps/manifestinformation: Manifest Information ^^^^^^^^^^^^^^^^^^^^ Should your app need any generic global manifest settings, these can be set via the Manifest Globals property on the Advanced tab in the Inspector. Metadata for need for external libraries can be added via the Manifest Metadata property on the Advanced tab in the Inspector. .. _/topics/application_structure/android/android_apps/android_app_deployment: Android app deployment ---------------------- There are two ways to deploy Android apps: * :doc:`Google Play Store` * :doc:`Via a URL` .. _/topics/application_structure/android/android_apps/videos: Videos about app deployment --------------------------- * `Signing your app `_ * `Publishing your app to the Play Store `_ .. _/topics/application_structure/android/android_apps/see_also: .. seealso:: :doc:`MobileApplication` class ================== Adding permissions ================== Some Android declares or libraries may require that your app have specific permissions in order to call them. You can add these permissions to your app's manifest file via the Permissions property. To do so: 1. In the Navigator, click on Android under Build Settings. 2. In the Inspector, click on the Advanced tab. 3. Click to edit the Permissions property. 4. Add your permissions then click OK. Permissions are added to the AndroidManifest.xml file when the app is run/built. Format ------ Permissions should be added in XML format. For example: .. code:: XML =============================== Copy files to an Android device =============================== There are two ways to copy files to your Android device. You can use a Copy Files Build Step or you can use Google Drive (or most other file sharing services). .. _/copy_files_to_an_android_device/using_copy_files_build_step: Using Copy Files build step --------------------------- You can use Build Automation to copy files into the Resources folder of your Android app. When the app is running on the device, you can access the files directly or copy them out of the Resources folder to another location (this is required to write to the files as the Resources folder is read-only). .. note:: The Resources folder is read-only! If you need to modify files copied in the manner, you first have to copy them out of Resources and into another location, such as Documents. Here are the steps to set up a Copy Files Build Step: #. A Copy Files Build Step is added to your project by selecting Insert > Build Steps > Copy Files. In the center area you can drag the files (or folders) you want to copy to the device. #. In the Inspector, change the Destination to "Resources Folder". #. You can also give this Copy Files step a name. It is helpful to give it a name that describes what it is copying. #. Drag the Copy Files Step from its original location onto the the Android item in the Build Settings section of the Navigator. Be sure that it is after the Build item, but before the Sign item. Any Copy File Build Steps that you add should be before the "Sign" step so that the files you have copied into the app bundle are properly signed for submission to the Google Play Store or deployment to a device. When you Run or Build your project, these files are now copied to the Resources folder. Your app can access the files using :doc:`SpecialFolder.Resources`, which returns a FolderItem that points to the specified file in Resources. For example, if you've copied a file called MyFile.html to Resources, you can access it like this: .. code:: xojo Var myFile As FolderItem = SpecialFolder.Resource("MyFile.html") Now that you have a FolderItem, you can use the file directly or you can copy it to Documents so that it can be modified by using the CopyTo method of FolderItem: .. code:: xojo Var myFile As FolderItem = SpecialFolder.Resource("MyFile.html") If myFile.Exists Then Var destFile As FolderItem = SpecialFolder.Documents.Child(myFile.Name) myFile.CopyTo(destFile) End If You can also copy files to a folder within Resources by specifying a value for the Subdirectory property in the Inspector for the Build Step. You still use the :ref:`Resource` function to get the folder, which you can then iterate through to access the files: .. code:: xojo Var myFolder As FolderItem = SpecialFolder.Resource("MyFolder") If myFolder.Exists Then For Each c As FolderItem In myFolder.Children ' use c as necessary to access or copy the file Next End If .. note:: The Framework folder is also supported. Use this folder when you need to include a framework file to which you plan to access via :doc:`Delcares`. .. _/copy_files_to_an_android_device/file_sharing: File sharing ------------ Copying files to and from an Android device is done most easily using the Google Drive app for Android. This will connect you to the Google Drive on your Google account. From your computer, you can then either use a browser to connect to your Google drive or the Google Drive app. ========= App icons ========= For iOS App icons, refer to App Icons. The Icon Editor is used to add application icons to desktop and web apps. Desktop apps display the icon in the file viewer for the OS (Finder, Explorer, etc.) and in other places (such as the Dock on Mac or the Task Bar on Windows). Web applications display the application icon on the app's loading screen. To use the Icon Editor, select the App object in the Navigator and then click the icon next to the Icon property in the Inspector. The Icon Editor displays as a window with blank areas for icons of various sizes. You can drag icons files to these areas to set the icon for that size. You can also copy and paste icons between the sizes; they are scaled automatically for you. If you copy and paste an icon, be sure to also copy and paste its mask. To remove a specific icon size, click on its area and press the delete key. You can also use the Delete, Paste, Import contextual menu when you right-click on any of the individual icon image areas to add or remove icons. Common image formats are supported, such as JPG, BMP, PNG, etc. You can also drag an ICNS or ICO file onto the window background (instead of an image area) to load and populate all the image sizes at one time. If you add an icon that has both the image and the mask (such as with most PNG files) then both the image and mask are added. Use the Images/Masks/Preview selector to display the various components of the icon images. For best results, be sure to add icons (and masks) for every available size. Different platforms use different sizes. If the masks are missing, the icons may not appear on some platforms. On HiDPI screens, the next higher sized icon size is used. So if a 512 point app icon is needed on a HiDPI screen, then the 1024 pixel icon will be used. .. warning:: All images must be 72 DPI to work properly. .. image:: https://documentation.xojo.com/topics/application_structure/images/app_icons_icon_editor.png ================================= Coding guidelines for 64-bit apps ================================= You can create 64-bit desktop, web and console apps for Mac, Windows and Linux. In most cases, you'll be able to build a 64-bit version of your existing projects without having to make any changes. As of 2017 Release 3, the Xojo IDE itself is also 64-bit. With 64-bit, your apps gain access to much more memory than before (32-bit apps had a 3-4GB limit) and math-based code can see significant speed increases due to the new optimizing compiler. In addition, 64-bit operating systems can run 64-bit apps more efficiently than 32-bit apps since they do not have to load an extra compatibility layer. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/creating_a_64-bit_app: Creating a 64-bit app --------------------- .. image:: https://documentation.xojo.com/topics/application_structure/images/coding_guidelines_for_64-bit_apps_build_architecture.png The Inspector for each build target (Windows, macOS and Linux) has a property called Architecture where you can select the type of architecture you want to build. To create a 64-bit app for your existing projects, change the Architecture setting to a 64-bit target and then press the Build button. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/coding_notes: Coding notes ------------ .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/compiler_constants: Compiler constants ****************** In a 32-bit app, :doc:`Target32Bit` returns True and Target64Bit returns False. In a 64-bit app, :doc:`Target64Bit` returns True and Target32Bit returns False. Use these two compiler constants to determine the architecture of the app. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/integers: Integers ******** in a 64-bit app, the :doc:`Integer` data type is an alias for :doc:`Int64` and :doc:`UInteger` is an alias for :doc:`UInt64`. In a 32-bit app, the Integer data type is an alias for :doc:`Int32` and UInteger is an alias for :doc:`UInt32`. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/declares: Declares ******** In general, you'll want to review any Declares that are used in your project. In particular, if a :doc:`Declare` is using a specific Integer type, such as Int32, it may be possible that the library requires you to use Int64 in a 64-bit app. To simplify this, use the Integer data type which is an alias to Int32 in 32-bit apps and Int64 in 64-bit apps. On Mac, some macOS declares require a floating-point number. The 32-bit libraries use :doc:`Single`, but the 64-bit libraries use :doc:`Double`. For these situations you should use the :doc:`CGFloat` data type. A 64-bit app can only use 64-bit libraries. Declaring to a 32-bit library from a 64-bit app will fail. Similarly, a 32-bit app cannot use 64-bit libraries. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/building_notes: Building notes -------------- The type of app you need to build for your users varies based on the operating systems you need to support. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/macos: macOS ***** All versions of macOS supported by Xojo are already 64-bit. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/windows: Windows ******* Windows is available in both 32-bit and 64-bit versions. The 32-bit version of Windows cannot run 64-bit apps, but the 64-bit version of Windows can run both 32-bit and 64-bit apps. This means you will likely need to create both 32-bit and 64-bit versions of your Windows apps so that your users can choose the appropriate one for their version of Windows. Keep in mind that the TargetWin32 compiler constant returns True on Windows, regardless of whether the app is 32-bit or 64-bit. This constant is indicating that the Win32 API is being used and is not an indicator of 32-bit or 64-bit systems. To check if the app is 64-bit, use the Target64bit compiler constant. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/build_files: Build files ^^^^^^^^^^^ For 64-bit builds, the compiler places some framework DLLs next to the executable and not into the "MyApplication Libs" (or just "Libs") folder like it did for 32-bit builds. A little history: For 32-bit Windows builds, the compiler doesn't actually spit out a native PE32 file. What it does is writes out a stub executable and then tacks all of the program data onto the end of it. The stub is responsible for loading that data into memory and performing all of the relocations and import binding that the OS loader would normally do. The stub doesn't have to link directly against any of the libraries referenced by the program (including the framework), so Xojo has the freedom to put the DLLs required by the program anywhere. However, this approach occasionally gets apps flagged as malware by antivirus programs and prevents some features Windows users have wanted for ages (like the ability to edit the app manifest). For 64-bit Windows executables, the compiler is using a native linker to generate actual PE32+ files. These executables link directly against whatever DLLs it needs and are therefore bound to the operating system's search path. In order for the loader to find the DLLs the executable need to launch, they have to be in the same folder as the executable itself. Currently, Visual Studio runtime DLLs appear next to the executable along with the Xojo framework DLL (XojoGUIFramework64.dll). If you use WebKit with HTMLViewer then CEF3 related files and DLLs also appear alongside the executable. Attempting to the make the Windows build structure look more like macOS bundles is unlikely to happen, primarily due to the DLL issue but also because in Windows, the directory is the application bundle. Microsoft expects programs to use installers and doesn't really care how 'messy' your program's folder is. That said, the change in directory structure does not impose a requirement that installers are used or that it has to be on the system disk. The only change is that there will be more files in the folder with the executable. .. image:: https://documentation.xojo.com/topics/application_structure/images/coding_guidelines_for_64-bit_apps_windows_64-bit_advanced_build_settings.png .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/manifest_file: Manifest file ^^^^^^^^^^^^^ For Windows 64-bit apps a standard manifest is used. The manifest includes information from the Windows Build Settings, including version information, OS settings and RunAs settings. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/linux: Linux ***** Linux distributions are also available in both 32-bit and 64-bit versions, although 64-bit versions are far more common. In fact, some Linux distributions are no longer making 32-bit versions available. 32-bit versions of Linux cannot run 64-bit apps. 64-bit versions of Linux can run 32-bit versions of app, but only if the appropriate 32-bit compatibility libraries have been installed. These libraries are not typically installed by default and are not always easy to install. This means you will likely want to create both 32-bit and 64-bit versions of your Linux apps so that your users can choose the appropriate one for their version of Linux. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/plugins: `Plugins` ********* A 64-bit app requires `plugins` that support 64-bit. If your app uses `plugins`, you should check with your plugin vendor to see if they have updated versions that support 64-bit. Also note that since the Xojo IDE itself is 64-bit, any `plugins` you use must have a 64-bit plugin part in order for the IDE to recognize them. This is true even if you are building 32-bit apps. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/ide_script_for_building: IDE script for building *********************** To make it easier to build all the different versions of your app in one step, you can use the following IDE Script: .. code:: xojo ' Build all target platforms Const kOSX32 = 7 Const kOSX64 = 16 Const kWin32 = 3 Const kWin64 = 19 Const kLin32 = 4 Const kLin64 = 17 Const kLinARM = 18 Var path As String path = BuildApp(kOSX32) path = BuildApp(kOSX64) path = BuildApp(kWin32) path = BuildApp(kWin64) path = BuildApp(kLin32) path = BuildApp(kLin64) path = BuildApp(kLinARM) Var result As String result = ShowDialog("Build All", _ "Finished building.", _ "OK", "", "", -1) This script creates Windows, macOS and Linux builds for both 32-bit and 64-bit plus a Raspberry Pi build (ARM 32-bit) all in one step. You can comment out the parts you do not need to build. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/other_64-bit_information: Other 64-bit information ------------------------ * For some projects, 64-bit builds can take longer to finish. Be sure to let Xojo finish building. For best speed set Optimization Level to "Default" in the Shared Build Settings. * To reduce build times, ensure you do not have really long methods that can be refactored and simplified. Project items (classes, Windows, etc.) with large amounts of code can also increase build times. Break them into separate classes to reduce build times. * Arrays can have a maximum index value of 2,147,483,646. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/troubleshooting: Troubleshooting --------------- If you are unable to build your project as 64-bit, please create an :doc:`issue` and attach the project, along with additional information such as the platform you are using and anything relevant from the system log. .. _/topics/application_deployment/coding_guidelines_for_64-bit_apps/see_also: .. seealso:: :doc:`Target64Bit` constant; :doc:`Integer` data type; :doc:`Declare` command; :doc:`Desktop Apps`, :doc:`Web Apps`, :doc:`Console Apps` topics ============ Console apps ============ Unlike iOS, desktop and web apps, console apps are not event-based. Console apps have no graphical user interface and are designed to run from the terminal, command line or as a background process. .. _/topics/application_structure/console_apps/console_project_overview: Console project overview ------------------------ When you create a console project, you get the following project items added automatically: * App: The App object is where your put the code to run. .. _/topics/application_structure/console_apps/app_object: App Object ---------- For a console project, the App object is a subclass of ConsoleApplication, but you can also choose to change the Super to ServiceApplication to create a Windows Service. .. _/topics/application_structure/console_apps/event_handlers: Event Handlers ************** The Application class for a console app has these event handlers: * :ref:`Run`: This event handler is called when your app first starts, either by running the app from Xojo or by running a built app. Your app quits when the Run event ends or you call the Quit method. You can manually quit the app by pressing Control-C in the console/terminal. * :ref:`ConsoleApplication.UnhandledException`: Executes when a runtime error occurs that is not caught by your code. This event gives you a “last chance” to catch runtime errors before they cause your app to terminate. .. _/topics/application_structure/console_apps/background_apps: Background apps --------------- Console apps are often used to create background apps. On macOS and Linux, background apps are called daemons. On Windows, background apps are called services. .. _/topics/application_structure/console_apps/daemon: Daemon ****** To turn your console app into a daemon that can run in the background, call the Daemonize method of the ConsoleApplication class from the Run event handler. .. code:: xojo If Not Daemonize Then Quit End If You can also use the Daemonize method on macOS, but Apple would rather you use launchd to start daemon processes. .. _/topics/application_structure/console_apps/service_application: Service Application ******************* To create a console app that can run as a Windows Service you need to use the ServiceApplication class. After you create your console app, change the Super of the App from ConsoleApplication to ServiceApplication. Alternatively you can choose ServiceApplication from the Templates in the Project Chooser. A service app gives you additional events that help you manage things when the services starts, stops or is paused. Refer to :doc:`ServiceApplication` for more information about these events. .. _/topics/application_structure/console_apps/build_settings: Build Settings -------------- The Build Settings section of the Navigator contains the build-specific settings for your app in general and for specific OS targets. You can check the box next to each target in order to build an app for that target. Console targets are: Windows, macOS and Linux. .. _/topics/application_structure/console_apps/shared: Shared ****** The Inspector for Shared settings contains these properties: * Major Version: The Major version for your app. Version numbers are usually written as 1.2.3.4, where “1” is the major version. * Minor Version: The Minor version for your app. Version numbers are usually written as 1.2.3.4, where “2” is the minor version. * Bug Version: The Bug version for your app. Version numbers are usually written as 1.2.3.4, where “3” is the bug version. * Stage Code: Used to indicate the type of app you are building (Development, Alpha, Beta, Final). * Non Release Version: The Non Release (build) version for your app. Version numbers are usually written as 1.2.3.4, where “4” is the non release version. * Auto Increment Version Info: When ON, the Non Release Version increases by one each time you do a Build. * Short Version: A short text description for your app. Usually this contains just the version number (such as 1.2.3.4) and is displayed by some operating systems in Get Info or Property windows for the application. * Long Version: A longer text description for your app. Usually this contains the application name, copyright, version and other information. This is displayed by some operating systems in Get Info or Property windows for the application. * Package Info: A text description for your app that may be displayed by some operating systems in Get Info or Property windows for the application. * Use Builds Folder: When ON, a separate folder is created alongside the project file. Each platform (macOS, Windows, Linux) also gets its own subfolder within this builds folder. * Include Function Names: When ON, the actual names of your function calls are included in the built app. This is useful for debugging purposes and for getting stack traces. * Language: The language to used to resolve dynamic constants. * Optimization: Used for all 64-bit builds and 32-bit ARM builds (those that use the LLVM compiler). There are these choices: * Default: Optimized for fast build times. * Moderate: A balance between build times and math-related code execution time. * Aggressive: Longer build times, but code is more optimized for faster math-related code execution time. * Command Line Arguments: These are the command-line arguments that are passed to your app when running it from Xojo. * Destination: Specifies the path where the running app is placed. Normally it is placed alongside your project, but you can choose a different location. .. _/topics/application_structure/console_apps/build_settings/macos: macOS ***** The macOS section contains settings used when building the macOS app. * Mac App Name: The actual file name for your macOS app. * File Types: n/a * Architecture: The CPU architecture for the app. Choices are x86 64-bit, ARM 64-bit and Universal. * Bundle Identifier: The bundle identifier is required by macOS and is used as a unique descriptor for your app. It is usually specified as a reverse domain name, such as com.xojo.myapplication. .. _/topics/application_structure/console_apps/build_settings/windows: Windows ******* The Windows section contains build settings for your Windows apps. * Windows App Name: The actual file name for the Windows app. * Company Name: The “Company Name” appears in the Copyright section of the app properties in the Details tab. * Product Name: The name of the product as installed in the Windows Start > All Programs menu. This also appears in “Product name” on the app properties Details tab. * Internal Name: This is useful when your product has a different internal name than its external name. This is not shown on the app properties Details tab. * File Description: [TBD] * Include Windows Runtime DLLs: Copies the Universal Runtime DLLs beside your app executable. For more information, refer to Windows Universal Runtime. * Architecture: The CPU architecture for the app. Choices are x86 32-bit, x86 64-bit and ARM 64-bit. .. _/topics/application_structure/console_apps/build_settings/linux: Linux ***** The Linux section contains build settings for your Linux apps. * Linux App Name: The actual file name for the Linux app. * Architecture: The CPU architecture for the app. Choices are ARM 32-bit, x86 32-bit, x86 64-bit and ARM 64-bit. Linux ARM apps run on the Raspberry Pi and other similar single-board computers. .. _/topics/application_structure/console_apps/this_computer: This Computer ************* The This Computer section shows you the appropriate target settings (macOS, Windows or Linux) for the OS you are currently using. For example, if you are running Xojo on macOS, then This Computer shows the macOS Build Settings. .. _/topics/application_structure/console_apps/deployment: Deployment ---------- To create a stand-alone app for deployment, you create a build by clicking the Build button on the main toolbar or choosing Project > Build from the menu. This creates apps for each of the OS platforms you selected, with separate folders for each (when Use Build Folders is ON). Console apps are deployed by installing them on a computer and running them on the computer. Console apps have two parts: the main executable and its libraries (contained in the Libs folder). Both parts are required in order for the app to run. You can just zip the console app and its Lib folder for distribution. .. csv-table:: :header: "OS", "Architecture", "Build Folder Name" :widths: auto "Linux","x86 32-bit","Linux" "Linux","x86 64-bit","Linux 64 bit" "Linux","ARM 32-bit","Linux ARM" "Linux","ARM 64-bit","Linux ARM64" "macOS","x86 64-bit","macOS 64 bit" "macOS","ARM 64-bit","macOS ARM 64 bit" "macOS","Universal","macOS Universal" "Windows","x86 32-bit","Windows" "Windows","x86 64-bit","Windows 64 bit" "Windows","ARM 64-bit","Windows ARM 64 bit" .. _/topics/application_structure/console_apps/macos: macOS ***** On macOS, you can start a console app from the Terminal. Navigate to the folder containing the app and type its name at the Terminal prompt. You will need to include the prefix "./" in order for the Terminal to actually find the app in the current folder: .. code:: xojo ./MyApp .. _/topics/application_structure/console_apps/windows: Windows ******* On Windows, you can start a console app from the Command tool. Just type its name at the command prompt: .. code:: xojo MyApp .. _/topics/application_structure/console_apps/linux: Linux ***** On Linux, you can start a console app from the Terminal. Navigate to the folder containing the app and type its name at the Terminal prompt. You will need to include the prefix "./" in order for the Terminal to actually find the app in the current folder: .. code:: xojo ./MyApp Desktop ======= .. toctree:: :maxdepth: 1 :name: sec-desktop Desktop apps ============ Desktop apps ============ A desktop app has a graphical user interface (GUI) and are run directly on the user's computer. .. _/topics/application_structure/desktop/desktop_apps/desktop_project_overview: Desktop project overview ------------------------ When you create a desktop project, you get the following project items added automatically: * App: The App object allows you to specify some initial app settings, including the default window, default menu bar and app icons. * Window1: This is the main window layout where you place your controls. * MainMenuBar: This is the main menu bar. Most apps only have a single menu bar. .. _/topics/application_structure/desktop/desktop_apps/app_object: App Object ---------- For a desktop project, the App object is a subclass of :doc:`DesktopApplication`. You use it to specify the default window that opens when your app starts, the default menu bar that is used by the app and the app icon (in various sizes). .. youtube:: PpyhUHLZkWc The DefaultWindow property shows a list of all the Windows in the project. Select the one you want to automatically appear when the app starts. You can also set it to None so you can then show a specific window yourself from one of the App event handlers. .. image:: https://documentation.xojo.com/topics/application_structure/desktop/images/desktop_apps_app_desktop_inspector.png .. _/topics/application_structure/desktop/desktop_apps/event_handlers: Event Handlers ************** The App object has several event handlers: * :ref:`Opening`: This event handler is called when your app first starts, either by running the app directly (in debug mode) or by running a built app. * :ref:`Closing`: The closing event is called when the user quits the app. * :ref:`DocumentCreated`: This event is called when the built app is run without supplying a document. * :ref:`DocumentOpened`: This event is called when one of the app's documents is double-clicked from the desktop causing the app to start. It is also called if you drop a document on the app. * :ref:`MenuBarSelected`: This event is called when the user clicks on a menu in the menu bar but before any menu items are displayed. This event handler executes after the MenuBarSelected event handler of any classes with instances in the frontmost window and after the window's MenuBarSelected event handler. This is the event handler that should be used to enable menu items that should be enabled regardless of whether there is a window open or not (this possibility exists on macOS apps). Note that if a menu item should always be enabled, you should use its :ref:`AutoEnabled` property instead of a MenuBarSelected event handler. * :ref:`AppleEventReceived`: Executes when an :doc:`AppleEvent` is received by the application. * :ref:`Activated`: The app is being activated. This occurs when the app is opening and when it is being brought to the front. * :ref:`Deactivated`: The app is being deactivated. This occurs when another app or a desktop window is being brought to the front or when the app quits. * :ref:`UnhandledException`: Called when a runtime error occurs that is not caught by your code. This event gives you a *last chance* to catch runtime errors before they cause your app to quit. .. _/topics/application_structure/desktop/desktop_apps/properties: Properties ********** * **DefaultWindow**: This is the window that is opened automatically when the app starts. You can also set this to None. * **MenuBar**: This is the primary menu bar for the app. It is used as the default menu bar for newly created windows. This property is accessible by the App function (see next topic). You can also set it to None so you can then show a specific window yourself from one of the App event handlers. * **Icon**: Click the icon image to display the :doc:`App Icon Editor` where you can specify the various sizes of icons used by the OS to display the app. .. _/topics/application_structure/desktop/desktop_apps/build_settings: Build Settings -------------- The Build Settings section of the Navigator contains the build-specific settings for your app in general and for specific OS targets. You can check the box next to each target in order to build an app for that target. Desktop targets are: Windows, macOS and Linux. Note: Properties in this section that are accessible by the :doc:`App` function are in italics. .. _/topics/application_structure/desktop/desktop_apps/shared: Shared ****** The Inspector for Shared settings contains these properties. .. image:: https://documentation.xojo.com/topics/application_structure/desktop/images/desktop_apps_shared_desktop_properties.png .. _/topics/application_structure/desktop/desktop_apps/version: Version ^^^^^^^ * **Major Version** (*MajorVersion*): The Major version for your app. Version numbers are usually written as 1.2.3.4, where “1” is the major version. * **Minor Version** (*MinorVersion*): The Minor version for your app. Version numbers are usually written as 1.2.3.4, where “2” is the minor version. * **Bug Version** (*BugVersion*): The Bug version for your app. Version numbers are usually written as 1.2.3.4, where “3” is the bug version. * **Stage Code** (*StageCode*): Used to indicate the type of app you are building (Development, Alpha, Beta, Final). * **Non Release Version** (*NonReleaseVersion*): The Non Release (build) version for your app. Version numbers are usually written as 1.2.3.4, where “4” is the non release version. Windows only allows a maximum value of 65535 for the Non Release Version. Entering a larger number will prevent your app from compiling. * **Auto Increment Version**: When ON, the Non Release Version is increased by one each time you do a Build (but not when you Run). * **Version** (*Version*): A short text description for the app. Usually this contains just the version number (such as 1.2.3.4) and is displayed by some operating systems in Get Info or Property windows for the app. * **Copyright** (*Copyright*): A longer text description for the app. Usually this contains the app name, copyright, version and other information. This is displayed by some operating systems in Get Info or Property windows for the app. * **Description** (*Description*): A text description for the app that may be displayed by some operating systems in Get Info or Property windows for the app. .. _/topics/application_structure/desktop/desktop_apps/build: Build ^^^^^ * **Use Builds Folder**: When ON, a separate folder is placed alongside the project file. Each platform that is built (macOS, Windows, Linux) also gets its own subfolder within this build folder. * **Include Function Names**: When ON, the actual names of your function calls are embedded in the built app. This is useful for debugging purposes and for getting stack traces. * **Language**: The language to used to resolve dynamic constants. Use Localization preferences to change the order of the languages. * **Optimization Level** (OptimizationLevel): Used for 64-bit builds and 32-bit ARM builds/debug runs (those that use the LLVM compiler). Choices are: * Default (0): Fastest build times. This setting is recommended for most apps and should also be used when debugging. * Moderate (6): Math-related code runs faster, but builds take longer. * Aggressive (4): Math-related code is highly optimized for performance, but builds may take a significant amount of time. * **Supports Hi-DPI**: Set to ON to indicate that this app should be built with support for hi resolution dots per inch displays (HiDPI). Use the :ref:`DesktopApplication.AllowHiDPI` constant to check this in your code. * **Supports Dark Mode**: Set to ON to indicate that this app works with Dark Mode on macOS Mojave (and later) or Windows. This is ignored on Linux as the app will always take on the OS appearance. Use the :doc:`AppSupportsDarkMode` constant and the :ref:`Color.IsDarkMode` method to check this in your code. .. _/topics/application_structure/desktop/desktop_apps/debug: Debug ^^^^^ * **Command Line Arguments**: These are the command-line arguments that are passed to your app when you run it in Debug mode. * **Destination**: Specifies the path where the app is located when you run it in Debug mode. If not specified, then the app is placed alongside your project file (or in a folder alongside the project file on Windows and Linux). .. _/topics/application_structure/desktop/desktop_apps/usage: Usage ***** You can access some of these properties in your code by referencing the name in italics as a property of the App object. For example, this gets the Major Version: .. code:: xojo Var majorVer As String = App.MajorVersion You can also access/change some of these properties in an IDE Script. This IDE Script code changes the Optimization Level to Moderate: .. code:: xojo PropertyValue("App.OptimizationLevel") = "6" .. _/topics/application_structure/desktop/desktop_apps/build_settings/macos: macOS ***** The macOS section contains settings used when building the macOS app. .. image:: https://documentation.xojo.com/topics/application_structure/desktop/images/desktop_apps_mac_desktop_properties.png * **Mac App Name**: The actual file name for your macOS app. If not specified, the ".app" suffix is automatically added when the app is built. * **File Types**: Opens the Accept File Types dialog used to specify the acceptable file types that the app supports. For each file type, you can specify its role as None, Viewer, Editor or Shell. * None: Your app cannot handle the file type. * Viewer: Your app can open and read the file type, but cannot save it. * Editor: Your app can open, read, edit and write using the file type. This is what you should normally choose. * Shell: Not documented by Apple. * **Architecture**: The CPU architecture for the app. Choices are x86 64-bit, ARM 64-bit and Universal. * **Bundle Identifier**: The bundle identifier is required by macOS and is used as a unique descriptor for your app. It is usually specified as a reverse domain name, such as com.xojo.myapp. This is added to the CFBundleIdentifier section of the app's plist. * **Minimum Version - macOS**: Indicates the minimum version of macOS required for the app to run. If the user attempts to run the app on previous version of macOS, macOS presents a dialog box specifying the minimum required version. .. _/topics/application_structure/desktop/desktop_apps/build_settings/windows: Windows ******* The Windows section contains settings used when building the Windows app. .. image:: https://documentation.xojo.com/topics/application_structure/desktop/images/desktop_apps_windows_desktop_build_settings.png * **Windows App Name**: The actual file name for the Windows app. If not specified, the ".exe" suffix is automatically added when the app is built. * **Company Name**: The "Company Name" appears in the Copyright section of the File Explorer app properties in the Details tab. You can change this in IDE Script with the propertyvalue of "CompanyName". * **Product Name**: The name of the product as installed in the Windows Start > All Programs menu. This also appears in "Product name" on the File Explorer app properties Details tab. You can change this in IDE Script with the propertyvalue of "ProductName". * **Internal Name**: This is useful when your product has a different internal name than its external name. This is not shown on the File Explorer app properties Details tab. You can change this in IDE Script with the propertyvalue of "Internalname". * **File Description**: This description is displayed in the File Explorer app properties in the Description area of the General tab and on the Details tab. * **MDI**: When ON, the app's windows will be enclosed in the "parent" MDI window. MDI stands for Multiple Document Interface. Although still supported by Windows, it is not commonly used. * **MDI Caption**: If MDI is ON, you can enter the caption that appears in the title bar of the MDI window. * **Architecture**: The CPU architecture for the app. Choices are x86 32-bit, x86 64-bit and ARM 64-bit. x86 64-bit is the default. .. youtube:: g7LjpAV3uRU These settings are available on the "Advanced" (gear) tab of the Inspector: .. image:: https://documentation.xojo.com/topics/application_structure/desktop/images/desktop_apps_windows_desktop_advanced_build_settings.png * **Include Runtime DLLs**: Copies the Universal Runtime DLLs beside your app executable. For more information, refer to Windows Universal Runtime. * **Include PDB**: Generates a Program Database (PDB) file that can be used for advanced debugging. For more information, see the `What the PDB is this? `_ blog post. * **Use WinUI (Experimental)**: For use with plugins that utilize WinUI when drawing controls. The following are also available when the Architecture is 64-bit: * **Security**: This setting indicates the security level that the app runs as. Choices are: "User" (default), "Highest Available", and "Administrator". This would be used if your app needs to run at a higher security level than a regular user, for example if it needed to modify the firewall or install a service. Available when Architecture is 64-bit. .. _/topics/application_structure/desktop/desktop_apps/build_settings/linux: Linux ***** The Linux section contains settings used when building the Linux app. .. image:: https://documentation.xojo.com/topics/application_structure/desktop/images/desktop_apps_linux_desktop_build_settings.png * **Linux App Name**: The actual file name for the Linux app. * **Architecture**: The CPU architecture for the app. Choices are ARM 32-bit, x86 32-bit, x86 64-bit and ARM 64-bit. Linux ARM apps run on the :doc:`Raspberry Pi` and other similar single-board computers. .. _/topics/application_structure/desktop/desktop_apps/this_computer: This Computer ************* The This Computer section shows you the appropriate target settings (macOS, Windows or Linux) for the OS you are currently using. For example, if you are running Xojo on macOS, then This Computer shows the macOS Build Settings. .. _/topics/application_structure/desktop/desktop_apps/building_and_deployment: Building and deployment ----------------------- To create a stand-alone app for deployment, you create a build by clicking the Build button on the main toolbar or choosing Project > Build from the menu. This creates apps for each of the OS platforms you selected, with separate folders for each (when Use Build Folders is ON). You can also use IDE Scripting to create builds. .. _/topics/application_structure/desktop/desktop_apps/build_folder_names: Build folder names ****************** .. csv-table:: :header: "OS", "Architecture", "Folder Name" :widths: auto "Linux","x86 32-bit","Linux" "Linux","x86 64-bit","Linux 64 bit" "Linux","ARM 32-bit","Linux ARM" "Linux", "ARM 64-bit", "Linux ARM64" "macOS","x86 64-bit","macOS 64 bit" "macOS","ARM 64-bit","macOS ARM 64 bit" "macOS","Universal","macOS Universal" "Windows","x86 32-bit","Windows" "Windows","x86 64-bit","Windows 64 bit" "Windows","ARM 64-bit","Windows ARM 64 bit" Desktop apps are deployed by installing them on a computer and running them on the computer. Once you have created your builds, you'll need to test them on the target OS and possibly package them into a format for distribution (such as an installer). Always test your apps on the specific target platform! .. _/topics/application_structure/desktop/desktop_apps/building_and_deployment/macos: macOS ***** macOS apps consist of a single App file, called the Application Bundle. You can embed other files into the Application Bundle because it is technically a folder that macOS treats as a file. In Finder, you can right-click on any App and select "Show Package Contents" to see the actual contents of the Application Bundle as a folder. Of course, your app can still have separate files not in the Bundle, in which case you want to make sure that your app is in a folder of its own. Since an app is technically a folder, you have to include it in some sort of container in order to distribute it, such as a disk image (DMG), installer or even a simple Zip file. Refer to Desktop Deployment for macOS for specifics on macOS app deployment. .. _/topics/application_structure/desktop/desktop_apps/building_and_deployment/windows: Windows ******* Windows apps have a minimum of these parts: the main executable, its resources (contained in a Resources folder) and its libraries (contained in a corresponding Libs folder). All parts are required in order for the app to run. The Libs and Resources folder can be renamed to just "Libs" or "Resources". This allows several apps in the same folder to shared the Libs and Resources folders. Libs can only be shared for apps built with same version of Xojo. When building your application, Xojo will create a "resources.pri" file in your build directory. This file contains theming assets for your application. Removing this file could negatively impact your application's appearance, or even cause it to crash. Generally you should use an installer to deploy Windows apps. With an installer you can create shortcuts in the Start menu or Desktop and copy the files to the appropriate locations. There are many products available for creating installers for Windows apps. Two that work well with Xojo apps are Inno Setup and Advanced Installer. For simple needs or for testing purposes, you can also just zip the Windows app and its related folders for distribution. You can create a Zip by right-clicking on the parent folder in Windows Explorer and selecting Send To > Compressed (zipped) Folder. Once unzipped, the app can be run from any location. Although this can be useful for testing purposes, it is not recommended for proper Windows app deployment. When you compile a macOS app on Windows, the resulting app is created as a tar archive file. You can transfer this file to macOS and expand the archive in order to run it. Refer to Desktop Deployment for Windows for specifics on Windows app deployment. .. _/topics/application_structure/desktop/desktop_apps/building_and_deployment/linux: Linux ***** Linux apps also have these parts: the main executable and its libraries (contained in a corresponding Libs folder). Both parts are required in order for the application to run. It is also possible that there is a Resources folder that may contain localization files (or other files that you chose to copy there using Build Automation). Additionally, app icon PNG files are placed alongside the built app with these names: appicon_128 (128x128), appicon_48 (48x48) and appicon_32 (32x32). The Libs and Resources folder can be renamed to just "Libs" or "Resources". This allows several apps in the same folder to shared the Libs and Resources folders. Libs can only be shared for apps built with same version of Xojo. How you deploy a Linux application varies depending on the Linux distributions you support. Different distributions have different formats for packaging. Common installer formats are deb (used by Debian and Ubuntu) and RPM (RedHat Package Maker) used by RedHat. For simple needs, you can also just zip the Linux application and Lib folder for distribution. When you compile a macOS app on Linux, the resulting app is created as a tar archive file. You can transfer this file to macOS and expand the archive in order to run it. Refer to Desktop Deployment for Linux for specifics on Linux app deployment. iOS === .. toctree:: :maxdepth: 1 :name: sec-ios App icons Copy files to iOS device iOS apps Retina images Using a plist ========= App icons ========= There are also a wide variety of app icon sizes for the various iOS devices. When you create a new iOS project, an empty App Icon item is added to your project. When you click on it, you'll see a list of available app icons sizes. Drag the images you want to use to the appropriate item. Note that @2x and @3x icons are used on retina devices and are double or triple the resolution of @1x images. You can use the iOS ImageMaker utility to create all the icon files for you from a single image or you can create them yourself (see below). Your app icons should be opaque with no transparency. If you have deleted the App Icon project item from your project you can add it back by selecting Insert App Icon from the Insert menu. If you created an App Icons folder using earlier versions of Xojo, you should migrate it to an App Icon item by right-clicking on it an selecting "Convert" or by deleting the App Icons folder, selecting Insert > App Icon from the menu and then dragging in your images to the appropriate sections. For a description of other App Icon recommendations from Apple, refer to the `iOS Human Interface Guidelines `_ section on App Icons. .. _/topics/application_structure/ios/app_icons/ios_imagemaker_utility: iOS ImageMaker utility ---------------------- .. image:: https://documentation.xojo.com/topics/application_structure/ios/images/app_icons_ios_image_maker.png The iOS ImageMaker utility included with Xojo can be used to quickly create all the file sizes you need for your App Icon and Launch Images. This utility is located in your Xojo folder in Extras/iOS Utilities On the App Icons tab, choose the icon image to scale to the other sizes. For best results, select an icon that is 180x180 or larger. Then select the icons sizes you want and click the Create Icons buttons. The tool will create a folder containing the icon files. You can then drag each icon to the appropriate image size of the App Icon Image Set in your iOS project. ======================== Copy files to iOS device ======================== There are two ways to copy files to your iOS device. You can use a Copy Files Build Step or you can use iTunes after you set your app up for File Sharing. .. _/topics/application_structure/ios/copy_files_to_ios_device/using_copy_files_build_step: Using Copy Files build step --------------------------- You can use Build Automation to copy files into the Resources folder of your iOS app. When the app is running on the device, you can access the files directly or copy them out of the Resources folder to another location (this is required to write to the files as the Resources folder is read-only). .. note:: The Resources folder is read-only! If you need to modify files copied in the manner, you first have to copy them out of Resources and into another location, such as Documents. Here are the steps to set up a Copy Files Build Step: #. A Copy Files Build Step is added to your project by selecting Insert > Build Steps > Copy Files. In the center area you can drag the files (or folders) you want to copy to the device. #. In the Inspector, change the Destination to "Resources Folder". #. You can also give this Copy Files step a name. It is helpful to give it a name that describes what it is copying. #. Drag the Copy Files Step from its original location onto the the iOS item in the Build Settings section of the Navigator. Be sure that it is after the Build item, but before the Sign item. Any Copy File Build Steps that you add should be before the "Sign" step so that the files you have copied into the app bundle are properly signed for submission to the App Store or deployment to a device. When you Run or Build your project, these files are now copied to the Resources folder. Your app can access the files using :doc:`SpecialFolder.Resources`, which returns a FolderItem that points to the specified file in Resources. For example, if you've copied a file called MyFile.html to Resources, you can access it like this: .. code:: xojo Var myFile As FolderItem = SpecialFolder.Resource("MyFile.html") Now that you have a FolderItem, you can use the file directly or you can copy it to Documents so that it can be modified by using the CopyTo method of FolderItem: .. code:: xojo Var myFile As FolderItem = SpecialFolder.Resource("MyFile.html") If myFile.Exists Then Var destFile As FolderItem = SpecialFolder.Documents.Child(myFile.Name) myFile.CopyTo(destFile) End If You can also copy files to a folder within Resources by specifying a value for the Subdirectory property in the Inspector for the Build Step. You still use the :ref:`Resource` function to get the folder, which you can then iterate through to access the files: .. code:: xojo Var myFolder As FolderItem = SpecialFolder.Resource("MyFolder") If myFolder.Exists Then For Each c As FolderItem In myFolder.Children ' use c as necessary to access or copy the file Next End If .. note:: The Framework folder is also supported. Use this folder when you need to include a framework file to which you plan to access via :doc:`Declares`. .. _/topics/application_structure/ios/copy_files_to_ios_device/finder_file_sharing: Finder file sharing ------------------- When you enable File Sharing for an iOS app, you can use the macOS Finder to sync files to or from the app's Documentation folder on the device. In order to enable File Sharing, click on iOS under Build Settings in the Xojo IDE Navigator then in the Inspector, click the File Sharing switch. Build your app and deploy it to a device. Now any files that your app creates in the :doc:`SpecialFolder.Documents` folder are now accessible through the Finder when your device is connected to the computer via USB. 1. In the Finder, open a Finder window then click on the device and click the Files button to switch the listbox below it to show apps that save files. #. Find your app and click to expand it to see files that are in the app's Documents folder. #. You can click on individual files to save them to your computer or you can choose to add files to the list. Files you add are copied to the device when you sync it using the Finder. ======== iOS apps ======== iOS apps run on iOS devices such as iPhones and iPads. You have to be using Xojo on a Mac in order to be able to create iOS projects and build iOS apps. .. _/topics/application_structure/ios/ios_apps/ios_project_overview: iOS project overview -------------------- .. image:: https://documentation.xojo.com/topics/application_structure/ios/images/ios_apps_ios_project_items.png When you create your first iOS project, the following project items are added automatically: * :doc:`App`: The App object works similarly to how it works for desktop and web projects. In the App Inspector, you can set the Default Screen to use for iPhone and iPad-sized devices. Leave a device screen blank if you do not want it to have a native UI for the device. For example, to prevent an app from working on an iPhone, select "None" for the Default iPhone Screen property. If you do not want the app to run natively on an iPad, select "None" for the Default iPad Screen property. Keep in mind that the app will still run on the iPad, but will do so by running the iPhone app in the OS "scaled" mode. * iPhoneScreen: Specifies the initial screen displayed when the app is started on iPhone-sized devices. * iPadScreen: Specifies the initial screen displayed when the app is started on iPad-sized devices. * View1: This the main layout where controls are placed. It is roughly equivalent to a Window or WebPage in desktop and web projects. * App Icon: This item is where the various size app icons for the app are placed. You can use the ImageMaker utility (in the Extras/iOS Utilities folder of the Xojo installation) to create the image files and then drag them into the various sized areas. * Launch Screen: The screen that will be displayed when the app is launched. The LaunchScreen is provides the same functionality as a splash screen in a desktop app. Typically these are screenshots of your empty default screen so that it makes it look like your app is starting quickly, but they can be almost anything. To prevent an app from working on an iPhone, select "None" for the Default iPhone Screen App property. If you do not want the app to run natively on an iPad, select "None" for the Default iPad Screen App property. Keep in mind that the app will still run on the iPad, but will do so by running the iPhone screen in the iOS "scaled" mode. To prevent an app from working on an iPhone, select "None" for the Default iPhone Screen App property. If you do not want the app to run natively on an iPad, select "None" for the Default iPad Screen App property. Keep in mind that the app will still run on the iPad, but will do so by running the iPhone screen in the iOS "scaled" mode. .. _/topics/application_structure/ios/ios_apps/app_object: App object ---------- The App object works similarly to how it works for desktop and web projects. For an iOS project, the App object is a subclass of :doc:`MobileApplication`. You use it to specify the default screen layouts for the devices. You can add events, properties and methods to the App object. Use the App prefix to refer to public properties and methods elsewhere in your code. For example, to refer to the property UserName added to the App object in a class of the project, you would write: .. code:: xojo App.UserName = "Billy" .. _/topics/application_structure/ios/ios_apps/event_handlers: Event Handlers ************** The Application class for an iOS application has event handlers. They are: * :ref:`LowMemoryWarning`: Called when iOS is running low on memory. * :ref:`Opening`: Called when an application is launched. * :ref:`UnhandledException`: Called when an exception was raised but not handled by its own :doc:`Try` block. .. _/topics/application_structure/ios/ios_apps/properties: Properties ********** * Default iPhone Screen: The Screen to display when an iPhone-sized device is used. Set this to "None" to prevent the app from working on an iPhone. * Default iPad Screen: The Screen to display when an iPad-sized device is used. Set this to "None" when you do not want a native iPad app. The app will still run on the iPad, but will do so by running the iPhone screen in the iOS "scaled" mode. .. _/topics/application_structure/ios/ios_apps/usage: Usage ***** The LowMemoryWarning event handler gets called when iOS is running low on memory. Since iOS does not use virtual memory, it has to purge apps from memory when the OS detects memory is running low. Implement this event handler if your app reserves a lot of memory (large Text arrays, for example) so that you can release the memory. If you do not release the memory quick enough, iOS will terminate the app. The Opening event handler is called when the app is launched (or relaunched if it was removed from memory). It is not called if your app remains in memory and the user switches back to it from another app. The Opening event handler is a great place for app initialization code. The UnhandledException event handler is called when an exception was raised and not handled by its own Try...Catch block. Return True to allow your app to continue, essentially ignoring the exception (this may cause instability depending on the cause of the exception). This event is commonly used to log error information or maybe save state before the app quits. You might find it useful to put a Break statement in this event handler so that the debugger is displayed when an unhandled exception occurs. In addition, you can add some code to save the stack to an array so you can review it in the debugger: .. code:: xojo Var stack() As StackFrame = exc.StackFrames Break .. _/topics/application_structure/ios/ios_apps/screens: Screens ------- In the Inspector for App, you can specify the default screens to use when the app is run on an iPhone or an iPad. A Screen is a project item that specifies the type of layout and supported orientations. Two Screens are added by default with new projects: iPhoneScreen and iPadScreen. When you click on a Screen you are shown a preview of how it might look on the device. .. _/topics/application_structure/ios/ios_apps/build_settings: Build Settings -------------- The Build Settings section of the Navigator contains the build-specific settings for your app. .. _/topics/application_structure/ios/ios_apps/shared: Shared ****** The Inspector for Shared settings contains these properties: * Major Version (MajorVersion): The Major version for your app. Version numbers are usually written as 1.2.3.4, where “1” is the major version. * Minor Version (MinorVersion): The Minor version for your app. Version numbers are usually written as 1.2.3.4, where “2” is the minor version. * Bug Version (BugVersion): The Bug version for your app. Version numbers are usually written as 1.2.3.4, where “3” is the bug version. * Stage Code (StageCode): Used to indicate the type of app you are building (Development, Alpha, Beta, Final). * Non Release Version (NonReleaseVersion): The Non Release (build) version for your app. Version numbers are usually written as 1.2.3.4, where “4” is the non release version. * Auto Increment Version Info: When ON, the Non Release Version is increased by one each time you do a Build (but not when you Run). * Short Version (ShortVersion): A short text description for your app. Usually this contains just the version number (such as 1.2.3.4) and is displayed by some operating systems in Get Info or Property windows for the app. * Long Version (LongVersion): A longer text description for your app. Usually this contains the app name, copyright, version and other information. This is displayed by some operating systems in Get Info or Property windows for the app. * Package Info (PackageInfo): A text description for your app that may be displayed by some operating systems in Get Info or Property windows for the app. * Use Builds Folder: When ON, a separate folder is placed alongside the project file. * Include Function Names: When ON, the actual names of your function calls are embedded in the built app. This is useful for debugging purposes and for getting stack traces. * Language: Allows you to specify the language to use to resolve dynamic constants. * Optimization: Used for App Store builds (which use the LLVM compiler). There are these choices: * Default: Optimized for fast build times. * Moderate: A balance between build times and math-related code execution time. * Aggressive: Longer build times, but code is more optimized for faster math-related code execution time. * Simple Reference: When ON, you do not have to use namespace prefixes with any of the new framework classes used by iOS projects. * Command Line Arguments: These are the command-line arguments that are passed to your app when you run it in Debug mode. * Destination: Specifies the path where the app is located when you run it in Debug mode. If not specified, then the app is placed alongside your project file (or in a folder alongside the project file on Windows and Linux). .. _/topics/application_structure/ios/ios_apps/ios: iOS *** .. image:: https://documentation.xojo.com/topics/application_structure/ios/images/ios_apps_ios_build_settings_inspector.png The section of the Build Settings contains iOS-specific settings, including: * iOS App Name: The actual name for your iOS app. This is the name that appears on the Springboard in iOS and is the name of the actual app file. * Bundle Identifier: The bundle identifier is used by iOS as a unique descriptor for your app. It is usually specified as a reverse domain name, such as com.xojo.myapp. A bundle identifier is required for iOS apps. When submitting to the App Store, this bundle must match the bundle for the app information you created in iTunes Connect. * Minimum Version: This is the minimum version of iOS upon which your app will run. * Team: This is the Team to use for certificates when code signing the iOS app. You have to code sign the app before it can be installed to a device or submitted to the App Store. * Build For: This allows you to indicate how the app will be deployed so that Xojo can output the appropriate format (Development, App Store or Enterprise). .. tip:: Choosing Build For > App Store, will cause Xojo to create a signed package file that can be submitted to the App Store using iTunes Connect and Xcode's Application Loader. You can access this property from IDE Scripting using the name "BuildForAppStore". .. _/topics/application_structure/ios/ios_apps/entitlements_and_additional_information: Entitlements and additional information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ By clicking on the Advanced (Gear) tab in the Inspector you can set the Entitlements for your iOS app. You can specify your own Info.plist that is configured with the entitlements you need or you can turn the invidividual entitlements on using the Inspector properties. Some entitlements have additional settings you will need to make by clicking on the Options button next to the entitlement. Note that turning on an entitlement does not enable features in your app, it only requests permission (from iOS) for your iOS app to make use of the features. Do not enable entitlements for features that your app does not use or your app will likely be rejected when submitted to the App Store. Refer to Apple's Entitlements docs for more information. .. csv-table:: :header: "Type", "Description" :widths: 15, 55 "App Groups","Allows you to define the app groups your app of which your application is a part. All apps in the same group produced by a single development team can access shared containers and communicate using IPC. Apps may belong to one or more app groups. More information is available in `Apple's documentation `_. The Xojo IDE will automatically add the *group.* prefix if you don't supply it yourself." "Apple Pay","A list of merchant IDs for which your app can accept Apple Pay payments. These are typically in reverse domain notation, starting with the string *merchant.* For example *merchant.com.yourcompany*. More information is available in `Apple's documentation `_." "Associated Domains","A list of domains for specific services, such as shared web credentials, universal links and App Clips. The domains have the format : with the services being one of: * webcredentials * applinks * activitycontinuation * appclips More information is available in `Apple's documentation. `_" "Background Modes","These are services provided by your app which require it to run in the background. The modes are: * Audio, Airplay and Picture in Picture (Audio) * Location updates (Location) * Voice over IP (VOIP) * External accessory communication (External Accessory) * Uses Bluetooth LE accessories (Bluetooth Central) * Acts as a Bluetooth LE accessory (Bluetooth Peripheral) * Background fetch (Fetch) * Push notifications (Remote Notification) * Background processing (Background Processing)" "Camera Access","Allows you to add a message indicating why your app needs access to the user's camera. This is used with the **Image Picker** control." "Custom URL Schemes","Allows you to specify one or more URL schemes to which your app can respond. URLs are received by your app in the App.HandleURL event." "Data Protection","Allows your app to protect specific files using the built-in encryption available on some devices. When your applications designates a specific file as protected, the system stores that file on-disk in an encrypted format. For use with Declares." "File Sharing","Indicates whether your app shares its Documents folder through iTunes." "Game Center","For gaming apps, this allows players to connect to the Game Center service, enabling them to interact with their friends, view leader boards, or play head to head in your game. Note: Toggling this switch also indicates that the device must be capable of running GameKit." "HealthKit","Allows your app, with user permission, to store and retrieve personal health information." "HomeKit","Allows your app to interact with HomeKit accessories and create home configurations." "iCloud","Enables your app to store data and documents in iCloud." "Inter-App Audio","Allows your app to send audio and to receive audio from other inter-app audio-enabled apps. Note: This was deprecated by Apple in iOS 13." "Keychain Sharing","Allows your app to share passwords from its keychain with other applications made by your team." "Location","Allows you to add messages that will be displayed when asking users for access to their location. There is one message each for *When In Use* and *Always*." "Maps","Allows your routing app to indicate that it can provide specific directions beyond what the Maps app supports by default. The Uber app is an excellent example of an app that would need to provide this information." "Personal VPN","Allows your app to create and control a custom system VPN configuration." "Photos Access","Allows you to add a message indicating why your app needs access to the user's Photo Library. This is used with the Image Picker control." "Push Notifications","Specifies whether the device should use the development or production Apple Push Notification service when registering for push notifications. Xojo will automatically set this value based on whether or not you are building for the App Store. This works with Xojo's NotificationCenter class." "Shortcut Items","Allows you to specify the default set of shortcut items. The editor accepts a comma delimited list of SFSymbols, UIApplicationShortcutIconTypes and/or images from your project." "User Authentication","Allows your app to use Touch ID/Face ID. Enable this when using Xojo's UserAuthentication class." "Wallet","Allows your app to add items to and delete items from the Wallet." "Wireless Accessories","Allows your app to configure MFi Wi-Fi accessories which are devices compatible with HomeKit, AirPlay Audio, Find My and CarPlay." .. _/topics/application_structure/ios/ios_apps/build_folder_names: Build folder names ****************** .. csv-table:: :header: "Type", "Architecture", "Folder Name" :widths: auto "Build","ARM","iOS" "Debug","ARM","iOS Simulator" .. _/topics/application_structure/ios/ios_apps/ios_app_deployment: iOS app deployment ------------------ Refer to this topic for an overview of the various ways that you can deploy iOS apps: * :doc:`iOS Deployment Overview` .. _/topics/application_structure/ios/ios_apps/see_also: .. seealso:: :doc:`MobileApplication` class; :doc:`Mobile Screens`, :doc:`iOS Deployment Overview` topics ============= Retina images ============= When you add an image to your project (Insert > Image) an :doc:`Image Set` is created. An Image Set provides a way for you to manage all the different sizes of an image that are needed for iOS retina screens. The Image Set editor displays three boxes for three different image sizes: 1x, 2x and 3x. Images sized at 1x are the original image as it appears on non-Retina devices. Images sizes at 2x have double the horizontal and vertical resolution and are used on most retina devices. Images sized at 3x are triple the horizontal and vertical resolution. To add the images, drag them from Finder onto the appropriate box. For best results, iOS prefers PNG images. These images are not copied to your project, but are referenced as external items. This means if you move the external files or copy the project, the images may not be found. It is often convenient to create a ProjectImages folder next to your project where you place your images files and then drag them from there. If you drag an image file onto the Navigator, a new Image Set with the image populated at the 1x size is created. If you do not provide images in all three sizes, iOS will scale from the images you have provided. Scaling up from 1x images results in a blurry image, so at the very least you will want to specify images in 2x sizes since most devices have 2x retina screens. .. _/topics/application_structure/ios/retina_images/using_images_in_your_code: Using images in your code ------------------------- When you create an Image Set, you can give it a descriptive name. The Image Set in the above screenshot is named "ClockImage". To use the Image in your code, you just refer to it by its name. So to draw it in the Paint event handler of a Canvas, you would do this: .. code:: xojo g.DrawImage(ClockImage, 0, 0) To use the Image with an Image View control, use the Inspector to pick it from the drop-down in the Image property. Or you can assign it via code. This assigns the Image Set in an MobileImageViewer's Opening event handler: .. code:: xojo Me.Image = ClockImage You do not have to worry about the specific size of the image as the appropriate one will be chosen or one will be created by scaling from what is available. ============= Using a Plist ============= Apps built for iOS and macOS consist of an “application bundle” which contains the app itself, resources and other components such as frameworks. It also contains an “Info.plist” file, which is a special XML file in Apple's "property list" format containing specific settings that tell the OS about your app. For certain apps, you may need to modify the plist to enable features. To make it easier for you to include your own settings in the app plist file, you can create your own Info.plist file with the specific settings you need and drag it into your Xojo project. When your app is built, the settings in your plist file are copied to the app plist file. One example of when you might need a plist to to deal with :doc:`Using Non-Secure URLs on macOS and iOS` on iOS or macOS 10.11 or later when connecting to http URLs. .. _/topics/application_structure/ios/using_a_plist/general_plist_guidelines: General Plist guidelines ------------------------ * Do not add more than one plist file to a project. * Any items in your plist file that are duplicates of what is created during the build process are overwritten by the build process. * Only top-level keys and their entire value are copied. For example, if a key specifies a dict for a value then the entire dict is copied. Top-level keys are keys that are immediate children of PLIST > DICT in the plist XML structure. * The file must have both a plist header and the extension ".plist" before you add them to a project. .. _/topics/application_structure/ios/using_a_plist/apple_docs: Apple docs ---------- Refer to Apple's docs for more details about plist and keys: * `Apple Info.plist Key Reference `_ * `Apple Security topic `_ * `Apple iPhone Plist Keys `_ * `Apple Cocoa Plist Keys `_ .. _/topics/application_structure/ios/using_a_plist/see_also: .. seealso:: :doc:`URLConnection`, :doc:`DesktopHTMLViewer`, :doc:`MobileHTMLViewer` classes; :doc:`Using Non-Secure URLs on macOS and iOS` topic ============== The App Object ============== Every project has an App object that is automatically added to it when the project is created. This App object gives you access to events and properties that are specific to the app type. If you look at the Inspector for the App object, you'll see that it has a super which varies by project type: .. csv-table:: :header: "Project Type", "Application Class" :widths: auto Android, :doc:`MobileApplication` Console, :doc:`ConsoleApplication` Desktop,:doc:`DesktopApplication` iOS, :doc:`MobileApplication` Web, :doc:`WebApplication` .. _/topics/application_structure/ios/the_app_object/accessing_the_app_object: Accessing the App Object ------------------------ You can set the scope of properties, methods and constants of the App subclass using the same rules as for any class. The global :doc:`App` function gives you a reference to the App class in your project so that you can access its public properties, methods and constants. To access a public property somewhere in your project, you would write it like this: .. code:: xojo value = App.MyProperty You can rename the App object to give it a more descriptive name. If you do, you still use the :doc:`App` function to get access to it. If you wish, you can even subclass App. When you do this on a desktop project, your new subclass gets the properties for the default window, menu bar and icon. If you have subclassed App, the App function gives you a reference to the subclass instead. .. note:: You use the :doc:`App` function to get a reference to the App object even if you have renamed or subclassed the App object. .. _/topics/application_structure/ios/the_app_object/see_also: .. seealso:: :doc:`Desktop Apps`, :doc:`Web Apps`, :doc:`Console Apps`, :doc:`iOS Apps` topics Web === .. toctree:: :maxdepth: 1 :name: sec-web Introduction ============ Introduction ============ Web apps have a graphical user interface that runs inside a web browser and a companion server app that runs on (or as) a web server. You can also create web apps that function as web services by utilizing the HandleURL event on the App object. .. _/getting_started/application_structure/web/introduction/web_project_overview: Web project overview -------------------- When you create a web project, you get the following project items added automatically: * App: The App object allows you to specify some initial app settings, including * Session: Each user that connects to the web app gets its own Session instance. Use the Session object to save information that is specific to the user/session and to access session-specific information. * WebPage1: This is the main window layout where you place your controls. You can add your own web pages as well. .. _/getting_started/application_structure/web/introduction/app_object: App Object ---------- For a web project, the App object is a subclass of :doc:`WebApplication`. You use it to specify the default web page that opens when your app starts and a session connects, default messages, HTMLHeader and the app icon (in various sizes). .. _/getting_started/application_structure/web/introduction/event_handlers: Event Handlers ************** The WebApplication class for a web app has event handlers. They are: * :ref:`Closed`: The Closed event is called when the app quits. * :ref:`HandleURL`: This event fires when an http request comes to your app which would otherwise result in a 404 Page Missing response. * :ref:`Opening`: This event handler is called when your app first starts. * :ref:`UnhandledException`: Called when a runtime error occurs that is not caught by your code. This event gives you a “last chance” to catch runtime errors before they cause your app to quit. .. _/getting_started/application_structure/web/introduction/properties: Properties ********** * :ref:`DefaultPage`: This is the web page that is displayed automatically when the app starts. * :ref:`HTMLHeader`: Can be used to add static HTML code to each page. * Icon: This opens the Icon Editor and lets you specify the various size icons that are used by the app. These icons are used by the loading screen and as the browser favicon. This is a design-time-only property. .. _/getting_started/application_structure/web/introduction/design_considerations: Design considerations --------------------- Keep these things in mind when designing and developing your web apps. .. youtube:: xD0hsQORR4M .. _/getting_started/application_structure/web/introduction/client/server: Client/Server ************* All web apps use a client/server design. This means that a web app has two parts: the client user interface and the app that runs on the server. The client is the part that the user interacts with. For a web app, this is the user interface that runs in a web browser. Web apps support recent versions of modern web browsers such as Safari, Chrome, Firefox and Internet Explorer. The app is where your Xojo code runs and it is typically run on a server. The client and the web app components can reside on the same computer, but they are usually different computers: the user's computer for the web browser/user interface and a server that is running the app. .. _/getting_started/application_structure/web/introduction/latency: Latency ******* When you are testing your web apps locally, both the client (web browser) and server (app) are running on your computer. This means that communication between them is quick. But when you deploy an app to a web server, there can be a longer delay as the web browser has to communicate across the Internet to the app on the server. This delay is called latency and has a lot of factors, including the speed of the user's Internet connection, general Internet congestion and the responsiveness of the server. It is important when designing your app to keep latency in mind, by not designing your UI as if it expects fast (desktop-like) responsiveness. .. _/getting_started/application_structure/web/introduction/multiple_users_and_sessions: Multiple users and sessions *************************** A web app typically has multiple users connected to it at one time. This is handled for you using a concept called Sessions. The Session object is a subclass of WebSession and is automatically added to all web projects. Each user that connects to your web app gets its own Session subclass that you can access using the Session global function. Use the :doc:`Session` object to store information that is global to the connected session, but not global to the overall app. .. _/getting_started/application_structure/web/introduction/cookies: Cookies ******* Cookies are a browser feature that allows the web browser to save Session-specific data. Web apps have built-in support for Cookies as methods of the :doc:`WebSession` class. Cookies are a standard way for web browsers to retain information pertaining to a session. For example, if your web app has users log in using a UserID, you could save the UserID as a cookie. The next time they open the web app, you can ask the web browser for the cookie and if it is available, pre-fill the UserID name for them. This is how you set a cookie value: .. code:: xojo Session.Cookies.Set("UserName", "BobRoberts") A cookie without an expiration date will expire the moment the web session ends. To set the expiration date, pass in a :doc:`DateTime` object. This cookie will expire in one year: .. code:: xojo Session.Cookies.Set("UserName", "BobRoberts", DateTime.Now.AddInterval(1)) This is how you retrieve a cookie value: .. code:: xojo Var userName As String userName = Session.Cookies.Value("UserName") .. _/getting_started/application_structure/web/introduction/hashtags: Hashtags ******** Hashtags are a way for you to identify different areas of the web app. Web apps always show the same browser URL, even as you navigate to different web pages. You can use the hashtag as a way to identify different pages (or data) so that the user can return directly to it later. Hash tags are written using the “#” character like this: .. code:: html http://www.mywebsite.com/#details http://www.mywebsite.com:8080/#settings The :doc:`WebSession` event is called when the hashtag is changed by the user. In this event, you can get the new value and choose to navigate to a different page or display different data. For example, you can send the hash tag #50 to display row 50 in a ListBox with code like this: .. code:: xojo Var row As Integer row = Hashtag.Val - 1 Var dataList As WebListBox = MainPage.DataList If row >= 0 And row <= dataList.RowCount Then dataList.SelectedRowIndex = row End If This is an example of a URL for the above code: .. code:: html http://127.0.0.1:8080/#50 More simply, this code in the HashtagChanged event displays a settings page when the URL "http://127.0.0.1:8080/#settings" is used: .. code:: xojo If Hashtag = "settings" Then SettingsPage.Show End If .. _/getting_started/application_structure/web/introduction/build_settings: Build Settings -------------- The Build Settings section of the Navigator contains the build-specific settings for your app in general and for specific OS targets. You can check the box next to each target in order to build an app for that target. Web targets are: Windows, macOS and Linux. .. _/getting_started/application_structure/web/introduction/shared: Shared ****** The Inspector for Shared settings contains these properties: .. image:: https://documentation.xojo.com/topics/application_structure/web/images/introduction_web_shared_build_settings.png * **Major Version**: The Major version for your app. Version numbers are usually written as 1.2.3.4, where “1” is the major version. * **Minor Version**: The Minor version for your app. Version numbers are usually written as 1.2.3.4, where “2” is the minor version. * **Bug Version**: The Bug version for your app. Version numbers are usually written as 1.2.3.4, where “3” is the bug version. * **Stage Code**: Used to indicate the type of app you are building (Development, Alpha, Beta, Final). * **Non Release Version**: The Non Release (build) version for your app. Version numbers are usually written as 1.2.3.4, where “4” is the non release version. * **Auto Increment Version Info**: When ON, the Non Release Version increases by one each time you do a Build. * **Version**: A short text description for your app. Usually this contains just the version number (such as 1.2.3.4) and is displayed by some operating systems in Get Info or Property windows for the app. * **Copyright**: A longer text description for your app. Usually this contains the app name, copyright, version and other information. This is displayed by some operating systems in Get Info or Property windows for the app. * **Description**: A text description for your app that may be displayed by some operating systems in Get Info or Property windows for the app. * **Use Builds Folder**: When ON, builds are placed in a separate folder alongside the project file. Each platform (macOS, Windows, Linux) also gets its own subfolder within this builds folder. * **Include Function Names**: When ON, the actual names of your function calls are included in the built app. This is useful for debugging purposes and for getting stack traces. * **Language**: Allows you to specify the language to use to resolve dynamic constants. * **Optimization**: Used for all 64-bit builds and 32-bit ARM builds (those that use the LLVM compiler). There are these choices: * Default: Optimized for fast build times. * Moderate: A balance between build times and math-related code execution time. * Aggressive: Longer build times, but code is more optimized for faster math-related code execution time. * **Port**: Select a specific port to use to connect to the web app or let the web app choose the port automatically. * **Application Identifier**: This is an unique identifier for the web app. If you deploy multiple web apps on the same server, they must all have unique Application Identifiers. * **Supports Hi-DPI**: Set to ON to indicate that this app should be built with support for hi resolution dots per inch displays (HiDPI). * **Command Line Arguments**: These are the command-line arguments that are passed to your app when running it in Debug mode. * **Destination**: Specifies the path where the app is located when you run it in Debug mode. If not specified, then the app is placed alongside your project file. * **Debug Port**: This is the port that is used when running your web app in Debug mode. Note: If you ever have difficulty debugging your web app (such as it not starting), make sure the debug port is not in use by another app on the computer. When in doubt, change the Debug Port to another value. * **Launch Browser**: If set to ON, the your browser will open with the app in it when the project is run. Turning this setting OFF is useful when building web apps that have no webpages such as a REST API app. .. _/getting_started/application_structure/web/introduction/xojo_cloud: Xojo Cloud ********** .. image:: https://documentation.xojo.com/topics/application_structure/web/images/introduction_xojo_cloud_build_settings.png The Xojo Cloud section is used to deploy your web app to a Xojo Cloud server. * **Application Name**: The name of your web app. Your web app will be deployed into a folder with this name, which will be of the URL to the app. * **Server**: The Xojo Cloud server to which to deploy. You will need to have purchased a Xojo Cloud server and be logged in to your account in Xojo in order to see your servers. * **Statistics**: Click the Show button to display a window of statistics for the entire server and individual apps that are installed on it. * **Last URL**: The previously assigned URL for accessing this app from a browser. * **Domain Name**: The full domain or subdomain used to access this app from a browser. .. image:: https://documentation.xojo.com/topics/application_structure/web/images/introduction_xojo_cloud_server_statistics.png .. _/getting_started/application_structure/web/introduction/macos: macOS ***** The macOS section allows you to specify settings for the macOS web app. * **Mac App Name**: The actual file name for the macOS app. * **Architecture**: The CPU architecture for the app. Choices are x86 64-bit, ARM 64-bit and Universal. * **Bundle Identifier**: This is the same as the Application Identifier. .. _/getting_started/application_structure/web/introduction/windows: Windows ******* The Windows section contains build settings for your Windows web apps. * **Windows App Name**: The actual file name for the Windows app. * **Company Name**: The “Company Name” appears in the Copyright section of the app properties in the Details tab. * **Product Name**: The name of the product as installed in the Windows Start > All Programs menu. This also appears in “Product name” on the app properties Details tab. * **Internal Name**: This is useful when your product has a different internal name than its external name. This is not shown on the app properties Details tab. * **File Description**: [TBD] * **Include Windows Runtime DLLs**: Copies the Universal Runtime DLLs beside your app executable. For more information, refer to Windows Universal Runtime. * **Architecture**: The CPU architecture for the app. Choices are x86 32-bit, x86 64-bit and ARM 64-bit. The default is x86 64-bit. .. _/getting_started/application_structure/web/introduction/linux: Linux ***** The Linux section contains build settings for your Linux web apps. * **Linux App Name**: The actual file name for the Linux app. * **Architecture**: The CPU architecture for the app. Choices are ARM 32-bit, x86 32-bit, x86 64-bit, ARM 64-bit. Linux ARM apps run on the Raspberry Pi and other similar single-board computers. .. _/getting_started/application_structure/web/introduction/deployment: Deployment ---------- The easiest way to deploy your Xojo Web apps is to use Xojo Cloud. With Xojo Cloud, you can deploy your web app in a single step by clicking the Deploy button on the main toolbar. Xojo Cloud also adds many additional features which are covered here: * :doc:`Xojo Cloud` * :doc:`Xojo Cloud Control Panel` * :doc:`Xojo Cloud General Information` Should you prefer to deploy to your own servers, you can also do so as a standalone app. You create a build by clicking the Build button on the main toolbar or choosing Project > Build from the menu. For more information about web app deployment: * :doc:`Web App Deployment Overview` * :doc:`Web App Deployment Details` * :doc:`Xojo Cloud` * :doc:`Deploy Web App to Linux` * :doc:`Deploy Web App to IIS` Build automation ================ .. toctree:: :maxdepth: 1 :name: sec-build_automation Introduction IDE scripting IDE Communicator ============ Introduction ============ The Build Automation feature is used to automatically run specific tasks when you run or build your project. These tasks are called Build Steps. There are three build steps available: Copy Files, Script and External Script. .. _/topics/build_automation/introduction/overview: Overview -------- To add a Build Step to your project, select Insert > Build Step from the main toolbar and then choose a type from the Build Step submenu. Build Steps added to the CONTENTS area of the Navigator are inactive by default. To activate a Build step, you need to move it to a build target. You do this by dragging the Build Step from the Contents area of the Navigator to the Build Settings area of the Navigator. Drop the Build Step onto one of the targets (macOS, Windows, Linux or iOS) and the Build Step becomes active when running or building for that target. You can turn off a Build Step that is on a build target by changing the "Applies To" property to "None". Once you add a Build Step to the target, the target can be expanded to see the build step. When you expand it you will see the build step and an item called Build. The Build item allows you to specify whether the Build Step occurs before the project is built or after. Drag the Build Step before the Build item to have it be processed before the Build is done and drag it after the Build item to have it be built after the Build is done. Each Build Step has an “Applies to” setting (in the Inspector) that allows you to specify: Both, Debug, Release, None. * **Both**: The Build Step is always processed. * **Debug**: The Build Step is processed only when doing a Debug Run. * **Release**: The Build Step is processed only when doing a standalone release Build. * **None**: The Build Step is not processed (disabled). .. note:: For macOS Universal builds, build steps run just once before and after the build process rather than once for each executable built. .. _/topics/build_automation/introduction/copy_files: Copy files ---------- The Copy Files step allows you to copy a file, files or a folder to a specified location during the build process. Drag the files you want copied to the empty area (or use the toolbar buttons to add files or folders). To remove something from the list, select it and press Delete on the keyboard or use the delete button on the toolbar. You can right-click on a file in the list to display a menu with: Open File and Show on Disk. Use Open File to open the file in the OS default viewer. Show on Disk displays the file using the default file system viewer (such as Finder or Explorer). For each of these files you can also specify a subdirectory for them to be copied to by entering a value in the Subdirectory property of the Inspector. You can also specify the destination location for the files by using the Destination property in the Inspector. The choices are: App Parent Folder, Resources Folder, Frameworks Folder, Bundle Folder Parent and Contents Folder. * **App Parent Folder**: For Windows and Linux, the specified files are copied alongside the app executable. For Mac, the files are copied next to the executable in the App Bundle (Contents > MacOS). * **Resources Folder**: The specified files are copied to a Resources folder. For Windows and Linux the Resources folder is created alongside the app itself. For Mac, the Resources folder is contained within the App Bundle (Contents/Resources). In all cases, you should consider the contents of this folder to be read-only. If you need to write to these files, first copy them out to a location with write access (usually :doc:`SpecialFolder`.ApplicationSupport). You can get access to the Resources folder by using the :ref:`SpecialFolder.Resource` method. * **Frameworks Folder**: For Windows and Linux, the files are copied to the Libs folder for the app. For Mac, the files are copied to the Frameworks folder in the App Bundle (Contents/Frameworks). * **Bundle Parent Folder**: For Windows and Linux, the files are copied to the folder containing the executable (same as App Parent Folder). For Mac, the files are copied to the folder containing the App Bundle itself. * **Contents Folder**: For Windows and Linux, the file are copied to the folder containing the executable (same as App Parent Folder). For Mac, the files are copied to the App Bundle (Contents). On iOS, there is an additional step added by default that is called "Sign". Any Copy File Build Steps that you add should be before the "Sign" step so that the files you have copied into the app bundle are properly signed for submission to the App Store. .. _/topics/build_automation/introduction/accessing_files: Accessing files --------------- In most situations, you will use the Resources destination to copy files to the Resources folder for use by your app. To access files in this folder, you can use the :ref:`SpecialFolder.Resource` method to get a :doc:`FolderItem` that points to a file. For example, to get to a database that was copied to the Resources destination, you would use this code: .. code:: xojo Var bundleFile As Folderitem bundleFile = SpecialFolder.Resource("AppDatabase.sqlite") For accessing files in other locations, you will have to directly access the path yourself, starting with :ref:`DesktopApplication.ExecutableFile`, :ref:`WebApplication.ExecutableFile`, or :ref:`ConsoleApplication.ExecutableFile` and use the Child or Parent methods of :doc:`FolderItem` to work from there. .. _/topics/build_automation/introduction/script_and_external_script: Script and external script -------------------------- Scripting is a powerful way to automated your app builds. Scripts can contain a wide variety of commands to control the build process. You can choose to add an inline Script as a Build Step. This adds an item that is a Script Editor. Here you can directly enter the code for your script. You can also add an External Script which lets you reference a script file that is saved in a file. This can be useful if you want to re-use the same script with Windows, macOS and Linux builds. All the scripting commands are listed in the IDE Scripting section, but some useful ones include: DoCommand, DoShellCommand and Speak. DoCommand is used to run built-in IDE commands, perhaps the most useful is to save your project before it is run: .. code:: xojo DoCommand("SaveFile") To add such a script to your build process, add a Script Build Step using the Insert menu or Insert button. The script is added to the Navigator and the Script Editor appears. In the Script Editor, enter the code: .. code:: xojo DoCommand("SaveFile") Now you can drag the script to a target in the BUILD section of the Navigator so that it runs. If you want the script to run before the build is started, then drag it before the "Build" item. If you want it to run after, then drag it after the "Build" item. The Speak command uses the built-in voice to speak the supplied text. You can use this to let you know when a build has finished. .. code:: xojo Speak("Build Complete.") DoShellCommand allows you to run a shell command. You can run any shell command that is available in the Terminal or Command Shell of the operating system. A shell command might be used for more robust file management, to manipulate permissions, run commands to digitally code sign your app or anything else that you want. This example runs the code sign command on a Mac: .. code:: xojo Var command As String command = "codesign -f --deep -s ""Developer ID Application: YourName "" ""YourXojoApp.app """ Var result As String result = DoShellCommand(command) Note that when you code sign your app, you must have all the files for your build already in place. If you copy of modify files in the build after you code sign then that will invalidate the signature. An external script works the same except the script is stored in an external text file that you select. You would use an External Script if you have a script that you share across multiple projects or multiple build targets. .. _/topics/build_automation/introduction/see_also: .. seealso:: :doc:`Introduction to IDE Scripting` IDE Scripting ============= .. toctree:: :maxdepth: 1 :name: sec-ide_scripting Introduction Building commands Constants DoCommand Input output commands Notification commands Project commands Text commands ============================= Introduction to IDE scripting ============================= .. rst-class:: forsearch IDE scripting IDE Scripting is the ability to create scripts that allow you to manipulate the Xojo user interface (IDE). IDE Scripts use the XojoScript engine and support the Xojo programming language, a subset of functions and other IDE Scripting Commands. .. _/topics/build_automation/ide_scripting/introduction/creating_an_ide_script: Creating an IDE script ---------------------- To create an IDE script, select IDE Scripts > New IDE Script from the File menu. This opens the XojoScript Editor. You can open multiple IDE Script editors, each with their own scripts. .. image:: https://documentation.xojo.com/topics/build_automation/ide_scripting/images/introduction_ide_script_editor.png In this editor you write code using the XojoScript scripting language, which consists of Xojo programming language commands (such as If..Then or For..Next). You can also use special IDE Scripting commands such as DoCommand, DoShellCommand and more. All the IDE Scripting commands are listed in the IDE Scripting Commands section. This simple script saves the current project: .. code:: xojo Speak("Saving file.") DoCommand("SaveFile") To run the script, click the Run button on the Xojo Script Editor toolbar. .. _/topics/build_automation/ide_scripting/introduction/saving_and_loading_ide_scripts: Saving and loading IDE scripts ------------------------------ IDE Scripts are saved as text files and are not included within the project. To save a script, use File > Save or Save As. To load an existing script into the Script Editor, use File > Open. You can also add scripts to the IDE Scripts submenu. Clicking a script in this menu immediately runs it; it does not open in the Script Editor. To open it into the Script Editor hold down Option (on macOS) or Control (on Windows/Linux). To have a script appear in this submenu, add the saved script to the Scripts folder alongside the Xojo app or add it to a Scripts folder you create alongside your project file. There are also corresponding shortcut commands to run or open the scripts: .. csv-table:: :header: "Command", "macOS", "Windows / Linux" :widths: auto "Run",":kbd:`Cmd Option #`",":kbd:`Ctrl Alt #`" IDE Scripts can use Xojo language commands in addition to the commands described in the IDE Scripting Commands sections. .. youtube:: 4kETn988AWE .. _/topics/build_automation/ide_scripting/introduction/recording_a_script: Recording a script ------------------ On the XojoScript Editor toolbar is a Record button. When pressed, any commands in the IDE that have a corresponding script command are recorded when they are used. To try this, click the Record button (the text changes to "Stop Recording") and then select File > Open to open a project. You 'll see the this command appear in the XojoScript Editor: .. code:: xojo DoCommand("OpenFile") Now click Run to run your project (not the script) and you 'll see command appear in the editor: .. code:: xojo DoCommand("RunApp") As you use the IDE, any actions that have corresponding script commands will appear in the XojoScript Editor as they are recorded. All these recordable commands are listed on the DoCommand page. .. _/topics/build_automation/ide_scripting/introduction/assigning_a_shortcut_key: Assigning a shortcut key ------------------------ You can assign a keyboard shortcut to a script by placing a comment at the top of the script file with the shortcut defined. Example: ' KeyboardShortcut = CMD-OPT-SHIFT-Y .. note:: Function keys are not currently supported for IDE Script keyboard shortcuts. See the :ref:`DesktopMenuItem.ShortCut` property for valid keys. .. _/topics/build_automation/ide_scripting/introduction/example_scripts: Example scripts --------------- You can learn more about IDE scripting be experimenting with examples which you can download :download:`here`. Some of the included examples are: * AddCommentHeader * BuildAllDesktop * CountDebugRuns * CreateConstant * EncryptItems * Reload Project * SaveAll * SetShortVersion .. _/topics/build_automation/ide_scripting/introduction/see_also: .. seealso:: Refer to these topics for additional information on scripting: * :doc:`IDE Scripting Building Commands` * :doc:`IDE Scripting Constants` * :doc:`IDE Scripting DoCommand` * :doc:`IDE Scripting Input Output Commands` * :doc:`IDE Scripting Notification Commands` * :doc:`IDE Scripting Project Commands` * :doc:`IDE Scripting String Commands` * :doc:`XojoScript Runtime Scripting` * :doc:`Scripting Language` * :doc:`Scripting Functions` * :ref:`Scripting Errors` * :ref:`Scripting Warnings` * :doc:`IDE Communicator` ================= Building commands ================= ---- Description ----------- These commands are related to building projects. Properties ---------- .. rst-class:: table-centered_columns_3_and_4 .. csv-table:: :header: "Name", "Type", "Read-Only", "Shared" :widths: auto :ref:`BuildCurrentPlatform`, :doc:`Boolean`,, :ref:`BuildLanguage`, :doc:`String`,, :ref:`BuildLinux`, :doc:`Boolean`,, :ref:`BuildMac`, :doc:`Boolean`,, :ref:`BuildWebDebugPort`, :doc:`Integer`,, :ref:`BuildWebPort`, :doc:`Integer`,, :ref:`BuildWin32`, :doc:`Boolean`,, :ref:`CurrentBuildAppName`, :doc:`String`, ✓, :ref:`CurrentBuildLocation`, :doc:`String`, ✓, :ref:`CurrentBuildLocationNative`, :doc:`String`, ✓, :ref:`CurrentBuildTarget`, :doc:`Integer`, ✓, Methods ------- .. rst-class:: table-centered_column_4 .. csv-table:: :header: "Name", "Parameters", "Returns", "Shared" :widths: auto :ref:`BuildApp`, "buildType As :doc:`Integer`, reveal As :doc:`Boolean` = :doc:`False`", ":doc:`String`", "" :ref:`CancelBuild`, "", "", "" :ref:`PropertyValue`, "attribute As :doc:`String`", ":doc:`String`", "" "", "attribute As :doc:`String`, :doc:`Assigns` value As :doc:`String`", "", "" Property descriptions --------------------- .. _ide_scripting_building_commands.buildcurrentplatform: ---- .. rst-class:: forsearch BuildCurrentPlatform **BuildCurrentPlatform** As :doc:`Boolean` Gets or sets whether to build the project for the current OS platform. .. _ide_scripting_building_commands.buildlanguage: ---- .. rst-class:: forsearch BuildLanguage **BuildLanguage** As :doc:`String` Used to get or set the build language. This is the “Language” property on the Shared Build Settings. Change the language to English: .. code:: xojo If BuildLanguage = "Default" Then BuildLanguage = "English" End If .. _ide_scripting_building_commands.BuildLinux: ---- .. rst-class:: forsearch BuildLinux **BuildLinux** As :doc:`Boolean` Gets or sets whether to build the project for Linux. This is also set to true when "This Computer" is selected and Xojo is running on Linux. Create a Linux build: .. code:: xojo BuildLinux = True DoCommand("BuildApp") .. _ide_scripting_building_commands.buildmac: ---- .. rst-class:: forsearch BuildMac **BuildMac** As :doc:`Boolean` Gets or sets whether to build the project for macOS. This is also set to true when "This Computer" is selected and Xojo is running on macOS. Build for macOS, but not Windows: .. code:: xojo BuildMac = True BuildWin32 = False DoCommand("BuildApp") .. _ide_scripting_building_commands.buildwebdebugport: ---- .. rst-class:: forsearch BuildWebDebugPort **BuildWebDebugPort** As :doc:`Integer` Gets or sets the web port for running debug apps. Set the debug port: .. code:: xojo BuildWebDebugPort = 8100 .. _ide_scripting_building_commands.buildwebport: ---- .. rst-class:: forsearch BuildWebPort **BuildWebPort** As :doc:`Integer` Gets or sets the web port for built apps. Use a value of -1 to choose a port automatically. Set the port: .. code:: xojo BuildWebPort = 8080 .. _ide_scripting_building_commands.buildwin32: ---- .. rst-class:: forsearch BuildWin32 **BuildWin32** As :doc:`Boolean` Gets or sets whether to build the project for Windows. This is also set to :doc:`True` when "This Computer" is selected and Xojo is running on Windows. Build for Windows, but not macOS: .. code:: xojo BuildMac = False BuildWin32 = True DoCommand("BuildApp") .. _ide_scripting_building_commands.currentbuildappname: ---- .. rst-class:: forsearch CurrentBuildAppName **CurrentBuildAppName** As :doc:`String` Returns the name of the app that was built. This can only be used in a Build Automation IDE Script that runs after the Build Step. This just the app name, so spaces and other special characters are not escaped. This property is read-only. .. note:: For macOS apps, this includes the full appname and extension. Display the built app's name: .. code:: xojo Print(CurrentBuildAppName) .. _ide_scripting_building_commands.currentbuildlocation: ---- .. rst-class:: forsearch CurrentBuildLocation **CurrentBuildLocation** As :doc:`String` Returns the shell path to the app that was built. This can only be used in a Build Automation IDE Script that runs after the Build Step. Since this is a shell path, spaces and other special characters are properly escaped. This property is read-only. Display the built app's shell path: .. code:: xojo Print(CurrentBuildLocation) .. _ide_scripting_building_commands.currentbuildlocationnative: ---- .. rst-class:: forsearch CurrentBuildLocationNative **CurrentBuildLocationNative** As :doc:`String` Returns the native (unescaped) path to the app that was built. This can only be used in a Build Automation IDE Script that runs after the Build Step. This property is read-only. Display the built app's native path: .. code:: xojo Print(CurrentBuildLocationNative) .. _ide_scripting_building_commands.currentbuildtarget: ---- .. rst-class:: forsearch CurrentBuildTarget **CurrentBuildTarget** As :doc:`Integer` Returns an :doc:`Integer` that specifies the type of app that was built. This can only be used in a Build Automation IDE Script that runs after the Build Step. This property is read-only. The return value is one of these values: .. csv-table:: :header: "Value", "Build Target", "32/64-bit", "Architecture" :widths: auto 3, Windows, 32-bit, Intel 4, Linux, 32-bit, Intel 9, macOS Universal, 64-bit, Intel & ARM 10, iOS Simulator, 64-bit, ARM 12, Xojo Cloud, 64-bit, Intel 14, iOS Simulator, 64-bit, Intel 15, iOS Device, 64-bit, ARM 16, macOS (all), 64-bit, Intel 17, Linux, 64-bit, Intel 18, Linux, 32-bit, ARM 19, Windows, 64-bit, Intel 21, Android Emulator, 64-bit, Intel & ARM 23, Android Device, 64-bit, ARM 24, macOS, 64-bit, ARM 25, Windows, 64-bit, ARM 26, Linux, 64-bit, ARM Get the value for the target that was built: .. code:: xojo Var result As Integer = CurrentBuildTarget Method descriptions ------------------- .. _ide_scripting_building_commands.buildapp: ---- .. rst-class:: forsearch BuildApp **BuildApp**\(buildType As :doc:`Integer`, reveal As :doc:`Boolean` = :doc:`False`) As :doc:`String` Starts building the current project. The *buildType* can be one of these values: .. csv-table:: :header: "Value", "Build Target", "32/64-bit", "Architecture" :widths: auto 3, Windows, 32-bit, Intel 4, Linux, 32-bit, Intel 9, macOS Universal, 64-bit, Intel & ARM 10, iOS Simulator, 64-bit, ARM 12, Xojo Cloud, 64-bit, Intel 13, iOS Simulator, 64-bit, Intel 14, iOS Device, 64-bit, ARM 16, macOS (all), 64-bit, Intel 17, Linux, 64-bit, Intel 18, Linux, 32-bit, ARM 19, Windows, 64-bit, Intel 21, Android Emulator, 64-bit, Intel & ARM 23, Android Device, 64-bit, ARM 24, macOS, 64-bit, ARM 25, Windows, 64-bit, ARM 26, Linux, 64-bit, ARM If *reveal* is :doc:`True`, the built app is displayed using the OS file manager. Returns a :doc:`String` containing the Shell path of the built app. BuildApp cannot be used in an IDE Script that is called by a Build Automation Script. The code displays the location of a Mac build: .. code:: xojo Var appPath As String appPath = BuildApp(16) ' Mac 64-bit build Print("Built: " + appPath) .. _ide_scripting_building_commands.cancelbuild: ---- .. rst-class:: forsearch CancelBuild **CancelBuild** Cancels the current build in IDE Pre-Build Scripts. In a Post-Build script this is ignored. Cancel a build: .. code:: xojo CancelBuild .. _ide_scripting_building_commands.propertyvalue: ---- .. rst-class:: forsearch PropertyValue **PropertyValue**\(attribute As :doc:`String`) As :doc:`String` **PropertyValue**\(attribute As :doc:`String`, :doc:`Assigns` value As :doc:`String`) Allows you to set some attributes of the build target. .. csv-table:: :header: "Names", "Values" :widths: auto "App.MacArchitecture, App.WindowsArchitecture, App.LinuxArchitecture", "x32, x64, ARM32, ARM64" "macOSMinimumVersion", "A version string - for example: 13.0.1" If the property doesn't support the *value* passed, the build target will be unchanged. This example sets the macOS architecture to x64: .. code:: xojo PropertyValue("App.MacArchitecture") = "x64" ========= Constants ========= ---- Description ----------- These constants can be used in Build Step IDE Scripts. .. rst-class:: table-centered_columns_3_and_4 .. csv-table:: :header: "Name", "Type" :widths: auto :ref:`AppSupportsDarkMode`, :doc:`Boolean` :ref:`AppSupportsHiDPI`, :doc:`Boolean` :ref:`CurrentBuildTargetIs32Bit`, :doc:`Boolean` :ref:`CurrentBuildTargetIs64Bit`, :doc:`Boolean` :ref:`CurrentBuildTargetIsAndroid`, :doc:`Boolean` :ref:`CurrentBuildTargetIsARM`, :doc:`Boolean` :ref:`CurrentBuildTargetIsConsole`, :doc:`Boolean` :ref:`CurrentBuildTargetIsDesktop`, :doc:`Boolean` :ref:`CurrentBuildTargetIsIntel`, :doc:`Boolean` :ref:`CurrentBuildTargetIsIOS`, :doc:`Boolean` :ref:`CurrentBuildTargetIsLinux`, :doc:`Boolean` :ref:`CurrentBuildTargetIsMacOS`, :doc:`Boolean` :ref:`CurrentBuildTargetIsUniversal`, :doc:`Boolean` :ref:`CurrentBuildTargetIsWeb`, :doc:`Boolean` :ref:`CurrentBuildTargetIsWindows`, :doc:`Boolean` :ref:`CurrentBuildTargetIsXojoCloud`, :doc:`Boolean` :ref:`CurrentScriptName`, :doc:`String` :ref:`DebugBuild`, :doc:`Boolean` :ref:`PreBuild`, :doc:`Boolean` :ref:`TargetLinux`, :doc:`Boolean` :ref:`TargetMacOS`, :doc:`Boolean` :ref:`TargetWindows`, :doc:`Boolean` Constant descriptions --------------------- .. _ide_scripting_constants.appsupportsdarkmode: ---- **AppSupportsDarkMode** As :doc:`Boolean` Returns :doc:`True` if the app is built with Supports Dark Mode property set to ON. .. _ide_scripting_constants.appsupportshidpi: ---- **AppSupportsHiDPI** As :doc:`Boolean` Returns :doc:`True` if the app is built with Supports HiDPI property set to ON. .. _ide_scripting_constants.currentbuildtargetis32bit: ---- **CurrentBuildTargetIs32Bit** As :doc:`Boolean` Returns :doc:`True` when doing a 32-bit build. .. _ide_scripting_constants.currentbuildtargetis64bit: ---- **CurrentBuildTargetIs64Bit** As :doc:`Boolean` Returns :doc:`True` when doing a 64-bit build. .. _ide_scripting_constants.currentbuildtargetisandroid: ---- **CurrentBuildTargetIsAndroid** As :doc:`Boolean` Returns :doc:`True` when doing a building an Android app. .. _ide_scripting_constants.currentbuildtargetisarm: ---- **CurrentBuildTargetIsARM** As :doc:`Boolean` Returns :doc:`True` when doing a build for a the ARM CPU architecture. .. _ide_scripting_constants.currentbuildtargetisconsole: ---- **CurrentBuildTargetIsConsole** As :doc:`Boolean` Returns :doc:`True` when building a Console app. .. _ide_scripting_constants.currentbuildtargetisdesktop: ---- **CurrentBuildTargetIsDesktop** As :doc:`Boolean` Returns :doc:`True` when building a Desktop app. .. _ide_scripting_constants.currentbuildtargetisintel: ---- **CurrentBuildTargetIsIntel** As :doc:`Boolean` Returns :doc:`True` when doing a build for the Intel CPU architecture (x86 or x86-64). .. _ide_scripting_constants.currentbuildtargetisios: ---- **CurrentBuildTargetIsIOS** As :doc:`Boolean` Returns :doc:`True` when building an iOS app. .. _ide_scripting_constants.currentbuildtargetislinux: ---- **CurrentBuildTargetIsLinux** As :doc:`Boolean` Returns :doc:`True` when doingn a Linux build. .. _ide_scripting_constants.currentbuildtargetismacos: ---- **CurrentBuildTargetIsMacOS** As :doc:`Boolean` Returns :doc:`True` when doing a macOS build. .. _ide_scripting_constants.currentbuildtargetisuniversal: ---- **CurrentBuildTargetIsUniversal** As :doc:`Boolean` Returns :doc:`True` when doing a macOS Universal build. .. _ide_scripting_constants.currentbuildtargetisweb: ---- **CurrentBuildTargetIsWeb** As :doc:`Boolean` Returns :doc:`True` when building a Web app. .. _ide_scripting_constants.currentbuildtargetiswindows: ---- **CurrentBuildTargetIsWindows** As :doc:`Boolean` Returns :doc:`True` when doing a Windows build. .. _ide_scripting_constants.currentbuildtargetisxojocloud: ---- **CurrentBuildTargetIsXojoCloud** As :doc:`Boolean` Returns :doc:`True` when doing a Xojo Cloud build. .. _ide_scripting_constants.currentscriptname: ---- **CurrentScriptName** As :doc:`String` Returns the name of the script that is currently running. .. _ide_scripting_constants.debugbuild: ---- **DebugBuild** As :doc:`Boolean` Indicates if the app is being run in debug mode (:doc:`True`) or as a Build (:doc:`False`). Update an App constant to indicate the number of debug runs: .. code:: xojo If DebugBuild Then If SelectProjectItem("App") Then ' Constant kDebugRuns must exist on App Var value As Integer = Val(ConstantValue("kDebugRuns")) + 1 ConstantValue("kDebugRuns") = value.ToString End If End If .. _ide_scripting_constants.prebuild: ---- **PreBuild** As :doc:`Boolean` Returns :doc:`True` if the script is running before the Build step. .. _ide_scripting_constants.targetlinux: ---- **TargetLinux** As :doc:`Boolean` Indicates if the Xojo IDE is running on Linux. .. _ide_scripting_constants.targetmacos: ---- **TargetMacOS** As :doc:`Boolean` Indicates if the Xojo IDE is running on a Mac. If the project is being built on a Mac, then code sign it: .. code:: xojo If TargetMacOS Then Var command As String command = "codesign -f --deep -s ""Developer ID Application: YourName "" ""YourXojoApp.app """ Var result As String result = DoShellCommand(command) End If .. _ide_scripting_constants.targetwindows: ---- **TargetWindows** As :doc:`Boolean` Indicates if the Xojo IDE is running on Windows. If the project is being built on Windows, start Inno Setup after the build is finished: .. code:: xojo If TargetWindows Then Var innoSetupPath As String innoSetupPath = "C:\\Program Files (x86)\Inno Setup 5/Compil32.exe" Var result As String Var resultCode As Integer result = DoShellCommand(innoSetupPath, 20000, resultCode) End If Method ========= DoCommand ========= ---- Description ----------- These commands are used to run other scripts or to execute commands that perform an IDE action. These commands are identified by recording a script while using the IDE. .. _/topics/build_automation/ide_scripting/docommand/docommand(cmdname_as_string): ---- .. rst-class:: forsearch DoCommand **DoCommand**\(cmdName As :doc:`String`) Executes the specified IDE command. Refer to Commands used by DoCommand for a list of available commands. Save the project: .. code:: xojo DoCommand("SaveFile") .. _/topics/build_automation/ide_scripting/docommand/commands_used_by_docommand: Commands used by DoCommand -------------------------- The following commands may be used as parameters to the DoCommand method. .. _/topics/build_automation/ide_scripting/docommand/project_navigation_and_management: Project navigation and management ********************************* * OpenFile: Displays the Open Project File dialog. * SaveFile: Saves the current project with no prompt. * SaveFileAs: Displays the File Save As dialog. * Import: Displays the import file dialog. * CloseWindow: Closes the current workspace window. * RunApp: Runs the current project in debug mode. * RunPaused: Runs the current project, but does not launch the debug app. * Resume: Causes a paused app to continue running. * Pause: Pauses the running app. * Kill: Quits the running app. * StepOver: Steps over the current line of code. * StepInto: Steps into to the method on the current line of code. * StepOut: Steps out of the current method to the line of code that executed it. * BuildApp: Builds the current project. * Print: Displays the print dialog. * PageSetup: Displays the page setup dialog. * GoBack: Go backward in tab history. * GoForward: Go forward in tab history. * Help: Shows the Documentation window. * Library: Toggles the display of the Library. * Inspector: Toggles the display of the Inspector. * Find: Toggles display of Find pane. * ToggleErrors: Toggles the display of the Errors pane. * ToggleMessages: Toggles the display of the Messages pane. * NewTab: Adds a new tab to the current workspace window. .. _/topics/build_automation/ide_scripting/docommand/project_items: Project items ************* * NewClass: Add a new class to the current project. * CopyFilesStep: Adds a new Copy Files Step to the current project. * RunIDEScriptStep: Adds a new Script Step to the current project. * NewRunExternalScriptStep: Displays file selector dialog to add a new External Script Step to the current project. * NewInterface: Adds a new interface to the current project. * NewContainerControl: Adds a new container control to the current project. * NewFileTypes: Adds a new file type set to the current project. * NewFolder: Adds a new folder to the current project. * NewLayout: Adds a new iOS layout to the current project. * NewMenuBar: Adds a new menu bar to the current project. * NewModule: Adds a new module to the current project. * NewReport: Adds a new report to the current project. * NewScreen: Adds a new MobileScreen to the current project. * NewToolbar: Adds a new toolbar to the current project. * NewWindow: Adds a new window to the current project. * NewWorker: Adds a new Worker class to the current project. * AddWebPage: Adds a new web page to the current project. * AddWebDialog: Adds a new web dialog to the current project. * AddWebStyle: Adds a new web style to the current project. .. _/topics/build_automation/ide_scripting/docommand/project_items_editing: Project items editing ********************* * AddEventImplementation: Displays the Add Event Handler dialog. * NewMethod: Adds a new method to the selected project item. * NewProperty: Adds a new property to the selected project item. * NewNote: Adds a new note to the selected project item. * NewMenuHandler: Adds a new menu handler to the selected project item. * NewComputedProperty: Adds a new computed property to the selected project item. * NewConstant: Adds a new constant to the selected project item. * NewDelegate: Adds a new delegate to the selected project item. * NewEnum: Adds a new enumeration to the selected project item. * NewEvent: Adds a new event definition to the selected project item. * NewExternalMethod: Adds a new external method to the selected project item. * NewSharedComputedProperty: Adds a new shared computed property to the selected project item. * NewSharedMethod: Adds a new shared method to the selected project item. * NewSharedProperty: Adds a new shared property to the selected project item. * NewStructure: Adds a new structure to the selected project item. .. _/topics/build_automation/ide_scripting/docommand/editing: Editing ******* * Comment: Add the comment prefix to the selected text in the code editor (or the current line if no text is selected). * CheckItemErrors: Equivalent to Project > Analyze Item. * CheckProjectErrors: Equivalent to Project > Analyze Project. * SelectAll: Selects all the controls for the selected project item. Does not work with the Code Editor. Use SelStart and SelLength instead. * Copy: Copies the selected item in the Navigator to the clipboard. * Paste: Pastes the text in the clipboard to the active code editor. * Cut: Cuts the selected control(s) for the selected project item. Does not work with the Code Editor. * Undo: Restores the last deleted control(s). Does not work with the Code Editor. * DeleteSelection: Not implemented. .. _/topics/build_automation/ide_scripting/docommand/layout_editor: Layout Editor ************* * AddFromLibrary: Not implemented. * EditModeCode: Switch to Code Editor. * GoToLastEvent: Go to last edited code. * EditModeView: Switch to Layout Editor. * StartInlineEditing: Open Default Property popout window. * LockPositions: Toggle lock for selected control. * ShowHideTabOrder: Displays Tab Order Editor dialog. * ToggleMeasurements: Toggle measurements. * OrderForward: Order selected controls forward. * OrderToFront: Bring selected controls to front. * OrderBackward: Order select controls backward. * OrderToBack: Send selected controls to the back. * FillWidth: Fill width on the selected control. * FillHeight: Fill height on the selected control. * AlignLeft: Aligns the selected controls to the left. * AlignRight: Aligns the selected controls to the right. * AlignTop: Aligns the selected controls to the top. * AlignBottom: Aligns the selected controls to the bottom. * AlignSpaceHorizontally: Align and space the selected controls horizontally. * AlignSpaceVertically: Align and space the selected controls vertically. .. _/topics/build_automation/ide_scripting/docommand/menu_editor: Menu Editor *********** * AddMenu: Adds a top-level menu to the menu bar. * AddMenuItem: Adds a menu item to the selected menu. * AddMenuSeparator: Adds a separator to the selected menu. * AddSubmenu: Adds a submenu to the selected menu. * ConvertToMenu: Converts a submenu item to a top-level menu. * ViewAsWin32: Changes to the Windows menu view. * ViewAsOSX: Changes to the macOS menu view. * ViewAsLinux: Changes to the Linux menu view. .. _/topics/build_automation/ide_scripting/docommand/file_types_set_editor: File Types Set Editor ********************* * NewFileType: Adds a new file type to the editor. * Clear: Removes the selected file type from the editor. * AddCommonFileType$Pdf: Adds PDF file type. * AddCommonFileType$Rtf: Adds RTF file type. * AddCommonFileType$Mp3: Adds MP3 file type. * AddCommonFileType$Jpeg: Adds Jpeg file type. * AddCommonFileType$Png: Adds PNG file type. * AddCommonFileType$Any: Adds “any” file type. * AddCommonFileType$Text: Adds text file type. * AddCommonFileType$Mpeg: Adds Mpeg file type. * AddCommonFileType$Quicktime: Adds QuickTime file type. * AddCommonFileType$More: Adds “more” file type. .. _/topics/build_automation/ide_scripting/docommand/report_layout_editor: Report Layout Editor ******************** * AddPageSection: Add page header/footer section to report. * AddGroupSection: Add group header/footer section to report. .. _/topics/build_automation/ide_scripting/docommand/copy_file_steps_editor: Copy File Steps Editor ********************** * AddFileToCopyFilesStep: Displays file dialog to select a file to add to the editor. * RemoveFileFromCopyStep: Removes the selected file from the editor. ===================== Input output commands ===================== ---- Methods ------- .. rst-class:: table-centered_column_4 .. csv-table:: :header: "Name", "Parameters", "Returns", "Shared" :widths: auto :ref:`DoShellCommand`, "command As :doc:`String`, timeout As :doc:`Integer` = 3000, :doc:`ByRef` resultCode As :doc:`Integer`", ":doc:`String`", "" :ref:`EnvironmentVariable`, "variable As :doc:`String`", ":doc:`String`", "" "", "variable As :doc:`String`, :doc:`Assigns` value As :doc:`String`", "", "" :ref:`ExportLocalizableValues`, "lang As :doc:`String`, savePath As :doc:`String`", ":doc:`Boolean`", "" :ref:`LoadText`, "nativePath As :doc:`String`", ":doc:`String`", "" :ref:`OpenFile`, "filePath As :doc:`String`", "", "" :ref:`RunScript`, "scriptName As :doc:`String`", "", "" :ref:`SaveText`, "nativePath As :doc:`String`, aString As :doc:`String`", ":doc:`Boolean`", "" Method descriptions ------------------- .. _ide_scripting_input_output_commands.doshellcommand: ---- **DoShellCommand**\(command As :doc:`String`, timeout As :doc:`Integer` = 3000, :doc:`ByRef` resultCode As :doc:`Integer`) As :doc:`String` Runs a shell *command* or shell script. The Shell environment is configured with these variables: * IDE_PATH: Path to the folder containing the IDE * PROJECT_PATH: Path to the folder containing the current project * PROJECT_FILE: Path to the actual project file The *command* is run synchronously and returns the output as the result. The *timeout* is in milliseconds. Run the macOS code sign shell command: .. code:: xojo Var command As String command = "codesign -f --deep -s ""Developer ID Application: YourName"" ""YourXojoApp.app""" Var result As String result = DoShellCommand(command) .. _ide_scripting_input_output_commands.environmentvariable: ---- **EnvironmentVariable**\(variable As :doc:`String`) As :doc:`String` Gets the value of the environment *variable* passed. On macOS, displays the HOME environment variable: .. code:: xojo Print(EnvironmentVariable("HOME")) **EnvironmentVariable**\(variable As :doc:`String`, :doc:`Assigns` value As :doc:`String`) Sets the *value* of an environment *variable*. .. _ide_scripting_input_output_commands.exportlocalizablevalues: ---- **ExportLocalizableValues**\(lang As :doc:`String`, savePath As :doc:`String`) As :doc:`Boolean` Exports the specified language *lang* as a locale file to the specified location at *savePath*. The *lang* names can be a name in the languages that are as presented in the popup menus in the IDE for exporting. The *savePath* to the destination is a native path and so is platform specific. The destination file will be created or overwritten if it already exists. The save can fail if the path to the destination does not exist or the language is not in the list of known languages the IDE supports. .. code:: xojo If ExportLocalizableValues("english", "/Users/username/Desktop/english.xojo_locale") Then Print "success" Else Print "fail" End If .. _ide_scripting_input_output_commands.loadtext: ---- **LoadText**\(nativePath As :doc:`String`) As :doc:`String` Loads the contents of the file at *nativePath* into a String. .. code:: xojo Var data As String = LoadText("C:\\Test.txt") .. _ide_scripting_input_output_commands.openfile: ---- **OpenFile**\(filePath As :doc:`String`) Attempts to open a project file using the specified path, which can be either a native or shell path. Open the file at the path: .. code:: xojo OpenFile("c:\\projects\\IDEScriptTest.xojo_binary_project") .. _ide_scripting_input_output_commands.runscript: ---- **RunScript**\(scriptName As :doc:`String`) Runs the passed script, found in the Scripts folder, either next to the IDE or next to the frontmost project file. Run "CommentScript": .. code:: xojo RunScript("CommentScript") .. _ide_scripting_input_output_commands.savetext: ---- **SaveText**\(nativePath As :doc:`String`, aString As :doc:`String`) As :doc:`Boolean` Writes the contents of *aString* to the file at *nativePath*. .. code:: xojo Call SaveText("C:\\Test.txt", "Hello, World!") ===================== Notification commands ===================== ---- Description ----------- The Notification commands are used to get the user's attention by displaying a message or making a sound. Methods ------- .. rst-class:: table-centered_column_4 .. csv-table:: :header: "Name", "Parameters", "Returns", "Shared" :widths: auto :ref:`Beep`, "", "", "" :ref:`DebugLog`, "message As :doc:`String`", "", "" :ref:`Print`, "text As :doc:`String`", "", "" :ref:`ShowDialog`, "message As :doc:`String`, explanation As :doc:`String`, DefaultButtonCaption As :doc:`String`, CancelButtonCaption As :doc:`String`, AltButtonCaption As :doc:`String`, icon As :doc:`Integer`", ":doc:`String`", "" :ref:`ShowURL`, "url As :doc:`String`", "", "" :ref:`Speak`, "text As :doc:`String`", "", "" Method descriptions ------------------- .. _ide_scripting_notification_commands.beep: ---- **Beep** The Beep command plays the system beep sound. .. code:: xojo Beep .. _ide_scripting_notification_commands.debuglog: ---- **DebugLog**\(message As :doc:`String`) Displays the specified *message* in the Messages pane. .. _ide_scripting_notification_commands.print: ---- **Print**\(text As :doc:`String`) Displays the specified *text* in a simple dialog box with an OK button. Display "hello": .. code:: xojo Print("Hello") .. _ide_scripting_notification_commands.showdialog: ---- **ShowDialog**\(message As :doc:`String`, explanation As :doc:`String`, DefaultButtonCaption As :doc:`String`, CancelButtonCaption As :doc:`String`, AltButtonCaption As :doc:`String`, icon As :doc:`Integer`) As :doc:`String` The ShowDialog command displays a dialog box on the screen. You can specify the *message* and determine the buttons and their text. Returns a String containing the caption of the button that was pressed. .. Note:: If you pass in an empty string to *CancelButtonCaption* or *AltButtonCaption*, then the button does not appear. Values for Icon are: .. csv-table:: :header: "Value", "Description" :widths: auto "-1", "No icon" "0", "Note icon (App Icon on Mac)" "1", "Caution icon (On Mac, App Icon is superimposed)" "2", "Stop Icon (App Icon on Mac)" "3", "Question Icon (App Icon on Mac)" Display a simple dialog: .. code:: xojo Var result As String result = ShowDialog("Hello!", _ "Is it me you're looking for?", _ "Yes", "No", "", 1) .. _ide_scripting_notification_commands.showurl: ---- **ShowURL**\(url As :doc:`String`) Displays the *url* in the default web browser. .. code:: xojo ShowURL("http://www.xojo.com") .. _ide_scripting_notification_commands.speak: ---- **Speak**\(text As :doc:`String`) The Speak command speaks the supplied *text*. Say "hello": .. code:: xojo Speak("Hello") ================ Project commands ================ ---- Description ----------- These commands are related to working with the IDE workspace windows, creating new projects and to directly modify or get information about project items or Xojo. .. _/topics/build_automation/ide_scripting/project_commands/changedeclaration: ChangeDeclaration(name As String, parameters As String, returntype As String, scope As Integer, implements As String) --------------------------------------------------------------------------------------------------------------------- Changes the declaration of the current property or method. The value for scope may be one of the following: * 0 = Public * 1 = Protected * 2 = Private Generally this is used to rename a property or method after it has been added to a project item. In the case of a property, the parameters value is used for the default value of the property. Sample code *********** Add a new property to Window1 and set its values: .. code:: xojo If SelectProjectItem("Window1") Then DoCommand("NewProperty") ChangeDeclaration("UserName", "Bob Roberts", "String", 2, "") End If .. _/topics/build_automation/ide_scripting/project_commands/closeproject: CloseProject(prompt As Boolean = True) -------------------------------------- Close all Workspace windows associated with the project that is open in the front most Workspace window. If there are unsaved changes when you call this with prompt is True you will get a dialog asking if you want to save changes. Call it with prompt = False to close all projects without asking to save changes. Sample code *********** .. code:: xojo CloseProject(False) .. _/topics/build_automation/ide_scripting/project_commands/constantvalue: ConstantValue(name As String) As String --------------------------------------- Gets or sets the value of a project item constant. Sample code *********** Change the value of an existing constant on App: .. code:: xojo If SelectProjectItem("App") Then ' kBeta must already exist ConstantValue("kBeta") = "True" End If You can also refer to the full constant name like this: .. code:: xojo ' kBeta must already exist ConstantValue("App.kBeta") = "True" .. _/topics/build_automation/ide_scripting/project_commands/decryptitem: DecryptItem(password As String) As Boolean ------------------------------------------ Decrypts the selected project item using the specified password. Returns :doc:`True` if it succeeded. Sample code *********** Decrypt the project items in the array: .. code:: xojo Var items() As String items = Array("Class1", "Class2") Var name As String For Each name In items If SelectProjectItem(name) Then If Not DecryptItem("pa55w0rd") Then Print("Error encrypting " + name) Exit For End If End If Next .. _/topics/build_automation/ide_scripting/project_commands/dirtyallprojectitems: DirtyAllProjectItems -------------------- Marks all items in a project, including external items, as needing to be saved. .. _/topics/build_automation/ide_scripting/project_commands/encryptitem: EncryptItem(password As String) As Boolean ------------------------------------------ Encrypts the selected project item using the specified password. Returns :doc:`True` if it succeeded. Sample code *********** Encrypt the project items in the array: .. code:: xojo Var items() As String items = Array("Class1", "Class2") Var name As String For Each name In items If SelectProjectItem(name) Then If Not EncryptItem("pa55w0rd") Then Print("Error encrypting " + name) Exit For End If End If Next .. _/topics/build_automation/ide_scripting/project_commands/itemattribute: ItemAttribute(name As String) As String --------------------------------------- Returns the value of the attribute specified by the name parameter on the currently selected item. Returns an empty string if the attribute does not exist. String values will have their quotation marks stripped off. ItemAttribute(name As String, Assigns value As String) --------------------------------------- Sets the value of the attribute specified by the name parameter on the currently selected item. For string values, you must include the quotation marks. .. _/topics/build_automation/ide_scripting/project_commands/itemdescription: ItemDescription As String ------------------------- Used to get or set the description of a events, methods, properties, etc. .. _/topics/build_automation/ide_scripting/project_commands/itemhasattribute: ItemHasAttribute(name As String) As Boolean ------------------------------------------- Returns :doc:`True` if the currently selected item has an attribute with the specified name. .. _/topics/build_automation/ide_scripting/project_commands/itemremoveattribute: ItemRemoveAttribute(name As String) ----------------------------------- Removes all attributes with the specified name on the currently selected item. .. _/topics/build_automation/ide_scripting/project_commands/location: Location As String ------------------ Used to get or set a location in the project. This can be a project item or a property, method, event (etc.) or a project item. Separate each item with a period. When you set a location, the appropriate project item is selected in the Navigator. Sample code *********** Get the selected project item: .. code:: xojo Var loc As String = Location Select "Window1" in the Navigator: .. code:: xojo Location = "Window1" To determine if a specific control is selected, add "Controls" to the object and follow it with the control name. For example, this checks if a button is selected on Window1: .. code:: xojo If Location = "Window1.Controls.Button1" Then Print("Button1 is Selected!") End If .. _/topics/build_automation/ide_scripting/project_commands/newandroidproject: NewAndroidProject ----------------- Creates a new Android project and opens a new workspace window. .. _/topics/build_automation/ide_scripting/project_commands/newconsoleproject: NewConsoleProject ----------------- Creates a new console project and opens a new workspace window. Sample code *********** Create a new console project: .. code:: xojo NewConsoleProject .. _/topics/build_automation/ide_scripting/project_commands/newguiproject: NewGUIProject ------------- Creates a new desktop project and opens a new workspace window. Sample code *********** Create a new desktop project: .. code:: xojo NewGUIProject .. _/topics/build_automation/ide_scripting/project_commands/newiosproject: NewiOSProject ------------- Creates a new iOS project and opens a new workspace window. Sample code *********** Create a new iOS project: .. code:: xojo NewiOSProject .. _/topics/build_automation/ide_scripting/project_commands/newwebproject: NewWebProject ------------- Creates a new web project and opens a new workspace window. Sample code *********** Create a new web project: .. code:: xojo NewWebProject .. _/topics/build_automation/ide_scripting/project_commands/projectitem: ProjectItem As String --------------------- Returns the name of the project item that is selected in the Navigator (of the current tab, if more than one tab is open). Sample code *********** Display the name of the selected project item: .. code:: xojo Print(ProjectItem) .. _/topics/build_automation/ide_scripting/project_commands/projectshellpath: ProjectShellPath As String -------------------------- Returns the shell path for the project currently being edited. If the project has not yet been saved, this returns the empty string. Sample code *********** Displays the shell path for the current project: .. code:: xojo Print(ProjectShellPath) .. _/topics/build_automation/ide_scripting/project_commands/propertyvalue: PropertyValue(propName As String) As String ------------------------------------------- PropertyValue(propName As String, Assigns value As String) ---------------------------------------------------------- Gets or sets the value of a project item property. This only works for properties of project items that are part of the Xojo framework (such as Window or App). You can only modify properties that are part of the framework (such as Window1.Title), not properties that you have added. You can specify just the property name which will use the currently selected project item. Or you can specify a project item, followed by the property name using dot notation. When referring to the App object, always use the name “App” even if you have renamed it in your project. The property value is always returned or set as a string, even if the property itself is not a string. For Boolean values use the string values "True" or "False". .. _/topics/build_automation/ide_scripting/project_commands/properties_of_app_that_can_be_changed: Properties of App that can be changed ************************************* Be sure to use the "App." prefix in front of any of these properties. * DefaultWindow * DefaultWebPage * DefaultPhoneScreen * DefaultTabletScreen * MenuBar * MajorVersion * MinorVersion * BugVersion * StageCode * NonReleaseVersion * AutoIncrementVersionInformation * Version * Copyright * Description * WindowsAppName * MDI * MDICaption * CompanyName * ProductName * InternalName * FileDescription * LinuxAppName * MacOSXAppName * iOSAppName * AndroidAppName * AcceptFileTypes * Application Identifier (the space is necessary here) * IncludeFunctionNames * SupportsHiDPI * SupportsDarkMode * Port * LaunchMessage * HTMLHeader * DisconnectMessage * Identifier * CopyRedistNextToWindowsEXE * MacCreator * OptimizationLevel * ' 0 = Default * ' 4 = Aggressive * ' 6 = Moderate .. code:: xojo PropertyValue("App.OptimizationLevel") = "4" * WindowsRunAs (0= User, 1 = Highest Available, 2 = Administrator) * CopyRedistNextToWindowsEXE * SupportsWindows10 * SupportsWindows8 * SupportsWindows8.1 * SupportsWindows7 Sample code *********** Set the App.Version property to match the version numbers: .. code:: xojo Var version As String version = PropertyValue("App.MajorVersion") + "." + _ PropertyValue("App.MinorVersion") + "." + _ PropertyValue("App.BugVersion") + " (" + _ PropertyValue("App.NonReleaseVersion") + ")" PropertyValue("App.Version") = version To set a Boolean value: .. code:: xojo PropertyValue("App.SupportsDarkMode") = "True" .. _/topics/build_automation/ide_scripting/project_commands/quitide: QuitIDE(saveChanges As Boolean) ------------------------------- Quits the IDE, either ignoring any unsaved changes or saving changes without prompting. Sample code *********** Quit and save changes: .. code:: xojo QuitIDE(True) .. _/topics/build_automation/ide_scripting/project_commands/selectwindow: SelectWindow(windowTitle As String) ----------------------------------- SelectWindow(index As Integer) ------------------------------ Selects the IDE workspace window with the specified title or index (0-based), bringing it to the front. The title is the project filename without the extension. Sample code *********** Bring the first workspace window to the front: .. code:: xojo SelectWindow(0) Bring the workspace named "Test" to the front: .. code:: xojo SelectWindow("Test") .. _/topics/build_automation/ide_scripting/project_commands/selectprojectitem: SelectProjectItem(itemPath As String) As Boolean ------------------------------------------------ Selects the project item specified by the path. Returns :doc:`True` if the item was selected, False if not (usually because it does not exist). Specify the path using dot notation, with each level separated by a period. To select a method, property or other item within a project item, use the Location method. Sample code *********** Select Method1 on Window1: .. code:: xojo Var selected As Boolean = SelectProjectItem("Window1.Method1") Select Class1 that is within Module1: .. code:: xojo Var selected As Boolean = SelectProjectItem("Module1.Class1") .. _/topics/build_automation/ide_scripting/project_commands/sublocations: SubLocations(baseLocation As String) As String ---------------------------------------------- Returns all the locations within the given base location (a module or folder) as a tab-delimited String (ChrB(9)). If baseLocation is an empty string, then all top-level project items are returned. The *baseLocation* must be a module or folder name. Sample code *********** Display the name of each item in a folder: .. code:: xojo Var itemList As String itemList = Sublocations("Folder1") Var items() As String items = itemList.ToArray(ChrB(9)) Var name As String For Each name In items Print("Item: " + name) Next .. _/topics/build_automation/ide_scripting/project_commands/text: Text As String -------------- Gets or sets the entire text of what is currently displayed in the current Code Editor. Sample code *********** Add a comment header to the top of the currently displayed method: .. code:: xojo Var header As String header = "// Copyright 2018 Acme, Inc." Text = header + EndOfLine + EndOfLine + Text .. _/topics/build_automation/ide_scripting/project_commands/typeofcurrentlocation: TypeOfCurrentLocation As String ------------------------------- Returns a string that is the type of the item at the current location. This can either be a project item or a code item. The following types may be returned: .. csv-table:: :header: "Name" :widths: auto "Attribute" "Attribute List" "Class" "Computed Property" "Constant" "Cursor" "Database" "Debugger" "Delegate" "Enumeration" "Event Declaration" "Event Implementation" "External Method" "External Objective-C Method" "FileType" "Folder" "Interface" "Menu" "Menu Handler" "Method" "Module" "Movie" "Note" "Picture" "Profiler" "Property" "Schema" "Structure" "Toolbar" "Web Form" "Window" Sample code *********** Show the type of the item at the current location: .. code:: xojo If SelectProjectItem("Window1.Method1") Then Print(TypeOfCurrentLocation) ' displays Method End If Displays the type (Super) of Window1: .. code:: xojo SelectWindow("Window1") Print TypeOfCurrentLocation .. _/topics/build_automation/ide_scripting/project_commands/windowcount: WindowCount As Integer ---------------------- Returns the number of open workspace windows in the IDE. Sample code *********** Get the number of workspace windows: .. code:: xojo Var count As Integer = WindowCount .. _/topics/build_automation/ide_scripting/project_commands/windowtitle: WindowTitle(index As Integer) As String --------------------------------------- Returns the name of a workspace window using its index (0-based). Sample code *********** Loop through all open workspaces and saves their contents: .. code:: xojo Var saved As String Var comma As String Var i As Integer For i = 0 To WindowCount - 1 SelectWindow(i) saved = saved + comma + WindowTitle(i) DoCommand("SaveFile") comma = ", " Next Print("Saved: " + saved) .. _/topics/build_automation/ide_scripting/project_commands/xojoversion: XojoVersion As Double --------------------- Returns the version of Xojo as a Double. For example, Xojo 2019 Release 2 would return 2019.02. Note that “#if” requires a constant value and XojoVersion is a method on the Script context so cannot be used with #if. But in an IDE script or build script, using “if XojoVersion” to cause code to be skipped is the same as using “#if XojoVersion” to cause the code to be skipped. Sample code *********** Display the version: .. code:: xojo Print("XojoVersion: " + Format(XojoVersion, "0000.00#")) ============= Text commands ============= ---- Description ----------- These are general String-related commands that are useful when used with the other IDE Scripting commands. Properties ---------- .. rst-class:: table-centered_columns_3_and_4 .. csv-table:: :header: "Name", "Type", "Read-Only", "Shared" :widths: auto :ref:`Clipboard`, :doc:`String`,, :ref:`EndOfLine`, :doc:`String`, ✓, :ref:`SelectionLength`, :doc:`Integer`,, :ref:`SelectionStart`, :doc:`Integer`,, :ref:`SelectedText`, :doc:`String`,, Property descriptions --------------------- .. _ide_scripting_text_commands.clipboard: ---- **Clipboard** As :doc:`String` Used to get or set the text contents of the OS clipboard. Create a new Note for the selected project item and pastes in the contents of the Clipboard into the Note: .. code:: xojo DoCommand("NewNote") Text = Clipboard .. _ide_scripting_text_commands.endofline: ---- **EndOfLine** As :doc:`String` Returns the default line ending for the OS running the IDE. This is also the line separator used for code editor text that is returned by Text and SelectedText. This property is read-only. Add text to a new note: .. code:: xojo DoCommand("NewNote") Text = "Line1" + EndOfLine + "Line2" .. _ide_scripting_text_commands.selectionlength: ---- **SelectionLength** As :doc:`Integer` Gets or sets the length of characters in the current selection of the code editor. .. _ide_scripting_text_commands.selectionstart: ---- **SelectionStart** As :doc:`Integer` Gets or sets the offset of the selection (or insertion) point in the code editor. .. _ide_scripting_text_commands.selectedtext: ---- **SelectedText** As :doc:`String` Gets or sets the selected text in the code editor. After assign a string to SelectedText, the selection will be empty and positioned immediately after the inserted text. Take the selected text and add an inline constant containing the text to the top of the method and replaces the selected text with the name of the constant: .. code:: xojo Var constantText As String constantText = SelectedText Var newConstant As String newConstant = "Const kValue = """ + constantText + """" SelectionStart = SelectionStart - 1 SelectionLength = SelectionLength + 2 SelectedText = "kValue" Text = newConstant + EndOfLine + EndOfLine + Text ================ IDE Communicator ================ Although Xojo itself cannot be run as a command line app, it can be controlled using a separate command-line app that communicates with Xojo using IPC sockets and the "XojoIDE" path. There are two versions of the communication protocol. The original v1 protocol lets you send IDE Scripts directly to Xojo, but you do not get any reponse back from Xojo. The newer v2 protocol lets you send IDE Scripts directly to Xojo (embedded in JSON) and returns responses (from the Print command) and script errors to you. .. _/topics/build_automation/ide_communicator/version_1: Version 1 --------- The IDECommunicator example project (Examples/IDE/Build Automation/IDE Scripting/IDECommunicator/v1) contains a console project that can be used communicate with Xojo from the command line by sending IDE Scripts to Xojo for it to run. Xojo must already be running in order for the communication to work. To use it, build the app for your platform and then run it from the command line with the “-h” option to see how it works. This command runs it on Mac and Linux (# indicates your shell/terminal prompt): .. code:: xojo # ./IDECommunicator -h Most often you will send it an IDE Script file. This runs the provided test script: .. code:: xojo # ./IDECommunicator testscript.xojo_script .. _/topics/build_automation/ide_communicator/version_2: Version 2 --------- Starting with Xojo 2016 Release 4, a new protocol can be used for communication. The new protocol allows two-way communication and the capture of load and compile errors (instead of showing dialogs). Xojo includes two IDE Communicator projects that can help you use this new protocol, located here: Example Projects/Advanced/IDE Scripting/IDECommunicator/v2. * IDECommunicator-Tester: A desktop project that lets you send IDE Scripts to Xojo. * IDECommunicator: A console project that lets you send IDE Scripts to Xojo. Xojo must be be running in order for the communication to work. To use the console project, built it for your OS and then run it from the command line to see how it works. This command runs it on Mac and Linux (# indicates your shell/terminal prompt): .. code:: xojo # ./IDECommunicator Most often you will send it an IDE Script file. This runs the provided test script: .. code:: xojo # ./IDECommunicator testscript.xojo_script .. _/topics/build_automation/ide_communicator/protocol_information: Protocol information ******************** The new Communicator protocol uses JSON strings termined with NULL characters (NUL) for communication. To turn on the new protocol, you need to upgrade to version 2 by sending the following command: .. code:: xojo "{""protocol"":2}" + Chr(0) Once in this mode, you can send scripts and receive responses as JSON with these values: * "script": The script you want to send. * "tag": Unique text used to identify responses. For example, sending this JSON will attempt to build the app: .. code:: json {"script":"Print BuildApp(7,False)", "tag":"build"} Don't forget that you need to add a NULL character terminator (NUL or Chr(0)) to the JSON you send. If the build was successful you will receive a response like this: .. code:: json {"tag":"build", "response":""} In this case, "response" is the value of the last value normally sent by the "Print" method. If a build error occurs, "response" will be an object containing errors, like this: .. code:: json { "tag":"build", "response":{ "buildError":{ "errors":[ { "type":"Code", "message":"Can't find a type with this name", "source":"dat", "location":"Module1.Run", "position":"Module1.Run, line 2" } ] } } } This response is a compile error indicating that there was a bad type "dat" on Module1.Run, Line 2. .. _/topics/build_automation/ide_communicator/script_errors: Script errors ************* This is an example of the response to an error in a supplied script: .. code:: json { "tag":"build", "response":{ "scriptError":[ { "type":"compiler", "line":0, "number":11, "message":"Error found while compiling" } ] } } .. _/topics/build_automation/ide_communicator/load_errors: Load errors *********** This is an example of an error caused by a missing picture in an image on load: .. code:: json { "tag":"build", "response":{ "openErrors":[ { "projectError":[ { "name":"image", "type":"Unable to locate original image", "data":"The original image cannot be located", "isFatal":false } ] } ] } } This error response indicates a missing Picture object: .. code:: json { "tag":"build", "response":{ "openErrors":[ { "projectError":[ { "name":"Artboard", "type":"Missing File 'Artboard.png'", "data":"Macintosh HD:Users:username:Desktop:Artboard.png", "isFatal":false } ] } ] } } You will also get an openError if the IDE version is lower than the project version: .. code:: json { "tag":"build", "response":{ "openErrors":[ { "projectError":{} }, { "error":{ "type":"IDE Version Conflict", "projectVersion":"2016.040", "ideVersion":"2016.030" } }, { "projectError":{} } ] } } .. _/topics/build_automation/ide_communicator/environment_variables: Environment variables --------------------- **Communicating With Multiple Xojo IDEs Independently** The Xojo IDE listens on an IPCSocket with a path of /tmp/XojoIDE. In order to communicate with more than one running at a time, you need to use a unique path for each running IDE. This can be accomplished by setting the environment variable XOJO_IPCPATH to a filename. The only valid characters for the filename are a-z, A-Z, 0-9 and _. .. code:: xojo env XOJO_IPCPATH=Xojo2019r2 /Applications/Xojo 2019 Release 2/Xojo.app/Contents/MacOS/Xojo & **Prevent Dialogs from Appearing When IDE Starts** When Xojo is launched with the environment variable XOJO_AUTOMATION set to TRUE it will skip the Feedback Crash dialog and the Restore Previous Project dialog so the build automation process doesn't get paused. (Added in 2018r3.) Code management =============== .. toctree:: :maxdepth: 1 :name: sec-code_management Coding guidelines Custom code reformatting Sharing code among multiple projects Using version control with Xojo projects ================= Coding guidelines ================= Every developer and team should have coding guidelines to help ensure that code is readable by everyone on the team (or even yourself several months from now). There is no such thing as coding guidelines that everyone will agree with, but the guidelines described here are meant to be a starting point and match the coding guidelines generally used for the Xojo example projects and sample code that you see here in the documentation. The most important thing is consistency, regardless of what guidelines you choose to use. Inconsistent code is more difficult to understand. .. _/topics/code_management/coding_guidelines/definitions: Definitions ----------- .. csv-table:: :header: "Term", "Definition" :widths: auto "camel case","The first *word* in the name is lower cased. Subsequent *words* are started with an upper case letter: **customerName**" "title case","All *words* in the name are started with an upper case letter: **CustomerName**" .. _/topics/code_management/coding_guidelines/naming: Naming ------ Having a consistent way to name the things in your project is an important first step in coding standards. Ideally coding standards will help others write correct code more easily and grasp existing code more readily. .. _/topics/code_management/coding_guidelines/constants: Constants ********* * Start with lower case "k", followed by title case: ``kMaxUsers``. .. _/topics/code_management/coding_guidelines/local_variables: Local variables *************** * Use camel case: ``Var customerName As String`` * Minimize use of abbreviations; spell things out. Use ``customerName``, not ``custNm``. * Use of single-letter variables names are acceptable for looping variables, such as used by For loops. .. _/topics/code_management/coding_guidelines/arrays: Arrays ****** * Should be plural: ``Customers() As String`` .. _/topics/code_management/coding_guidelines/properties: Properties ********** * Use title case: ``CustomerName`` * A Private property that is the companion for a Computed Property should start with "m" and then be title case: ``mCustomerName``. .. _/topics/code_management/coding_guidelines/methods: Methods ******* * Use title case: ``SaveCustomer`` * Method parameters should be camel case: ``SaveCustomer(customerName As String)`` .. _/topics/code_management/coding_guidelines/controls: Controls ******** * Use title case, with a suffix indicating the type of control: OKButton. Below are some common suffixes: .. csv-table:: :header: "Control", "Suffix", "Example" :widths: auto "AndroidMobileTable, iOSMobileTable", "Table", "CustomerTable" "DesktopButton, WebButton, MobileButton, DesktopBevelButton","Button","SaveButton" "DesktopListBox, WebListBox","List","CustomerList" "DesktopSegmentedButton, WebSegmentedButton, MobileSegmentedButton","Selector","TaskSelector" "DesktopCheckBox, WebCheckBox","Check","TaxableCheck" "DesktopPopupMenu, WebPopupMenu, MobilePopupMenu","Popup","StatePopup" "DesktopRadioGroup, WebRadioGroup","Radio","SourceRadio" "DesktopTextField, WebTextField, MobileTextField","Field","NameField" "DesktopTextArea, WebTextArea, MobileTextArea","Area","DescriptionArea" "DesktopCanvas, WebCanvas, MobileCanvas","Canvas","GraphCanvas" "DesktopLabel, WebLabel, MobileLabel","Label","NameLabel" "DesktopPagePanel, WebPagePanel","Panel","MainPanel" "DesktopTabPanel, WebTabPanel","Tab","MainTab" "DesktopProgressWheel, DesktopProgressBar, WebProgressWheel, WebProgressBar, MobileProgressBar, MobileProgressWheel","Progress","DownloadProgress" "DesktopHTMLViewer, WebHTMLViewer, MobileHTMLViewer","Viewer","DocViewer" "DesktopImageViewer, WebImageViewer, MobileImageViewer","Image","ProfileImage" "DesktopGroupBox","Group","BusinessGroup" .. _/topics/code_management/coding_guidelines/classes_(types): Classes (Types) *************** * Use title case. * Subclasses of built-in classes should use original class name as suffix: ``CustomerListBox``. .. _/topics/code_management/coding_guidelines/windows: Windows ******* * Use title case with "Window" as the suffix: ``CustomerWindow``. .. _/topics/code_management/coding_guidelines/interfaces: Interfaces ********** * Use title case with "Interface" suffix. .. _/topics/code_management/coding_guidelines/formatting: Formatting ---------- * Keywords should be in title case: .. code:: xojo For Each c As Customer In Customers * Data types should be in title case: .. code:: xojo Var count As Integer * Put spaces between all lists of arguments or parameters: .. code:: xojo SaveCustomer(name, location, value) * Do not put spaces before or after parenthesis. * Methods called without parameters should not include empty parenthesis. Use: .. code:: xojo MyMethod instead of: .. code:: xojo MyMethod() * Methods with parameters should always include parenthesis. Use: .. code:: xojo MyMethod(42) instead of: .. code:: xojo MyMethod 42 * Leave blank lines between code lines as appropriate to maintain readability. .. _/topics/code_management/coding_guidelines/sql: SQL *** * SQL commands are written in uppercase: .. code-block:: sql SELECT * FROM Team WHERE City = "Boston" .. _/topics/code_management/coding_guidelines/coding: Coding ------ * Var local variables near where they will be used with one declaration per line. * Methods not used outside the class/module should be Private. * Prefer dot notation over equivalent global methods when possible: ``Var length As Integer = customerName.Length`` * Never use :doc:`Me` when you mean :doc:`Self`. * Limit globals. * Prefer shared methods/properties on classes over global methods/properties on modules. * Classes, methods, properties, etc., within a Module or Class should be Private or Protected where possible. ======================== Custom code reformatting ======================== The Xojo Code Editor can format your code for you as you type or when you select code and choose "Standardize Format" from the contextual menu. If you don't like the way the Code Editor formats the code, you can override the code formatter by providing an IDE Script that does code formatting the way you want. There are two types of code formatting: * Single Line Reformatting: In the Code Editor Preference "Apply standardized format after ending line" is selected then this means that code formatting is applied: * As you enter each line of code. * When pasting a single line of code. * Block Reformatting: Applied when: * You select a block of code and choose "Standardize Format" from the contextual menu. * Paste multiple lines of code. To substitute your own code formatting, create an IDE Script and name it "ReformatCode.xojo_script". You should place this script in the Scripts folder alongside the Xojo IDE or alongside your project. In this IDE Script you'll need to implement the CleanBlock method in order to override code formatting: This IDE Script runs with a special Context that gives you access to things to help identify what to format. Essentially you are getting access to the Lexer (the one the compiler users), along with all the tokenized identifiers. .. _/topics/code_management/custom_code_reformatting/limitations: Limitations ----------- The ReformatCode.xojo_script has these limitations: * You can only use the core Xojo language with the Context methods and properties described below and the default language functions for Xojo Script. You cannot use other IDE Scripting commands. * ReformatCode.xojo_script is loaded and compiled once when the IDE starts. If you make changes you'll have to restart the IDE in order for them to take effect. * You cannot run ReformatCode.xojo_script from the IDE Scripts menu. .. _/topics/code_management/custom_code_reformatting/script_example: Script example -------------- This simple code just turns every identifier to uppercase: .. code:: xojo Sub CleanBlock() For i As Integer = 0 To LineCount - 1 Var s As String StartTokenizer(Line(i)) While NextToken = True If Not s.IsEmpty Then s = s + " " s = s + TokenText.Uppercase Wend ' If the line ends in a comment you will not get the token for a comment, ' but you will get the comment text in RemainingLine. If Not RemainingLine.IsEmpty Then s = s + RemainingLine End If Line(i) = s Next End Sub .. _/topics/code_management/custom_code_reformatting/context_methods: Context methods --------------- The following methods are available for use within the ReformatCode script. .. _/topics/code_management/custom_code_reformatting/constantvalue(propname_as_string)_as_string: ConstantValue(propName As String) As String ******************************************* Returns the value of a constant in a project item. The value is returned as a String regardless of its type. .. _/topics/code_management/custom_code_reformatting/debuglog(message_as_string): DebugLog(message As String) *************************** Outputs the message to the debug log. .. _/topics/code_management/custom_code_reformatting/xojoversion_as_double: XojoVersion As Double ********************* Returns the current Xojo version as a Double. **New in 2019r2.** .. code:: xojo Var version As String = XojoVersion.ToString If you need your script to be compatible with pre-2019r2 IDEs, you can create your own XojoVersion constant like this: .. code:: xojo Const XojoVersion = 0 When your script is run on 2019r2 or later, this constant will be removed so that the built-in one takes effect. .. _/topics/code_management/custom_code_reformatting/notes: Notes ^^^^^ On Windows, this logs to the debugger, so programs like DebugView can be used to view the string. On macOS, this logs to the Console. On Linux, this prints the message to StdErr which can be viewed when Xojo is run from the Terminal. .. _/topics/code_management/custom_code_reformatting/linecount_as_integer: LineCount As Integer ******************** The number of lines of code to be reformatted. .. _/topics/code_management/custom_code_reformatting/line(i_as_integer,_assigns_s_as_string): Line(i As Integer, Assigns s As String) *************************************** .. _/topics/code_management/custom_code_reformatting/line(i_as_integer)_as_string: Line(i As Integer) As String **************************** Gets or sets a specific line of code. .. _/topics/code_management/custom_code_reformatting/nexttoken_as_boolean: NextToken As Boolean ******************** Updates TokenText to contain the next token in the code's token stream. Returns False when there are no more tokens. .. _/topics/code_management/custom_code_reformatting/remainingline_as_string: RemainingLine As String *********************** This is the text remaining in the line after a token has been parsed. You can use this to get the comments on a line after parsing all the tokens in the line. .. _/topics/code_management/custom_code_reformatting/tokentext_as_string: TokenText As String ******************* The textual representation of the current token .. _/topics/code_management/custom_code_reformatting/tokentype_as_integer: TokenType As Integer ******************** The symbolic representation of the current token (should be matched against one of the exposed TOKEN_TK_xxxx properties below. .. _/topics/code_management/custom_code_reformatting/starttokenizer(code_as_string): StartTokenizer(code As String) ****************************** Initializes the tokenizer so that it can start fetching tokens for the provided code. .. _/topics/code_management/custom_code_reformatting/context_properties: Context properties ------------------ The following properties are available for use within the ReformatCode script. These properties tell you the type of the token in TokenText (all are read-only). * TOKEN_TK_IF As Integer * TOKEN_TK_SUB As Integer * TOKEN_TK_FUNCTION As Integer * TOKEN_TK_UNTIL As Integer * TOKEN_TK_AS As Integer * TOKEN_TK_END As Integer * TOKEN_TK_MOD As Integer * TOKEN_TK_DIM As Integer * TOKEN_TK_THEN As Integer * TOKEN_TK_ELSE As Integer * TOKEN_TK_WHILE As Integer * TOKEN_TK_WEND As Integer * TOKEN_TK_RAISE As Integer * TOKEN_TK_FOR As Integer * TOKEN_TK_EXCEPTION As Integer * TOKEN_TK_ARRAY As Integer * TOKEN_TK_OF As Integer * TOKEN_TK_NOT As Integer * TOKEN_TK_GOTO As Integer * TOKEN_TK_AND As Integer * TOKEN_TK_OR As Integer * TOKEN_TK_NIL As Integer * TOKEN_TK_REM As Integer * TOKEN_TK_NEXT As Integer * TOKEN_TK_ELSEIF As Integer * TOKEN_TK_TO As Integer * TOKEN_TK_TRUE As Integer * TOKEN_TK_FALSE As Integer * TOKEN_TK_BYREF As Integer * TOKEN_TK_NEW As Integer * TOKEN_TK_SELECT As Integer * TOKEN_TK_CASE As Integer * TOKEN_TK_RETURN As Integer * TOKEN_TK_SELF As Integer * TOKEN_TK_DO As Integer * TOKEN_TK_LOOP As Integer * TOKEN_TK_EXIT As Integer * TOKEN_TK_REDIM As Integer * TOKEN_TK_DOWNTO As Integer * TOKEN_TK_STEP As Integer * TOKEN_TK_ISA As Integer * TOKEN_TK_ME As Integer * TOKEN_TK_PRAGMA As Integer * TOKEN_TK_BYVAL As Integer * TOKEN_TK_OPTIONAL As Integer * TOKEN_TK_EXTENDS As Integer * TOKEN_TK_ASSIGNS As Integer * TOKEN_TK_CONST As Integer * TOKEN_TK_COMPILEIF As Integer * TOKEN_TK_COMPILEELSE As Integer * TOKEN_TK_COMPILEENDIF As Integer * TOKEN_TK_DECLARE As Integer * TOKEN_TK_CLASS As Integer * TOKEN_TK_INTERFACE As Integer * TOKEN_TK_IMPLEMENTS As Integer * TOKEN_TK_INHERITS As Integer * TOKEN_TK_PROPERTY As Integer * TOKEN_TK_PUBLIC As Integer * TOKEN_TK_PRIVATE As Integer * TOKEN_TK_PROTECTED As Integer * TOKEN_TK_SHARED As Integer * TOKEN_TK_NAMESPACE As Integer * TOKEN_TK_MODULE As Integer * TOKEN_TK_STATIC As Integer * TOKEN_TK_EVENT As Integer * TOKEN_TK_HANDLES As Integer * TOKEN_TK_EACH As Integer * TOKEN_TK_IN As Integer * TOKEN_TK_CATCH As Integer * TOKEN_TK_TRY As Integer * TOKEN_TK_FINALLY As Integer * TOKEN_TK_SUPER As Integer * TOKEN_TK_LIB As Integer * TOKEN_TK_IS As Integer * TOKEN_TK_CALL As Integer * TOKEN_TK_ADDRESSOF As Integer * TOKEN_TK_DELEGATE As Integer * TOKEN_TK_PARAMARRAY As Integer * TOKEN_TK_COMPILEELSEIF As Integer * TOKEN_TK_SOFT As Integer * TOKEN_TK_CONTINUE As Integer * TOKEN_TK_WITH As Integer * TOKEN_TK_STRUCTURE As Integer * TOKEN_TK_ENUM As Integer * TOKEN_TK_RAISEEVENT As Integer * TOKEN_TK_XOR As Integer * TOKEN_TK_GLOBAL As Integer * TOKEN_TK_AGGREGATES As Integer * TOKEN_TK_BREAK As Integer * TOKEN_TK_GETTYPE As Integer * TOKEN_TK_ATTRIBUTE As Integer * TOKEN_TK_CTYPE As Integer * TOKEN_TK_ADDHANDLER As Integer * TOKEN_TK_REMOVEHANDLER As Integer * TOKEN_TK_WITHEVENTS As Integer * TOKEN_TK_WEAKADDRESSOF As Integer * TOKEN_TK_USING As Integer * TOKEN_TK_ASYNC As Integer * TOKEN_TK_AWAIT As Integer * TOKEN_IDENTIFIER As Integer * TOKEN_NUMBER As Integer * TOKEN_STRING As Integer * TOKEN_ASSIGN As Integer * TOKEN_GE_RELOP As Integer * TOKEN_LE_RELOP As Integer * TOKEN_NE_RELOP As Integer * TOKEN_REALNUMBER As Integer * TOKEN_ENDL As Integer * TOKEN_UNMATCHEDQUOTES As Integer * TOKEN_COLOR As Integer * TOKEN_LINE_CONTINUATION As Integer * TOKEN_TK_VAR As Integer .. _/topics/code_management/custom_code_reformatting/see_also: .. seealso:: :doc:`IDE Scripting` topic ==================================== Sharing code among multiple projects ==================================== Normally your projects are independent of each other. But there are certainly times when you may have common project items that you want to share amongst multiple projects. .. _/topics/code_management/sharing_code_among_multiple_projects/copy_and_paste_with_a_master_project: Copy and paste with a master project ------------------------------------ The simplest way to do this is with copy and paste. You can select the project items in the Navigator, copy them and then paste them into another project. This creates two separate copies of the project items for each project. The best way to use this technique is with a “master project” that contains the shared code you use. Keep the master project updated with all your shared code and any changes you make. You can copy shared project items from the master project as needed to your other projects. This technique allows you to re-use project items without having to worry about changes affecting other projects. The downside to this approach is that you make fixes or enhances to the code in your master projects, then the projects you copied it to will not benefit from the change unless you manually copy the changes over. .. _/topics/code_management/sharing_code_among_multiple_projects/export_/_import_items: Export / import items --------------------- You can also use the File Export and Import items to create standalone files on disk that can be used to share with others and with other projects. Project Items can be exported as Binary or XML format. To export a project item, select it in the Navigator and then choose File > Export from the menu. You'll be prompted for a location. To import a project item that was previously exported, choose File > Import from the menu. .. youtube:: FAMWsoImw4k When you import an item exported in this way, it becomes part of the new project and is not affected by changes to the original. .. _/topics/code_management/sharing_code_among_multiple_projects/external_items: External items -------------- The last option is to convert project items to “External Project Items”. An External Project Item can appear in multiple projects, so changing the shared project item in one project changes it for all projects that use it. When you create an External Project Item, the item becomes a file on disk (in either Binary or XML format). To convert a normal project item to an External Project Item, open the contextual menu for the item in the Navigator (usually by right-clicking on it) and select “Make External". .. image:: https://documentation.xojo.com/topics/code_management/images/sharing_code_among_multiple_projects_externalprojectitem.png This prompts you to choose a location and type for the file. Your choices are Binary or XML. If your External Project Item is going to be stored in a version control system, XML is the better choice. External Project Items appear in the Navigator as italic with a small arrow icon superimposed on the normal icon. Once an you have a project item saved as a file on disk you can add it to another project as an external item. To do so, hold down the Alt key (:kbd:`⌘ Option` on Mac) before clicking in the menu, then select the File menu and you'll see the Import item now says "Import External". Choose that command and select the file to be imported. You can also add an external item by dragging the file from the disk to the Navigator while holding down :kbd:`⌘ Option` (on Mac) or :kbd:`Shift Ctrl` (on Windows and Linux). Files that were created using the standard "export" process can also be added as external using the above steps. .. warning:: If you make a change to an external project item outside of Xojo (perhaps with version control), the change is not automatically reflected in any open projects that are using the external project item. You have to close and reopen the open projects in order to see the change. .. _/topics/code_management/sharing_code_among_multiple_projects/see_also: .. seealso:: For additional information, refer to these topics: * :doc:`Project Types` * `Video: Code Sharing `_ ======================================= Using version control with Xojo projects ======================================= A good developer always has backups of their source code. But backups are not enough. You also want to use a Version Control System. This is also sometimes called a Version Control System. A version control system is able to track changes to individual files, keeping a history of the changes and allowing you to go back and look at prior versions of the file. In order to use a version control system with your projects, you first want to make sure you are using the Text Project (Xojo Project) format. The Text project format saves each project item as a separate text file on disk. Having separate files allows the version control system to track changes to individual project items. Text files allows you to use “difference” tools (aka diff tools) to compare differences between versions of a file. By having the changes tracked for each file, you can "commit" groups of changes to the version control system to easily tracked what was changed, by whom and when. The Xojo text file project format works with any version control system, including Subversion, Git (including GitHub), Mercurial, CVS, Vault, Team Foundation Server or anything else. .. _/topics/code_management/using_version_control_with_xojo_projects/git: Git --- Git is currently the most popular version control system. Git is a distributed version control system which does not rely as heavily on a central repository for your source code, although you can certainly use it that way. You can control Git using the command-line tool. But there are plenty of easy-to-use graphical tools that work with Subversion, such as SourceTree and Tower. .. _/topics/code_management/using_version_control_with_xojo_projects/git_hosting: Git hosting *********** You can use Git completely on your local workstation, but there are also hosting companies that offer centralized Git services such as GitHub, BitBucket, GitLab, Beanstalk and Assembla. .. _/topics/code_management/using_version_control_with_xojo_projects/start_an_open-source_project_on_github: Start an open-source project on GitHub ************************************** There are many Xojo-related open-source projects available on GitHub. Here is how you can make your own libraries and projects available on GitHub. .. _/topics/code_management/using_version_control_with_xojo_projects/create_a_github_account: Create a GitHub account ^^^^^^^^^^^^^^^^^^^^^^^ If you don't already have a `GitHub account `_, you 'll need to create one. GitHub is the most popular place for hosting open-source projects and it is entirely free. As its name suggests, GitHub uses the Git version control system for storing the project source and managing updates. It also has a built-in wiki for documentation and an issue tracker. The way GitHub works is that you 'll add your project, which is then available for anyone to download and use. In addition, others can submit changes to your project in the form of “pull requests”. These are perhaps enhancements or bug fixes that you can review and choose to merge into the project if you want. .. _/topics/code_management/using_version_control_with_xojo_projects/create_repository: Create repository ^^^^^^^^^^^^^^^^^ Now that you have a GitHub account, you 'll want to go to your GitHub home page and click the “New Repository” button. A repository is essentially where your project is stored. You 'll want a separate repository for each project. .. image:: https://documentation.xojo.com/topics/code_management/images/using_version_control_with_xojo_projects_githubcreaterepository.png Be sure to come up with a catchy “repository name” and always include a description. You 'll want to leave “Public” selected as it's not really open-source if it's not public – and you actually have to pay to host private repositories on GitHub. It is always good to check “Initialize this repository with a README”, which adds an empty README file. This is where you 'll briefly describe the project and how to use it (using Markdown). You should also select “Xojo” for the gitignore setting. This identifies the project as using the Xojo programming language, which helps with searches and also marks the “uistate” file (which just has general information about the Xojo IDE window locations and sizes) so that it is not included in the repository. Lastly, you 'll need to choose a license. For most projects, the MIT License is a good choice. It essentially allows anyone to use your code however they want. Click the Create Repository button to create the repository. .. _/topics/code_management/using_version_control_with_xojo_projects/clone_the_repository_to_your_computer: Clone the repository to your computer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now that the repository is created on GitHub, you 'll want to “clone” it to your computer. This copies the repository files to a folder on your computer so you can modify its files. At this point, the only files in the repository will be LICENSE and the empty README.md, but it is still necessary to do the clone so that you can create the folder where you will then add your Xojo code. In order to clone, you 'll want to use a Git client application. There are many to choose from, but two free ones are the official `GitHub Desktop `_ and `Atlassian SourceTree `_. GitHub Desktop simpler and SourceTree is more advanced. Download GitHub Desktop: https://desktop.github.com Start GitHub Desktop and click “Sign into GitHub.com”. On the Configure Git page, enter the name and email you want to use to identify your commits. Click Finish on the next page to show the main screen: On this screen you have the option of creating new repositories or cloning a repository. Since we already created a repository online in the previous section, you want to select “Clone a Repository”. In the list, choose the repository you created above, choose your local folder location (Choose…) and click Clone. You now have a folder on your disk where you can add your Xojo project. *Note: If you are using another client, you will choose its “clone a repository from URL” feature and then provide the URL shown in the “Clone or Download” button on the repository page.* .. _/topics/code_management/using_version_control_with_xojo_projects/add_your_xojo_project_code: Add your Xojo project code ^^^^^^^^^^^^^^^^^^^^^^^^^^ Start Xojo and open the project you want for the repository you cloned. Click “Save As”, make sure the Format is “Xojo Project” and choose the folder you created when you did the checkout. You 'll want to use the “Text Project” format as that is what works best with Git because it allows people to view source online and makes it possible to precisely track changes. .. image:: https://documentation.xojo.com/topics/code_management/images/using_version_control_with_xojo_projects_githubdesktop.png Go back to GitHub Desktop and you 'll now see all the Xojo files appearing in the list as “changed files”. (For a quick test try: Example Projects/Database/SQLite/SQLiteExample.) You now need to “Commit” these changes. By default they are all checked so you can enter a brief summary (for the first commit, often “Initial Commit.” is used) and then click the “Commit to master” button. .. image:: https://documentation.xojo.com/topics/code_management/images/using_version_control_with_xojo_projects_githubrepo.png This marked the files as committed in your local repository, which is needed before you can push the changes up to the online repository. Generally speaking you want to commit things after you have made changes that you want to track. You don't need to immediately push the changes up to the server (online repository) each time, however. But for this initial commit you do want to push it out to the server, so click the button in the toolbar that says “Push origin”. If you go back to the GitHub repository online and refresh the page you 'll now see the files have been committed. .. _/topics/code_management/using_version_control_with_xojo_projects/sharing_your_project: Sharing your project ^^^^^^^^^^^^^^^^^^^^ Your project is now publicly available on GitHub for you to share with the world. All you need to do now is give people the URL! Definitely at least share the URL with Xojo so we can add it to the Open Source Projects page in the Xojo Dev Center. However, one other thing you should do on the project is click the “Add topics” link under the project description near the top of the page and add some tags. At the very least, add a tag for “Xojo”, but also add tags for whatever other technologies are relevant to your project. This will make it much easier for people to find your project when they search. .. _/topics/code_management/using_version_control_with_xojo_projects/changing_and_updating_your_project: Changing and updating your project ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You 'll likely be continuing to change your project to fix bugs and add features. Each change you make shows up in GitHub Desktop where you can commit it and then push it to the server. .. image:: https://documentation.xojo.com/topics/code_management/images/using_version_control_with_xojo_projects_markdownpro.png As an example, you should also make sure you update the README.md file to actually have a bit more description of your project and how people can use it. On your computer, open README.md in a text editor (or even better, a Markdown editor if you have one, such as Markdown Pro). You can learn more about Markdown here: `GitHub Markdown Guide `_ Save the file and the go to GitHub Desktop to see that it shows as changed. With the file selected in GitHub desktop you can see that it shows you what has changed with the new content identified. This also works with source code and is why you want to use the Xojo Project text file format — you 'll be able to see exactly what code you 've changed. You can now Commit this change (always add Summary text so you know the reason for the change!) and then Push it to the server (Push origin). .. _/topics/code_management/using_version_control_with_xojo_projects/using_or_cloning_other_repositories: Using or cloning other repositories ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. image:: https://documentation.xojo.com/topics/code_management/images/using_version_control_with_xojo_projects_githubclone.png Now that you 've published your project, anyone can use it. And don't forget that you can also use other people's repositories. In both cases you 'll want to click the “Clone or Download” button on the repository page. For simple usage where you just want to grab the current version of the repository you can then click the Download ZIP button. Once downloaded, unzip the file and then open the project in Xojo where you can use it or copy/paste it into your own projects (review the license agreement before you use projects, of course). You 'll want to clone a project when you want to manage it using GitHub Desktop (or your GitHub client of choice). This allows you to fetch and pull updates from the online repository and also allows you to make your own changes and push them up to the server where the repository owner will get them as pull requests. The owner can then review your changes and decided whether they want to incorporate (pull) them into the project. .. _/topics/code_management/using_version_control_with_xojo_projects/learn_more: Learn more ^^^^^^^^^^ This section just scratches the surface of what GitHub does and also only touches lightly on the concept of version control in general. If you 'd like to use Git for version control, but don't want to make your projects public you might also want to check out `Atlassian BitBucket `_ or `GitLab `_, both of which offer free Git hosting for both private and public repositories. .. _/topics/code_management/using_version_control_with_xojo_projects/git_videos: Git videos ********** There are several videos on how to use Git with Xojo: * `Version Control with Xojo `_ * `Getting to Know Git Part 1 `_ * `Getting to Know Git Part 2 `_ .. _/topics/code_management/using_version_control_with_xojo_projects/subversion: Subversion ---------- Subversion (SVN) is another commonly used version control system that predates Git. SVN uses a central “repository” or database that contains your source code and all its versions. The process for working with Subversion is as follows: * Create a repository on the Subversion server * Check Out the repository to a local computer. This is where you edit your files. * When you want to make your changes permanent, you Commit them to the Subversion server. This also allows others on your team to see the changes. * If you have multiple developers working on a team, you use the Update command to get changes added to the server by others on your team. You can control Subversion using the command-line tool, called svn. But there are plenty of easy-to-use graphical tools that work with Subversion, such as SmartSVN, RapidSVN, Versions and Cornerstone. .. _/topics/code_management/using_version_control_with_xojo_projects/subversion_hosting: Subversion hosting ****************** You can run a Subversion server on your development machine, but having Subversion on an external server is a great way to have an easy off-site backup of your source code. You can choose to install Subversion on your own servers and some web hosting companies provide Subversion hosting as an optional feature. There are also companies that offer Subversion hosting, such as Cloudforge, Beanstalk and Assembla. .. _/topics/code_management/using_version_control_with_xojo_projects/additional_information: Additional information ********************** Here are some terms that are commonly used with Subversion: * Checkout: Download files from the Subversion repository to a local working copy. * Commit: Sends a set of changes in the local working copy over to the Subversion repository. * Ignore: For excluding files in your Subversion folder from being managed by Subversion. * Revert: For retrieving older versions of a file from the repository, discarding any local changes. * Merge: Used to combine files that have been separately modified by two or more people. To learn everything there is to know about Subversion, read the free, official Subversion book: `Version Control with Subversion `_. .. _/topics/code_management/using_version_control_with_xojo_projects/subversion_videos: Subversion videos ***************** * `Version Control with Xojo `_ .. _/topics/code_management/using_version_control_with_xojo_projects/online_and_network_file_systems: Online and network file systems ------------------------------- Do not edit your Xojo project files directly from networked file systems (SMB, etc.) or online file systems (DropBox, Google Files, Box, etc.). The multi-user access and automated syncing can cause project corruption if things happen at inappropriate times. For sharing projects between computers, Xojo recommends using a Using Version Control with Xojo Projects as described above. .. _/topics/code_management/using_version_control_with_xojo_projects/see_also: .. seealso:: :doc:`Project Types` topic Communication ============= .. toctree:: :maxdepth: 1 :name: sec-communication Hardware Internet Hardware ======== .. toctree:: :maxdepth: 1 :name: sec-hardware Communicating with serial devices ================================== Communicating with serial devices ================================== A serial device is a device that communicates by sending and/or receiving data in over an actual or a virtual serial port. This means that it is either sending data or receiving data at any one moment. It doesn't send and receive at the same time. In fact all USB devices are serial devices that have a driver of some kind (USB stands for Universal Serial Bus). To communicate with a serial device you configure an instance of a :doc:`SerialConnection` class, open the serial port to make the connection, read and/or write data to and/or from the serial device connected to one of your serial ports, and finally close the serial port when you are ready to disconnect from the serial device. .. _/topics/communication/hardware/communicating_with_serial_devices/setting_up: Setting up ---------- The first step is to add a Serial class to your project. The easiest way to do this is to drag a Serial controller from the Library to your Window. You can also drag the Serial controller to the Navigator to create a subclass that you can instantiate in your code. Before you can begin communicating with a serial device, you need to tell the Serial controller the port to use, the speed of the communication and other settings. If you 've added the Serial controller to your window you can change these properties using the Inspector. The specifics for how you configure these properties depends completely on the device you are using. You should consult the documentation for your device to determine the settings it supports. .. _/topics/communication/hardware/communicating_with_serial_devices/opening_the_port: Opening the port ---------------- Once you have configured the Serial controller, you can open the serial port to initiate communications with the serial device. This is done by calling the Open method of class. This method returns :doc:`True` if the connection is opened and False if it is not. For example, suppose you have a Serial controller whose name is “SerialDevice”. You can open the serial port with the following code: .. code:: xojo If SerialDevice.Open Then MessageBox("Opened serial port.") Else MessageBox("Could not open serial port.") End If Once you have successfully opened the serial port, it will be unavailable to all other applications (and to other Serial controllers as well) until it is closed. To close the Serial port, call the Close method: .. code:: xojo SerialDevice.Close .. _/topics/communication/hardware/communicating_with_serial_devices/reading_data: Reading data ------------ When the serial device sends data back to the Serial controller that is connected to it, the data that has been sent back goes into a place in the computer's memory called a buffer. The buffer is simply a place to store the data that has been sent by the serial device because most serial devices don't have much memory of their own. When new data arrives in the buffer, the DataAvailable event handler of the Serial controller is called. In the DataAvailable event handler, you use the Read or ReadAll methods to get some or all of the data in the buffer. This data is returned as a String. Use the Read method when you want to get a specific number of bytes (characters) from the buffer. If you want to get all the data in the buffer, use the ReadAll method. In both cases, the data returned from the buffer is removed from the buffer to make room for more incoming data. If you need to examine the data in the buffer without removing it from the buffer, you can use the LookAhead method instead. This code in the DataAvailable event appends incoming data to a TextArea: .. code:: xojo TextArea1.AddText(Me.ReadAll) You can clear all data from the buffer without reading it by calling the Flush method. Both the Read and ReadAll methods can take an optional parameter that enables you to specify the encoding. Use the Encodings object to get the desired encoding and pass it as a parameter. For example, the code above has been modified to specify that the incoming text uses the ANSI encoding, a standard on Windows: .. code:: xojo TextArea1.AddText(Me.ReadAll(Encodings.WindowsANSI)) You may need to specify the encoding when text is coming from another platform or is in another language. For information about text encoding, see the :doc:`Understanding Text Encodings` topic. .. _/topics/communication/hardware/communicating_with_serial_devices/writing_data: Writing data ------------ You can send data to the serial device at any time as long the serial port is open. You send data using the Write method. The data you wish to send must be a string. The Write method is performed asynchronously. This means that your application does not wait for the Write method to finish before continuing with the next line of code. This behavior allows your application to remain responsive. This code sends the text in a TextArea out to the Serial device: .. code:: xojo SerialDevice.Write(TextArea1.Text) If you would rather wait for all data to be sent before continuing with the next line of code, call the XmitWait method before calling Write: .. code:: xojo SerialDevice.XmitWait SerialDevice.Write(TextArea1.Text) .. _/topics/communication/hardware/communicating_with_serial_devices/changing_the_serial_configuration: Changing the serial configuration --------------------------------- There may be times when you need to change the Serial controller behavior properties while the serial port is open. While you can change these properties, the changes won't take effect until you close the serial port and reopen it. If you need the behavior properties to update immediately, call the Poll method. This updates all properties immediately and calls the DataAvailable event handler immediately if there is any data waiting in the buffer. .. code:: xojo SerialDevice.Poll .. _/topics/communication/hardware/communicating_with_serial_devices/closing_the_port: Closing the port ---------------- Once you are finished communicating with a serial device, you must close the serial port to end the communications session and make the port available to other Serial controllers or other apps. To close the Serial port, call the Close method using the same Serial controller that opened the port: .. code:: xojo SerialDevice.Close .. _/topics/communication/hardware/communicating_with_serial_devices/communicating_with_usb,_firewire_and_bluetooth_devices: Communicating with USB, FireWire and Bluetooth devices ------------------------------------------------------ Generally speaking, USB, FireWire and Bluetooth devices uses a completely different interface that often requires drivers in order to communicate with them. With that said, many of these devices have a chip in them that makes them appear and behave as if they were a serial device. This chip is often an FTDI chip, for which drivers are available. If your device is not recognized as a serial device, then you should investigate using the MonkeyBread Software plugin (available at the `Xojo Extras Store `_) which has some USB support for specific types of devices. .. _/topics/communication/hardware/communicating_with_serial_devices/usb_background: USB background ************** USB wraps up several things into one: * A cable interconnect specification (what the cable connectors need to look like) * A low-level packet oriented protocol, so the OS can figure out what driver to load and talk to the USB device to identify it * A vendor API, i.e., HID device like a mouse, keyboard, or mass storage device Just because something uses a USB cable doesn't mean you can talk to it. Some device types are very common so the OS vendors have the drivers built in. This includes HID devices (e.g., mice and keyboards) and mass storage devices like hard disks. If it's one of those, they will work without any additional work. Almost anything else requires a driver from the vendor. If you 'd like to control such a device, you will need to obtain a shared library from the manufacturer or write your own. Try to contact the manufacturer to see if they have a library. If the manufacturer has a library for USB communications, you can communicate with the library using Declare statements. .. _/topics/communication/hardware/communicating_with_serial_devices/using_serial_with_web_apps: Using serial with web apps -------------------------- Web apps can use the Serial class to communicate with serial devices that are attached to the web server. The Serial class cannot be used to communicate with serial devices on the user's computer. .. _/topics/communication/hardware/communicating_with_serial_devices/see_also: .. seealso:: :doc:`SerialConnection` class; :doc:`Connecting to A Serial Device` topic Internet ======== .. toctree:: :maxdepth: 1 :name: sec-internet Building a server into your app HTTP (web) communication Internet communications Communicating via TCP/IP Communicating via UDP Accessing web services Using non-secure URLs on macOS and iOS Sending email From your app Testing a web service =============================== Building a server into your app =============================== Sometimes you need to have a server that can handle multiple connections from multiple clients. You cannot build your own using the TCP classes because you will not be able to ensure you can respond to every request. There are two ways to build your own server. The first and easiest way is to use a Xojo web app. You might normally think of a web app as have a user interface, but a user interface is not required. You can create a web app and only implement the WebApplication.HandleURL or WebApplication.HandleSpecialURL events so that your server responds to requests send to it. This can be a fast and easy way to create your own server for a web services API, for example. The other option is to create your own server from scratch building it using the :doc:`ServerSocket` class. .. _/topics/communication/internet/building_a_server_into_your_app/using_a_serversocket: Using a ServerSocket -------------------- If you need to communicate with more than one app via the same port, it is difficult to do using the TCPSocket because each TCPSocket can manage only one connection at a time. To use the TCPSocket, you would have to implement a system for managing multiple TCPSockets, as connections are received. To handle this situation, you should use the ServerSocket class. A ServerSocket is a permanent socket that listens on a single port for multiple connections. When a connection attempt is made on that port, the ServerSocket hands the connection off to another socket, and continues listening on the same port. Without the ServerSocket, it is difficult to implement this functionality due to the latency between a connection coming in, being handed off, creating a new listening socket, and restarting the listening process. If you had two connections coming in at about the same time, one of the connections may be dropped because there was no listening socket available on that port. To initiate the ServerSocket's listening process, set the port to listen to by assigning a value to the Port property and call the ServerSocket's Listen method. On macOS and Linux, attempting to bind to a port less than 1024 will cause a SocketCore.Error event to fire with an error 105 unless your application is running with root permissions. This is a built-in security feature of Unix-based operating systems. This is not a bug, but a security feature that prevents problems that can arise from allowing sockets to listen on privileged ports. The ServerSocket control automatically manages a pool of TCPSockets available for use. You don't create the TCPSockets explicitly; instead you can set the size of this pool using the MinimumSocketsAvailable and MaximumSocketsConnected properties after establishing the listening socket. If you change the MaximumSocketsConnected property, it will not kill any existing connections. It just may not allow more connections until the existing connections have been released. If you change the MinimumSocketsAvailable property, it may fire the AddSocket event of the ServerSocket to replenish its internal buffer. When you call the ServerSocket's Listen method, it first fills its internal pool of handoff sockets. It does this by calling the AddSocket event. This event will be called until it has enough sockets in the internal pool of available sockets. It adds the number specified by the MinimumSocketsAvailable property, plus ten extra sockets. (Note that if you return Nil from this event, it will cause a NilObjectException.) The ServerSocket is not ready to hand off connections until this process is completed. Connections that come in while the server is populating its pool are rejected. To determine when the ServerSocket is ready to accept incoming connections, check its IsListening property. A ServerSocket can only return a TCPSocket (or a subclass of TCPSocket) in its AddSocket event. ServerSocket is only for use with TCPSockets. Since UDP is a connectionless protocol, it does not make sense for a ServerSocket to deal with UDPSockets. .. _/topics/communication/internet/building_a_server_into_your_app/reference_counting: Reference counting ****************** The reference count isn't incremented when you return a socket with the AddSocket method. Instead, the socket is pooled internally and its reference count is incremented when the server hands off a connection to that socket. If the ServerSocket is destroyed before it uses one of these pooled sockets, the unused sockets get destroyed as well. Until that time, the ServerSocket is the parent of the TCPSocket, and so the TCPSocket will remain. If a socket returned from the AddSocket event has been handed a connection and then the ServerSocket is destroyed, the socket will remain connected and continue to function. .. _/topics/communication/internet/building_a_server_into_your_app/see_also: .. seealso:: :doc:`ServerSocket` class; :doc:`Accessing Web Services` topic ======================== HTTP (Web) communication ======================== HTTP (HyperText Transfer Protocol) is the protocol that web browsers use. To communicate using the HTTP protocol use the :doc:`URLConnection` class. This class contains methods and properties that enable you to do all types of HTTP communication such as retrieving a URL or posting a form. .. _/topics/communication/internet/http_(web)_communication/requesting_content: Requesting content ------------------ Some of the more common things you do with a URLSocket is to get the contents of a web service and post forms to web pages. To use a URLSocket, you can drag it from the Library onto your Layout. Or you can add a subclass to your project. There are two ways to send HTTP requests to URLs: Send and SendSync. The Send method is asynchronous so the URL request will not force you app to wait until it returns. The SendSync method is synchronous so your app will wait until the data from the request is received (or it times out). You can make a SendSync call in a Thread to prevent your app from waiting. When you send a request you have to tell it the sending method. GET and POST are the two most common, but there are others. This code sends a GET request to a web service and waits up to 30 seconds for a response: .. code:: xojo Var content As String content = MyConnection.SendSync("GET", "http://127.0.0.1:8080/GetData", 30) The data will be in the content variable when the response is received. To do this same call asynchronously, you call it like this: .. code:: xojo MyConnection.Send("GET", "https://127.0.0.1:8080/GetData") With this method, the URLConnection.ContentReceived event is called when the data is received. You will need to implement the event handler so that you can get the data. .. _/topics/communication/internet/http_(web)_communication/downloading_a_file: Downloading a file ****************** Both Send and SendSync have optional parameters to request a file. To synchronously request a file you also provide a :doc:`FolderItem` for it to be saved to: .. code:: xojo Var outputFile As FolderItem = SpecialFolder.Documents.Child("data.json") MyConnection.SendSync("GET", "https://127.0.0.1:8080/GetData", outputFile, 30) And the asynchronous call is similar: .. code:: xojo Var outputFile As FolderItem = SpecialFolder.Documents.Child("data.json") MyConnection.Send("GET", "http://127.0.0.1:8080/GetData", outputFile) When the file is received, the FileReceived event is called, which you will need to implement so you know when the file download has finished. .. _/topics/communication/internet/http_(web)_communication/posting_to_a_form: Posting to a form ***************** To post data to a form on a page, you first build the form text and then use the SetRequestContent method to specify it. Then you can call the Send method to send the request. Here is an example that sends a POST request with form data: .. code:: xojo ' Build form String Var formText As String = "firstName=Bob&lastName=Roberts" ' POST it mySocket.SetRequestContent(formText, "application/x-www-form-urlencoded") mySocket.Send("POST", "https://www.webserviceurl.com") .. _/topics/communication/internet/http_(web)_communication/ios_and_mac_notes: iOS and macOS notes ******************* On iOS and macOS you will not be able to send requests to non-secure URLs. Apple calls this :doc:`App Transport Security`. That is only "https" URLs are allowed by default. If you need to call an "http" URL you will need to provide a plist with your app that is configured to allow these type of URLs. Refer to the :doc:`Using Non-Secure URLs on macOS and iOS` topic for more information. .. _/topics/communication/internet/http_(web)_communication/see_also: .. seealso:: :doc:`URLConnection` class; :doc:`Accessing Web Services`, :doc:`Using Non-Secure URLs on macOS and iOS` topics ======================= Internet communications ======================= Any kind of communication requires that all parties involved agree on a method of communication and a language. For example, if you want to communicate with a friend, you might talk to them face to face, call them on the phone, or send them email. Both of you must be able to communicate using the same language or you won't be able to communicate at all. Communications via the Internet works the same way. The language used is called a protocol. A protocol is an organized way of sending and/or receiving information. .. _/topics/communication/internet/internet_communications/internet_protocols: Internet protocols ------------------ If you are writing an app that will communicate with another application via TCP/IP (Transfer Control Protocol/Internet Protocol) for example, you will need to understand the protocol the other app will be expecting in order to communicate with it. For example, on the Internet, the protocol for the world wide web is called HTTP (HyperText Transfer Protocol), the protocol for sending email is called SMTP (Simple Mail Transfer Protocol), and a protocol for receiving email mail is called POP3 (Post Office Protocol 3). Complete descriptions of these Internet protocols and others are available on the Internet. The descriptions of these protocols are called RFCs (Request For Comments). The easiest way to find information on RFCs is to go search for “RFC”. This will give you a list of links to various web sites that explain all of the various Internet protocols. If you are writing an app that communicates with other apps you have written, then you can define your own protocol. Your protocol will be a set of commands you define that allow the apps to understand what the other wants. .. _/topics/communication/internet/internet_communications/limitations_on_sockets_in_web_applications: Limitations on sockets in web applications ------------------------------------------ You cannot use a networking socket of any kind on a web page because the socket is really running on the server and would have no way to push its data back to the client browser to update the user interface. However, sockets that are a property of your App class will work. .. _/topics/communication/internet/internet_communications/see_also: .. seealso:: :doc:`URLConnection` class Communicating via TCP/IP ======================== Sometimes apps need to communicate with other apps on the same network. This can be accomplished using the :doc:`SSLSocket` or :doc:`TCPSocket` classes. SSLSocket can send and receive data using the TCP/IP Internet protocol, using both secure and non-secure communication. TCPSocket can only do non-secure communication. Use these socket classes to communicate with other computers on the same network. When you connect to the Internet, you are part of the Internet network. This allows you to communicate with other computers on the Internet via TCP/IP. .. _/topics/communication/internet/communicating_via_tcp/ip/security: Security -------- The SSLSocket class is used to do secure communications via TCP/IP. It works similarly to the TCPSocket class, but has additional properties for the connection type, managing certificate locations and for checking if a secure connection was made. You are strongly encouraged to use SSLSocket for secure communication. .. _/topics/communication/internet/communicating_via_tcp/ip/set_up: Set up ------ To get started with TCP/IP communication, you need to add an :doc:`SSLSocket` class to your project. The easiest way to do this is to drag a TCPSocket control from the Library to the window or the Navigator and then change its Super to SSLSocket. Before you can connect to another computer using a socket, you must first set the port. The port is to TCP/IP what channels are to television or frequency assignments are to radio stations. Ports give an app the ability to focus on specific data rather than receiving all the data transmitted to your computer via TCP/IP. This allows you to browse the web and send email at the same time because the web uses one port and email uses another. The port is represented by a number and there are thousands of available ports. Some have already been designated for specific functions like web browsing (80), email (25, 110, 143), FTP (20, 21), etc. If you are designing an app that will need to communicate with another app, you will need to find out what port the other app is using. An SSLSocket has a Port property that can be assigned at design time or runtime but it must be assigned a value before you can connect to another computer. If you plan on initiating the connection, you must also set the Address property to the address (IP or resolvable name) of the computer to which you want to connect. Linux and MacOS have built-in restrictions regarding port numbers. Ports below 1024 cannot be assigned by a user who is not running with "root" privileges. MacOS is configured so that a user cannot gain root privileges via the graphic user interface. Most users run with Admin privileges — not root — so you should use ports above 1024 for normal TCP/IP communications with MacOS or Linux because the socket cannot access port numbers below 1024. This is not a bug but a security feature that is built into these operating systems. An SSLSocket control can only be connected to one connection (and thus app) at a time. If you need to maintain multiple connections simultaneously, you should use a :doc:`ServerSocket`, which is designed for this purpose. .. _/topics/communication/internet/communicating_via_tcp/ip/connecting_to_another_computer: Connecting to another computer ------------------------------ Once you have assigned a port and an IP address, you can connect to an app on the computer at that IP address, provided that the app is listening for TCP/IP connections on the port you have specified. To initiate a connection, you call the Connect method. Keep in mind that a connection is not necessarily established immediately after you call Connect. You have to wait for the app to which you are trying to connect to accept your connection. In addition, there could be general latency or other issues that take time to resolve. .. code:: xojo TCPConnection.Connect There are two ways to determine that you are connected. You can either wait until you receive a Connected event from your socket or test the return value of the IsConnected method. If you don't wait for this feedback, you either cause the connection process to halt, resulting in a lost connection error (102), or an out of state error (106). When the connection is established, the Connected event handler is called. If a connection is not established, an error occurs and the Error event handler is called. Once a connection is established, your app can begin sending and receiving data with the app at the other end of the connection. .. _/topics/communication/internet/communicating_via_tcp/ip/listening_for_a_connection_from_another_computer: Listening for a connection from another computer ------------------------------------------------ Your app can also listen for a connection request from another application. To do this, you use the Listen method: .. code:: xojo TCPConnection.Listen Once you put a socket into listen mode, it waits asynchronously for a connection request. Your app remains responsive while the socket is waiting and code continues to run. When a connection is requested, the Connected event handler is called, which lets you know you have a connection. .. _/topics/communication/internet/communicating_via_tcp/ip/reading_data: Reading data ------------ When the app at the other end of the connection sends data back to the TCPSocket that it's connected to, the DataAvailable event handler is called. The data that has been sent back goes into a place in the computet's memory called a buffer. The buffer is a place to store the data that has been sent by the other app. In the DataAvailable event handler, you can use the Read or ReadAll methods to get some or all of the data (returned as a String) in the buffer. Use the Read method when you want to get a specific number of bytes (characters) from the buffer. If you want to get all the data in the buffer, use the ReadAll method. In both cases, the data returned from the buffer is removed from the buffer to make room for more incoming data. If you need to examine the data in the buffer without removing it from the buffer, use the LookAhead method. This code in the DataAvailable event handler appends incoming data to a TextField: .. code:: xojo TextField1.AddText(Me.ReadAll) When you are reading text from an outside source, you may need to specify the text encoding. The text encoding is the scheme that maps each letter in the text to a numeric code. If the text comes from another app, operating system, or is in another language, you may need to specify which encoding was used. For more information on text encoding, refer to :doc:`Understanding Text Encodings`. Both the Read and ReadAll methods take an optional parameter that enables you to specify the encoding. Use the Encodings object to get the desired encoding and pass it as a parameter. For example, the code above has been modified to specify that the incoming text uses the UTF8 encoding, a common standard: .. code:: xojo TextField1.AddText(Me.ReadAll(Encodings.UTF8)) .. _/topics/communication/internet/communicating_via_tcp/ip/writing_data: Writing data ------------ You can send data to the application you are connected to at any time. You send data using the Write method. The data you wish to send must be a string. In this example, the text from a TextField is being sent via a TCPSocket control: .. code:: xojo TCPConnection.Write(TextField1.Text) If you need to send the text to an app that is expecting a specific text encoding, then you should convert the text to that encoding prior to sending it. Use the ConvertEncoding function to do this. Its parameters are the text to be converted and the text encoding to use. For example, the following line sends the text in the TextField using the MacRoman encoding: .. code:: xojo TCPConnection.Write(TextField1.Text.ConvertEncoding(Encodings.MacRoman)) After all your data has been sent, the SendComplete event handler is called. As data is being sent, the SendProgress event handler is called so that you can tell how much data has been sent so far. Never assume how much data is sent between calls to the SendProgress event. It is normal to see this fluctuate. If you are going to be sending small chunks of data across a network (especially a small network), it might make more sense to use the UDPSocket class instead. .. _/topics/communication/internet/communicating_via_tcp/ip/handling_errors: Handling errors --------------- Errors can occur while attempting to connect or while sending or receiving data. Errors are not always what they seem. For example, when the other computer closes the connection, an error is generated. When an error occurs, the Error event handler is executed. Errors are represented by numbers. The LastErrorCode property contains the number of the last error that occurred. See the :doc:`SocketCore` class for a complete list of error numbers. Errors are simply ways to alert your app to conditions it may not have anticipated or be able to anticipate. .. _/topics/communication/internet/communicating_via_tcp/ip/closing_the_connection: Closing the connection ---------------------- When you are finished communicating and wish to disconnect from the other app, you do so by closing the connection. The connection is closed by calling the Close method. Suppose you have a Socket named “TCPConnection” that has established a connection. To close the connection, you can use the following code: .. code:: xojo TCPConnection.Close .. _/topics/communication/internet/communicating_via_tcp/ip/other_socket_information: Other socket information ------------------------ .. _/topics/communication/internet/communicating_via_tcp/ip/destroying_a_socket: Destroying a socket ******************* When you call the Connect or Listen methods your socket's reference count is incremented. This means that the socket does not have to be owned by the anything (such as Window) in order for it to continue functioning. This is helpful in certain circumstances. For example, suppose you write your own socket subclass that implements all of the events for the socket, called MySocket. In the Pressed event for a Button you use the following code: .. code:: xojo Var s As New TCPSocket s.Port = 7000 s.Address = "127.0.0.1" s.Listen Even after this event is completed, the socket stays active listening for connections. In fact, the socket stays active and alive until you specifically close it by either closing the connection locally or remotely. Of course, the socket will also close when your app quits. .. _/topics/communication/internet/communicating_via_tcp/ip/see_also: .. seealso:: :doc:`SSLSocket`, :doc:`TCPSocket` class; :doc:`Understanding Text Encodings` topic ===================== Communicating via UDP ===================== The User Datagram Protocol, or UDP, is the basis for most high speed, highly distributed network traffic. It is a connectionless protocol that has very low overhead, but is not as secure or reliable as TCP. The :doc:`UDPSocket` provides access to this protocol. Like the TCPSocket control, the UDPSocket control is derived from the SocketCore class. In order to use a UDPSocket, it must be bound to a specific port on your machine. Once bound, the UDPSocket is ready for use. It immediately begins accepting any data that it sees on the port that it has bound to. It also allows you to send data out, as well as set UDP socket options. .. _/topics/communication/internet/communicating_via_udp/datagrams: Datagrams --------- In order to differentiate between which machine is sending you what data, a UDPSocket uses a data structure known as a :doc:`Datagram`. A Datagram is a built-in class with two properties, Address, which is the IP address of the remote machine that sent you the data, and Data — the actual data itself. When you attempt to send data out, you must specify information in the form of a Datagram. This information is the remote address of the machine you want to receive your packet, the port it should be sent to, and the data you wish to send to the remote machine. .. _/topics/communication/internet/communicating_via_udp/udpsocket_modes: UDPSocket modes --------------- UDP sockets can operate in various modes which are all very similar, but have vastly different uses. The mode that most resembles a TCP communication is called “unicasting.” This occurs when the IP address you specify when you write data out is that of a single machine. An example would be sending data to www.xojo.com, or some other network address. It is a Datagram that has one intended receiver. This code sends data to a specific machine: .. code:: xojo Var data As New Datagram data.Address = "www.xojo.com" data.Data = "Hello, World!" data.Port = 9500 UDPSocket1.Write(data) The second mode of operation is called “broadcasting”. As the name implies, this is a broadcasted write. It is akin to yelling into a megaphone. Everyone gets the message, whether they want to or not. If the machine happens to be listening on the port you specified, then it will receive the data on that port. This type of send is very network intensive. As you can imagine, broadcasting can create a huge amount of network traffic. The good news is, when you broadcast data out, it does not leave your subnet. Basically, a broadcast send will not leave your local network to travel out into the world. When you want to broadcast data, instead of sending the data to an IP address of a remote machine, you specify the broadcast address for your machine. This address changes from machine to machine, so there is a property of the UDPSocket class that tells you the correct broadcast address. This code broadcasts a message to all machines on your local network: .. code:: xojo Var data As String data = "Hello, World!" ' broadcasting the message UDPSocket1.Write(UDPSocket1.BroadcastAddress, data) The third mode of operation for UDPSockets is “multicasting”. It is a combination of unicasting and broadcasting that is very powerful and practical. Multicasting is a lot like a chat room: you enter the chatroom, and are able to hold conversations with everyone else in the chatroom. When you want to enter the chatroom, you call JoinMulticastGroup, and you only need to specify the group you wish to join. The group parameter is a special kind of IP address, called a Class D IP. They range from 224.0.0.0 to 239.255.255.255. Think of the IP address as the name of the chatroom. If you want to start chatting with two other people, all three of you need to call JoinMulticastGroup with the same Class D IP address specified as the group. When you wish to leave the chatroom, you only need to call LeaveMulticastGroup, and again, specify the group you wish to leave. You can join as many multicast groups as you like, you are not limited to just one at a time. When you wish to send data to the multicast group, you only need to specify the multicast group's IP address. All people who have joined the same group as you will receive the message. .. image:: https://documentation.xojo.com/topics/communication/internet/images/communicating_via_udp_nettank-udp.png This code sends data to members of the multicast group: .. code:: xojo Var data As String data = "Hello, World!" If UDPSocket1.JoinMulticastGroup("224.0.0.0") Then UDPSocket1.Write("224.0.0.0", data) End If .. _/topics/communication/internet/communicating_via_udp/see_also: .. seealso:: :doc:`UDPSocket`, :doc:`Datagram` classes ====================== Accessing web services ====================== Web services are APIs that exist on the web. The most common type are REST (Representational State Transfer) web services which you work with using simple HTTP requests. Also still is use is SOAP (Simple Object Access Protocol) but that is older and not quite as simple to use. With Xojo you an create your own web services or you can use existing services. You can create your own web services by using a web app and the :ref:`WebApplication.HandleURL` event handler. You can use/consume any web service using the :doc:`URLConnection` class. The specifics for how to connect to a web service varies by web service, but typically you will need to authenticate in some manner and the submit data to the web service so you can get results back. On MacOS and iOS you need to be aware of App Transport Security which only allows secure (https) URLs to be used. You can override this with a plist for your app. For more information, refer to the :doc:`Using Non-Secure URLs on macOS and iOS` topic. .. _/topics/communication/internet/accessing_web_services/videos: Videos ------ * `Using the HTTPSocket `_ * `REST Web Services Part 1 `_ * `REST Web Services Part 2 `_ * `Making Database Web Services `_ * `Connecting to 3rd Party Web Services `_ * `Using Slack API `_ * `Connecting to AirTable `_ * `Sending Email `_ .. _/topics/communication/internet/accessing_web_services/blogs: Blogs ----- * `Web Services-Xojo Web At Your Service `_ * `Web Services Part II: Xojo Web At Your Service `_ * `MultiTemp `_ * `Quote Web Service `_ * `Dogs Up `_ * `Sending Notifications with PushOver `_ * `Write a Slackbot in less than 20 lines of code `_ * `Formatting SQL with a Web Service `_ * `Cats Up: Using HTTPSocket with the Cat REST API `_ * `Testing Web Services with RESTy `_ .. _/topics/communication/internet/accessing_web_services/docs: Docs ---- * :doc:`Eddie's Electronics Sample Web Service` * :doc:`URLConnection` .. _/topics/communication/internet/accessing_web_services/open_source: Open source ----------- * `Slack API `_ * `Aloe Express `_ * `Luna: Open-Source REST framework for Xojo `_ * `Pushover `_ .. _/topics/communication/internet/accessing_web_services/see_also: .. seealso:: :doc:`URLConnection` class; :doc:`HTTP (Web) Communication`, :doc:`Using Non-Secure URLs on macOS and iOS` topics ====================================== Using non-secure URLs on macOS and iOS ====================================== Starting with iOS 9 and OS X 10.11, your apps have to use secure "https" connections or you will get this error: "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection". This applies to any control that works with HTTP, including: :doc:`URLConnection`, :doc:`DesktopHTMLViewer`, and :doc:`MobileHTMLViewer`. To continue to connect to non-secure "http" connections that you do not control you'll have to provide a :doc:`plist` with a temporary exception specified for each site you are accessing via http: .. code:: XML NSAppTransportSecurity NSExceptionDomains firstsite.com NSIncludesSubdomains NSTemporaryExceptionAllowsInsecureHTTPLoads secondsite.com NSIncludesSubdomains NSTemporaryExceptionAllowsInsecureHTTPLoads If you don't know the specific sites, you can request access to everything using a single key: .. code:: XML NSAppTransportSecurity NSAllowsArbitraryLoads Apple may reject an App Store submission if the app uses these settings without valid reasons. For more information about this, refer to `NSAppTransportSecurity `_ in Apple's docs. .. _/topics/communication/internet/using_non-secure_urls_on_macos_and_ios/see_also: .. seealso:: :doc:`URLConnection`, :doc:`DesktopHTMLViewer`, :doc:`MobileHTMLViewer` classes =========================== Sending email from your app =========================== When your app needs to send email there are a couple techniques you can use. .. _/topics/communication/internet/sending_email_from_your_app/use_email_client: Use email client ---------------- You can open a new email in the default system Email client by using the mailto: prefix with :ref:`System.GotoURL`. This is the simplest way to open the default Email client with the To: filled in: .. code:: xojo System.GotoURL("mailto:paul@xojo.com") You can also supply a Subject using this format: .. code:: xojo mailto:paul@xojo.com?subject=Xojo Which becomes this Xojo code: ShowURL("mailto:paul@xojo.com?subject=Xojo") You can even add both a Subject and some Body text using this format: .. code:: xojo mailto:paul@xojo.com?subject=Xojo&body=I%20love%20Xojo! You need to use :doc:`EncodeURLComponent` on the subject and body in order for them to be properly received by the Email client. .. _/topics/communication/internet/sending_email_from_your_app/ios_notes: iOS notes ********* On iOS you can use the :doc:`MobileSharingPanel` to allow the user to choose to send the provided text or image as an email. You cannot force the user to use email for this, however. You can also use the ShowURL method on iOS by implementing ShowURL using Declares as shown on the :doc:`iOS Declares page`. .. _/topics/communication/internet/sending_email_from_your_app/using_smtp: Using SMTP ---------- To directly send email, you use the :doc:`SMTPSecureSocket` class. There is also a non-secure :doc:`SMTPSecureSocket` class but most SMTP servers are secured these days so you should just use SMTPSecureSocket. Any SMTP server will work but you will need to configure it properly so that you can connect to it. .. _/topics/communication/internet/sending_email_from_your_app/gmail_configuration: Gmail configuration ******************* Many people have Gmail accounts and you can use the Gmail SMTP server to send emails as long as you do a little initial setup. By default, Gmail has SMTP disabled as they consider SMTP “less secure” than their web interface and proprietary API. You can enable SMTP for Gmail by visiting this page when logged into your Google Account: `Enable SMTP `_ Turn the switch from OFF to ON. With this enabled you can now use Gmail with port 465 and TLS security. Set this up for an SMTPSocket added to the window like this: .. code:: xojo ' Connect to Gmail MailSocket.Address = "smtp.gmail.com" MailSocket.Port = 465 MailSocket.SSLConnectionType = SSLSocket.SSLConnectionTypes.TLSv1 MailSocket.SMTPConnectionMode = SMTPSecureSocket.SMTPConnectionTypes.SSLTLS MailSocket.SSLEnabled = True MailSocket.Username = "YourGmailUsername" MailSocket.Password = "YourGmailPassword" Keep in mind that Gmail's SMTP server is not designed to send bulk email. If you are sending large amounts of email you should instead consider using a bulk email server that is designed for that purpose. .. _/topics/communication/internet/sending_email_from_your_app/sending_a_simple_email: Sending a simple email ---------------------- Before you can send an email, you need to create the email message. You do this using the :doc:`EmailMessage` class. This example creates simple email message and then sends it using an SMTPSocket: .. code:: xojo Var email As New EmailMessage email.FromAddress = "paul@xojo.com" email.Subject = "Xojo" email.BodyPlainText = "I love Xojo!" email.AddRecipient("hello@xojo.com") You then set up your SMTPSocket, add the EmailMessage to it and send it. This code uses the Gmail setup code from above to send the EmailMessage: .. code:: xojo ' Connect to Gmail MailSocket.Address = "smtp.gmail.com" MailSocket.Port = 465 MailSocket.SSLConnectionType = SSLSocket.SSLConnectionTypes.TLSv1 MailSocket.SMTPConnectionType = SMTPSecureSocket.SMTPConnectionTypes.SSLTLS MailSocket.SSLEnabled = True MailSocket.Username = "YourGmailUsername" MailSocket.Password = "YourGmailPassword" MailSocket.Messages.AddRow(email) MailSocket.SendMail The EmailMessage class has even more properties and methods to set HTML message text, headers and CC recipients. You can also add attachments to your email using the :doc:`EmailAttachment` class. .. _/topics/communication/internet/sending_email_from_your_app/sending_email_from_web_apps: Sending email from web apps *************************** Sending Email from web apps works the same as above. However because a web app can have multiple connected users you will want to have a single SMTPSecureSocket that is shared by all of them to prevent your SMTP server from rejecting too many connections. One way to do this is to use a :doc:`Semaphore` to manage the availability of the SMTPSocket. To do this you can initialize both the Semaphore and SMTPSocket in the :doc:`WebApplication.Opening` event. .. code:: xojo ' Semaphore is used to ensure that only one session at a time tries ' to send an email. MailSemaphore = New Semaphore ' Mail Semaphore is a Property: MailSemaphore As Semaphore ' Global socket for sending emails MailSocket = New SMTPSecureSocket ' MailSocket is a Property: MailSocket As SMTPSecureSocket ' Map the socket's error events to methods on the App object AddHandler MailSocket.MailSent, AddressOf MailSentHandler AddHandler MailSocket.ServerError, AddressOf MailServerErrorHandler Here are the method definitions for MailSentHandler and MailServerErrorHandler: .. code:: xojo Public Sub MailSentHandler(m As SMTPSecureSocket) MailSemaphore.Release ' Release the Semaphore to make the socket available for use End Sub Public Sub MailServerErrorHandler(m As SMTPSecureSocket, errorID As Integer, errorMessage As String, email As EmailMessage) MailSemaphore.Release ' Release the Semaphore to make the socket available for use End Sub The only code in them is to release the Semaphore to make the socket available for use once you are done with it. You could also put other code in here for error logging for example. You can then create a method on the App object to send the email. This method lets you pass in the To address, a Subject and Message body: .. code:: xojo Public Sub SendMail(toAddress As String, subject As String, message As String) MailSemaphore.Signal ' Connect to Gmail MailSocket.Address = "smtp.gmail.com" MailSocket.Port = 465 MailSocket.SSLConnectionType = SSLSocket.SSLConnectionTypes.TLSv1 MailSocket.SMTPConnectionType = SMTPSecureSocket.SMTPConnectionTypes.SSLTLS MailSocket.SSLEnabled = True MailSocket.Username = Username MailSocket.Password = Password ' Create EmailMessage Var mail As New EmailMessage mail.FromAddress = "paul@logicalvue.com" mail.AddRecipient(toAddress) mail.Subject = subject mail.BodyPlainText = message mail.Headers.AddHeader("X-Mailer","SMTP Test") ' Send it MailSocket.Messages.AddRow(mail) MailSocket.SendMail End Sub You can see that this code is similar to the earlier code to send an email. The only difference is the addition of the Semaphore Signal method at the beginning. If the MailSocket is currently in use, this will cause the method to wait for the MailSocket to be available. You can then call this method from elsewhere in the web app, perhaps a web page, like this: .. code:: xojo App.SendMail("paul@xojo.com", "Xojo", "I love Xojo!") .. _/topics/communication/internet/sending_email_from_your_app/sending_email_from_ios: Sending email from iOS ********************** You cannot use SMTPSecureSocket with iOS apps. Instead you will want your iOS app to call a web service API (that is using SMTPSecureSocket) to send the email. For example, you can modify the above web project to also handle a web service call. In the web project, Add the HandleURL event to the App object. Put this code in it for it to handle an API call to "/Mail": .. code:: xojo ' Send an email using the provided to address, subject and message If Request.Path = "Mail" Then ' Convert incoming data to JSON Var data As String = Request.Body.DefineEncoding(Encodings.UTF8) Var json As New JSONItem(data) ' Grab the parts we need Var email As String = json.Value("Email") Var subject As String = json.Value("Subject") Var message As String = json.Value("Message") ' Send the email SendMail(email, subject, message, Nil) Request.Status = 200 Return True End If This code call the SendMail that you already created to send the email after getting the Email address, Subject and Message from the JSON that is sent to the web service API. In the iOS project you can call the web service. First put an :doc:`URLConnection` on a :doc:`MobileScreen`, name it eMail and call the web service like this: .. code:: xojo Var mail As New Dictionary mail.Value("Email") = "paul@xojo.com" mail.Value("Subject") = "Xojo" mail.Value("Message") = "I love Xojo!" Var json As String json = GenerateJSON(mail) eMail.SetRequestContent(json, "application/x-www-form-urlencoded") ' Use the IP address of the running web app Email.Send("POST", "http://10.0.1.32:8080/api/Mail%22) .. _/topics/communication/internet/sending_email_from_your_app/receiving_email: Receiving email --------------- To receive email, you use the :doc:`POP3SecureSocket` or :doc:`POP3SecureSocket` classes. POP3 is a protocol for downloading email messages from a server. As messages are received it MessageReceived event handler is called with the EmailMessage provides as a parameter. POP3Socket has many events, properties and methods to allow you to receive POP3 email. Many email services use IMAP instead of POP. If you need IMAP, consider looking into the MonkeyBread Software Plugin. .. _/topics/communication/internet/sending_email_from_your_app/security: Security -------- You should stick to using the :doc:`SMTPSecureSocket` and :doc:`POP3SecureSocket` classes as they allow you to securely connect to their respective servers, which is required most everywhere these days. .. _/topics/communication/internet/sending_email_from_your_app/see_also: .. seealso:: :doc:`EmailAttachment`, :doc:`EmailHeaders`, :doc:`EmailMessage`, :doc:`POP3SecureSocket`, :doc:`POP3SecureSocket`, :doc:`SMTPSecureSocket`, :doc:`SMTPSecureSocket`, :doc:`URLConnection` classes Example Projects: * Example Projects/Communication/Internet/Email Video: .. youtube:: kkhSzYs1RBo ===================== Testing a web service ===================== In order to help you with testing a web service, the Eddie's Electronics web service is available for use with your apps. This web service is hosted on :doc:`Xojo Cloud` as a Xojo web app and uses a SQLite database for its data. .. _/topics/communication/internet/testing_a_web_service/usage: Usage ----- .. _/topics/communication/internet/testing_a_web_service/base_url: Base URL ******** All web services calls are done through the base URL: https://eews-demos.xojo.com .. note:: You cannot call the base URL directly. You must append one of the following API calls to the URL. .. _/topics/communication/internet/testing_a_web_service/getallcustomers: GetAllCustomers *************** HTTP Method: GET or POST This API call returns a JSON document containing the object "GetAllCustomers" which is a key-value store of a customer ID and its data, including FirstName, LastName, City, State and Zip. Sample Xojo code ^^^^^^^^^^^^^^^^ Add a Generic Object to a Window layout. Change its name to EEConnector and its Super to :doc:`URLConnection`. This code sends the web service request using the URLConnection: .. code:: xojo EEConnector.AllowCertificateValidation = False ' Send request to web service Var content As String = EEConnector.SendSync("POST", "https://eews-demos.xojo.com/GetAllCustomers") content = content.DefineEncoding(Encodings.UTF8) Var json As New JSONItem(content) If json.HasName("GetAllCustomers") Then Var customers As JSONItem = json.Value("GetAllCustomers") Var cnt As Integer = customers.Count Var names() As String = customers.Names For Each id As String In customers.Names Var customer As JSONItem = customers.Value(id) ResultsArea.AddText(customer.Value("FirstName") + " " + customer.Value("LastName") + EndOfLine) Next Elseif json.HasName("GetCustomer") Then Var customer As JSONItem = json.Value("GetCustomer") ResultsArea.AddText(customer.Value("FirstName") + " " + customer.Value("LastName") + EndOfLine) End If Sample JSON result ^^^^^^^^^^^^^^^^^^ .. code-block:: json { "GetAllCustomers": { "10174": { "FirstName":"Abdul", "LastName":"Mcconnell", "City":"Colorado Springs", "State":"CO", "Zip":"80935" }, "10179": { "FirstName":"Abel", "LastName":"Alexander", "City":"Arcadia", "State":"IA", "Zip":"51430" } } } .. _/topics/communication/internet/testing_a_web_service/getcustomer: GetCustomer *********** HTTP Method: POST This API call returns all the details for a single customer. You specify the customer by providing a simple JSON document (as request content) containing the ID of the customer you want. Sample Xojo code ^^^^^^^^^^^^^^^^ This code (using the same socket used in the example above) sends a request for a specific customer's data: .. code:: xojo Var cust As New JSONItem cust.Value("ID") = 10179 EEConnector.SetRequestContent(cust.ToString, "application/json") EEConnector.AllowCertificateValidation = False Var content As String = EEConnector.SendSync("POST", "https://eews-demos.xojo.com/GetCustomer") content = content.DefineEncoding(Encodings.UTF8) Var json As New JSONItem(content) If json.HasName("GetAllCustomers") Then Var customers As JSONItem = json.Value("GetCustomer") Var cnt As Integer = customers.Count Var names() As String = customers.Names For Each id As String In customers.Names Var customer As JSONItem = customers.Value(id) ResultsArea.AddText(customer.Value("FirstName") + " " + customer.Value("LastName") + EndOfLine) Next Elseif json.HasName("GetCustomer") Then Var customer As JSONItem = json.Value("GetCustomer") ResultsArea.AddText(customer.Value("FirstName") + " " + customer.Value("LastName") + EndOfLine) End If Sample JSON request ^^^^^^^^^^^^^^^^^^^ This JSON asks for information for customer ID 10179: .. code:: json {"ID": 10179} Sample JSON result ^^^^^^^^^^^^^^^^^^ This is the resulting JSON containing information for customer 10179: .. code:: json { "GetCustomer": { "ID":"10179", "FirstName":"Abel", "LastName":"Alexander", "City":"Arcadia", "State":"IA", "Zip":"51430", "Phone":"1-261-529-7025", "Email":"elit.sed@aliquamarcu.edu", "Photo":"Base64EncodedData", "Taxable":"0" } } .. _/topics/communication/internet/testing_a_web_service/implementation: Implementation -------------- Like all web services made with Xojo, the Eddie's Web Services uses the :ref:`HandleURL` event on :doc:`WebApplication`. The Eddie's WS uses HandleURL to determine which of the above methods was called in the URL to the web service. It then calls out to other methods that get the data from the SQLite database and convert it to JSON. Finally, HandleURL uses the :ref:`WebResponse.Write` method to send the JSON back to the caller. .. _/topics/communication/internet/testing_a_web_service/see_also: .. seealso:: :doc:`Accessing Web Services` topic Custom controls =============== .. toctree:: :maxdepth: 1 :name: sec-custom_controls Changing properties with the Inspector Creating a color selector control Plugins SDK Web Control SDK Web custom controls ====================================== Changing properties with the Inspector ====================================== When creating custom controls, you may want to make some of its properties changeable at design time in the Inspector. To do this, open the contextual menu for the control in the Navigator and select Inspector Behavior. This opens the Inspector Behavior dialog. In this dialog, you can control all the properties that appear in the Inspector. You can add any new public properties on your control and you can also remove existing properties so they do not appear in the Inspector. You will need to scroll to the end to view new properties. Check the box next to a property name and click OK to have the property displayed in the Inspector for the control when it is on a layout. .. image:: https://documentation.xojo.com/topics/custom_controls/images/changing_properties_with_the_inspector_inspector_behavior_for_linklabel_control.png Here are the steps in general: * Create your custom control. * Select the proper Inspector Behavior entries to expose in the IDE. * Drag and drop the custom control onto your layout. * Scroll the Inspector to the appropriate section (usually Behaviors) to see your exposed properties. If you camelcase your property names, the Inspector will insert spaces in the property label for better readability. .. _/topics/custom_controls/changing_properties_with_the_inspector/supported_data_types: Supported data types -------------------- .. csv-table:: :widths: auto Integer types Single Double String Text Color Boolean MenuBar Sound Window Movie Picture Image ColorGroup Enums .. _/topics/custom_controls/changing_properties_with_the_inspector/other_features_of_the_inspector_behavior_dialog: Other features of the Inspector Behavior dialog ----------------------------------------------- The Inspector Behavior dialog allows you to control all aspects of what appears in the Inspector. You can: * Add or remove group headings: Use the “+” or “-” buttons below the list or use the contextual menu. * Change the order of the properties, including moving them to different groups: Drag properties around as needed. * Set or modify default values: Double-click in the Default Value column for the property to set or modify the default value. When specifying the default value for an enumeration, use the enumeration's Integer value. * Change whether a property is shown or hidden: Check the property to display it; uncheck it to hide it. * Add enumerations: Use the Enumerations control on the right to add values that the user can select from a list. .. _/topics/custom_controls/changing_properties_with_the_inspector/see_also: .. seealso:: :doc:`Introduction`, :doc:`Custom Controls` topics ================================= Creating a color selector control ================================= In this short tutorial we will see how to create a Color Selector control from scratch. This one will be very useful when you want to include an option so the user can change the drawing color for a given item in your user interface layout. Through the tutorial we will see how: * Create our own controls based on the Canvas class. * How to use some of the :doc:`Graphic` class methods to do drawing. * How to implement Event Handlers and even redefine them so they are available again from any of the instances based on the class. * How to use some useful :doc:`Color` shared methods so our control automatically react to changes between Dark and Clear modes. .. image:: https://documentation.xojo.com/topics/custom_controls/images/creating_a_color_selector_control_colorselectore.jpg .. _/topics/custom_controls/creating_a_color_selector_control/create_your_own_canvas-based_subclass: Create your own Canvas-based subclass ------------------------------------- Canvas is the class you use to create your own custom drawing in Xojo: from buttons to any other kind of control. For our short tutorial, let's start with creating a new Desktop project. Then, add a new Class from ``Insert > Class``. This item will be added to the **Project Browser** (leftmost area in the IDE). Select the just added class in the Project Browser to access its **Inspector Panel**. There you'll change the following attributes with the given values: * **Name:** ColorSelector * **Super:** Canvas When we create a new class, we have the option to fill-in the ``Super`` field. That means that we enter here an already existing class, then our subclass will inherit and implement all the available properties, methods and events that such class may offer; and that means less code to write! At this point, we know that our subclass (new data type) has the name of **ColorSelector**; and that is based on a Canvas class. .. _/topics/custom_controls/creating_a_color_selector_control/a_property_to_store_the_selected_color: A property to store the selected color -------------------------------------- The main purpose of our subclass is to store the color selected by the user. We'll be using the OS native dialog to do that; so we need to clearly define a Property to store the value (think about it as a special kind of variable that lives on every class instance). With our "ColorSelector" still selected in the Project Browser, add a property from ``Insert > Property``; changing these attributes from the Inspector Panel: * **Name:** SelectedColor * **Type:** Color * **Scope:** Public .. _/topics/custom_controls/creating_a_color_selector_control/selecting_the_color: Selecting the color ------------------- In order to give our users the option to select any color using the native OS dialog, let's add to our ColorSelector class the MouseDown Event. Make sure you still have the ColorSelector item selected in the Project Browser, and then select ``Insert > Event Handler``. The Event Handler panel will appear, offering all the possible Event Handlers you can implement from those inherited from its Super class. Select ``MouseDown`` and confirm your selection. That will bring you into the Code Editor associated with that Event. That it's, the code that will be executed every time the event is raised in response to the user of the control clicking on its area (width and height). Write the following lines in the Code Editor: .. code:: xojo Call Color.SelectedFromDialog(SelectedColor,"") Me.Refresh return RaiseEvent MouseDown(x,y) The first line of code calls the ``Color.SelectedFromDialog`` shared method, passing as its first argument our ``SelectedColor`` property, so it can store into it the color value selected by the user. The magic from this shared method is that you don't need to take care of displaying a propper color selector dialog to the user on every OS, the Xojo Framework takes care of it on all the supported OSes: Windows, macOS and Linux! The second line of code instructs the control to "repainting" itself, because we did something that probably changed its visual representation (in this case, the selected color). What ``return RaiseEvent MouseDown(x,y)`` does it means? Don't forget that we are creating a class … and if our class consumes a given Event Handler that means that it will not be available for the real control instances we will use in our user interface layouts … except that we redefine it. In that case, that line of code will mean something like "ok, call this same event handler on the instance control that is based on me". And, of course, we will do that using the same event handler signature and passing along the expected parameters. .. _/topics/custom_controls/creating_a_color_selector_control/defining_a_new_event_handler: Defining a new Event Handler ---------------------------- Defining a new Event Handler for our class (and, thus, available for all the instances based on that class) is a pretty simple process in Xojo. Make sure that the ColorSelect item is still selected in the Project Browser and, then, choose the option Event Definition from the Insert menu. The previous option will add a new Event Handler Definition into the Class, so we only need to modify the name, parameters and expected returned value from the Inspector Panel. In this case, using these values: * **Event Name:** MouseDown * **Parameters:** X As Integer, Y As Integer * **Return Type:** Boolean As you can see, this is exactly the same Event Handler signature that the one we added to our class! .. _/topics/custom_controls/creating_a_color_selector_control/let's_paint_it!: Let's paint it! --------------- We have everything set up … except displaying to our control user what the current selected color is. We need to add a second Event Handler for that: Paint. The Paint event is where you designate how any Canvas based control will display to the user. For that, you can use any of the drawing methods available under the Graphics class. With them, you can control from the drawing color you need in every piece of the drawing, filling a specific control area using the current drawing color, draw simple or complex polygons, etc. Once added the Pain Event Handler to the ColorSelector class, write the following code in the associated Code Editor: .. code:: xojo g.DrawingColor = If(Color.IsDarkMode,&ccccccc,&c000000) g.DrawRectangle(0,0,g.Width,g.Height) g.DrawingColor = Color.FillColor g.FillRectangle(1,1,g.Width-2,g.Height-2) g.DrawingColor = SelectedColor g.FillRectangle(4,4, g.Width-8, g.Height-8) As you can see, our control drawing is quite simple. The Paint Event Handler receives a **Graphics** object as its first argument, so we can use it to do all the needed drawing. Maybe the most interesting line is the first one, because it decides what drawing color to use if the OS is in Dark Mode or not; and to get informed about that we only need to call the IsDarkMode shared method from the Color class itself. The ``IsDarkMode`` method will return ``True`` if the OS is in Dark Mode and ``False`` if not. That will be the color we will use to draw the external marquee of our control, so it looks right both in Dark and Clear modes. We also use other convenience shared method from the Color class (``FillColor``) so we can safely fill our control background using the systemwide configured color for that, even when the OS changes between Dark and Clear modes. Finally, we assign our ``SelectedColor`` color value to the ``DrawingColor`` property of the "g" (Graphics) variable, and simply fill a rectangle using that color. This will be the visual clue to the user about the current selected color. .. _/topics/custom_controls/creating_a_color_selector_control/putting_our_colorselector_to_work: Putting our colorselector to work --------------------------------- Is time to test our ColorSelect class! Select the Window1 item in the Project Browser. That will bring-in the Layout Editor for that window. Now, in order to add a new instance (control) of our class, we only need to drag the ColorSelector item from the Project Browser and drop it into the Layout Editor. That's all! Run the project and click on the ColorSelector control. It will display the panel letting you to select any desired color and, as soon you close that panel, the control will redraw itself to display the selected color. Of course, when you need to use the selected color in your code you just need to read the SelectedColor property from the instance. for example, something like: .. code:: xojo Var CurrentColor as Color = ColorSelector1.SelectedColor =========== Plugins SDK =========== The Plugins SDK (software development kit) is for using C/C++ to make your own plugin functions and controls for use with Xojo. Plugins can be created for use by all project types. You find the full Plugin SDK documentation located in you Xojo installation here: Extras/PluginsSDK The folder contains code and documentation in the following folders: * Documentation: Contains the SDK documentation in HTML format. * Examples: Example projects illustrating how to make Xojo plug-ins. * Glue Code: C++ code you must add to your plug-in projects. * Includes: Included by the glue code (and most likely by your own plug-in code as well). There is also a Plugins channel in the forum to discuss the creation of Xojo plugins: `Xojo Forum Plugins SDK Channel `_ =============== Web Control SDK =============== The Web Control SDK (software development kit) describes how to create your own custom controls or integrate 3rd party web controls into Xojo. .. _/topics/custom_controls/web_control_sdk/overview: Overview -------- The Web Control SDK documentation and example projects are included with your Xojo installation: Extras/WebSDK .. _/topics/custom_controls/web_control_sdk/basic_concepts: Basic concepts ************** Creating Web Controls in Xojo is a give and take relationship that requires careful planning for optimal execution. Read each section of this guide so that you have a good working knowledge of how communications with 3rd party controls works. .. _/topics/custom_controls/web_control_sdk/namespaces: Namespaces ********** For the purposes of avoiding conflicts between framework code, your code and anything else that you might put in your application, you must use a namespace for your controls. The root namespace for custom controls is XojoCustom, which you should use for all your JavaScript code. Refer to the Namespaces section for more information. To request a root namespace registration, send an email to WebNamespace@xojo.com and tell us what root namespace you would like to use. You will receive an email from us confirming your namespace choice. .. _/topics/custom_controls/web_control_sdk/control_naming_conventions: Control naming conventions ************************** To prevent name conflicts and to make it easier to identify in lists, you should prefix your classes. For example, a company named Acme might name their controls Acme_Button or Acme_SourceList. .. _/topics/custom_controls/web_control_sdk/event_order: Event order *********** Understanding the initial sequence of events that fire when your control is created and then added to a web page is crucial for successful deployment and the Xojo developer experience. For more information about the Events that are available to you and the order in which they fire, please see the Events section. =================== Web custom controls =================== To best way to create custom web controls is to use the :doc:`Web Control SDK`. But for some some things you may find it sufficient to subclass built-in controls to modify their behavior. .. _/topics/custom_controls/web_custom_controls/audio_player: Audio Player ------------ HTML5 has a built-in tag to play audio. You can use this with a :doc:`Web HTML Viewer` to create a control that can play audio from a web URL. Here are the steps: 1. Create a new class and set its Super to WebHTMLViewer and change its name to WebAudioPlayer. #. Add a String Constant to WebAudioPlayer, call it kAudioTag. It contains this HTML code to play audio: .. code:: XML 1. The rest of the code will be to substitute the URLs in this HTML. #. Add a private property, **mMP3URL As String**. #. Add a computed property, **MP3URL As String**. In the Set block for this property, add this code to substitute the provided URL into the HTML: .. code:: xojo mMP3URL = value Var tag As String = kAudioTag tag = tag.Replace("%MP3URL%", value) Self.LoadHTML(tag) 1. Add the Opening event handler with this code to initialize things: .. code:: xojo Self.URL = "" Me.Width = 300 Me.Height = 60 1. That's it for the WebAudioPlayer control. Now you are ready to use it on a page. #. Drag this control from the Navigator onto WebPage1. Add the Shown event and put this code to assign a URL of an audio file: .. code:: xojo Me.MP3URL = "http://www.archive.org/download//Stockfinster.-DeadLinesutemos025/01_Push_Push.mp3" Run the project. You'll see the audio player appear where the WebAudioPlayer control is. Click the play button to play the song. .. _/topics/custom_controls/web_custom_controls/combo_box: Combo Box --------- This shows you how you can combine two separate controls to create a new control. A Combo Box is a control that consists of a Text Field to type a value and a Popup Menu to choose from existing values. To implement this, you can use a :doc:`Web Container`. Here are the steps: 1. Add a Web Container to the project. #. Add a Web Popup Menu to the container. Set its width to 120 and its name to ComboPopup. #. Add a Web Text Field and place it directly on top of the Web Popup Menu. Align it on the left and set its width to 100. This allows the Popup Menu arrow to still appear. Set its name to ComboField. #. Add the SelectionChanged event to ComboPopup and use this code to make sure it gets focus and to set the selected value to be the text of ComboField: .. code:: xojo ComboField.SetFocus ComboField.Text = Me.Text 1. Now we want to add methods to the container to make it easy to add and remove values from the Popup Menu. #. Add a AddRow method and have it call ComboPopup.AddRow: .. code:: xojo Public Sub AddRow(value As String) ComboPopup.AddRow(value) End Sub 1. Add a RemoveAllRows method and have it call ComboPopup.RemoveAllRows: .. code:: xojo Public Sub RemoveAllRows() ComboPopup.RemoveAllRows End Sub 1. Now you are ready to use the control on a web page. Drag WebComboBox from the Navigator onto WebPage1 and add its Opening event with this code to add some rows: .. code:: xojo Me.AddRow("Maine") Me.AddRow("New Hampshire") Me.AddRow("Vermont") Run the project and you'll be able to type in the field or select something from the menu and have it fill in the field. .. _/topics/custom_controls/web_custom_controls/inspector_behavior: Inspector Behavior dialog ------------------------- .. image:: https://documentation.xojo.com/topics/custom_controls/images/web_custom_controls_inspector_behavior_for_audio_player_control.png When creating custom controls, you may want to make some of its properties changeable at design time in the Inspector. Using the AudioPlayer example above, it would be better if you could set the MP3URL property in the Inspector for the control on the layout rather than having to put code in the Opening event. You use the :doc:`Changing Properties With the Inspector` feature to change what is displayed in the Inspector. To use it, open the contextual menu for AudioPlayer in the Navigator and select Inspector Behavior. This opens the Inspector Behavior dialog. In this dialog, you can control all the properties that appear in the Inspector, including any new ones that you add. If you scroll down, you will see the MP3URL parameter. Check the box next to its name and click OK to have the property displayed in the Inspector for the control when it is on a layout. Now you can specify the MP3URL in the Inspector and remove code that manually sets it. Here are the steps in general: 1. Create your custom control. #. Select the proper Inspector Behavior entries to expose in the IDE. #. Drag and drop the custom control onto your layout. #. Scroll the inspector to the appropriate section (usually Behaviors) to see your exposed properties. .. _/topics/custom_controls/web_custom_controls/other_features_of_the_inspector_behavior_dialog: Other features of the Inspector Behavior dialog *********************************************** The Inspector Behavior dialog allows you to control all aspects of what appears in the Inspector. You can: * Add or remove group headings: Use the “+” or “-” buttons below the list or use the contextual menu. * Change the order of the properties, including moving them to different groups: Drag properties around as needed. * Set or modify default values: Double-click in the Default Value column for the property to set or modify the default value. * Change whether a property is shown or hidden: Check the property to display it; uncheck it to hide it. * Add enumerations: Use the Enumerations control on the right to add values that the user can select from a list. .. _/topics/custom_controls/web_custom_controls/see_also: .. seealso:: :doc:`Web Control SDK`, :doc:`Changing Properties With the Inspector` topics Data processing =============== .. toctree:: :maxdepth: 1 :name: sec-data_processing Faster processing using the Worker class ======================================== Faster processing using the Worker class ======================================== A :doc:`Worker` is a special object that you can add to your project. Code in the Worker's JobRun event runs as a separate process in a Console application to allow multiple CPU cores to be used. When your project is Run in debug mode, Worker uses threads to simulate separate console app processes to help simplify debugging. So when debugging you will **not see any benefit of using multiple cores** since Xojo threads are limited to a single CPU core. Worker can be used with Desktop projects. .. _/topics/data_processing/faster_processing_using_the_worker_class/worker_class: Worker class ------------ .. _/topics/data_processing/faster_processing_using_the_worker_class/events: Events ****** .. csv-table:: :header: "Name", "Description" :widths: 20, 50 "JobCompleted(jobResult As String)","Called when JobRun completes and contains the results returned by JobRun." "JobProgressed(progressData As String)","Called by SendProgress() method to provide information about the progress of the job." "JobRequested() As String","Return something that indicates the job to process. This information is supplied as the parameter to JobRun. You should avoid passing around large chunks of data as that will not be memory efficient. If you have large data (pictures, data, databases, etc.) then pass along information on how to get the data using other means such as file access or connecting to the database and fetching what is needed." "JobRun(jobData As String) As String","When a job is completed, the JobCompleted event is called passing along the results returned from JobRun." "Error(jobData As String)","If there is an error, the job data is provided here so you can determine which job caused the error." .. _/topics/data_processing/faster_processing_using_the_worker_class/properties: Properties ********** .. csv-table:: :header: "Name", "Description" :widths: 20, 50 "CorePercent As Integer","The the maximum number of cores to use expressed as a percent of total CPU cores." "MaximumCoreCount As Integer","The maximum number of cores to use. Regardless of the percent, this is the most CPU cores that will be used. This should be 1 or greater." "ProjectItemsToInclude As String (Design-Time)","List of Project Items to include in the Worker Console app. No classes or modules are included by default. Enter one project item per line. To include all classes and modules, enter ``IncludeAllClassesAndModules``. In addition you can use the :doc:`Compatibility Flags` to mark individual methods or properties so that they are not included in the Worker Helper. For example, to exclude a method, select the method and click the Advanced tab in its Inspector and turn off the checkboxes next to Console. This will prevent the method from being included in the Worker Helper. You may find this useful if you have classes or modules that have some methods that use desktop features (for example, :ref:`FolderItem.ShowOpenFileDialog`) but you don't want to refactor everything out to separate project items." .. _/topics/data_processing/faster_processing_using_the_worker_class/methods: Methods ******* .. csv-table:: :header: "Name", "Description" :widths: 20, 50 "Start","Starts the worker by creating the separate helpers based on your core settings." "Stop","Stops the worker and all its helpers." "SendJobCompleted(jobResult As String)","Manually call JobCompleted event sending along the result in the parameter." "SendProgress(progressData As String)","Calls the JobProgressed event sending along the data in the parameter. This should only be called from the JobRun event (or methods it calls from the event)." .. _/topics/data_processing/faster_processing_using_the_worker_class/usage: Usage ----- * Add a Worker to your project using Insert (menu or toolbar) and name it as necessary. * Add the events. You will need JobRequested and JobRun at a minimum. You'll probably also want JobCompleted to know when jobs are finished. * Start your worker. For example: Worker1.Start .. _/topics/data_processing/faster_processing_using_the_worker_class/notes: Notes ----- The code in the JobRun events runs separately in each Worker Helpers (Console app). This means that this code (and any code that it calls) has to be Console-safe and cannot make use of Desktop commands or features. If you use these, you will get compile errors. The other events all run on the desktop app. This distinction is important. For example, if you create a property on your Worker (TestValue for example) and assign it a value in the JobRequested event (which ran in the desktop app), you will find that that property value is not set when that property is accessed from the JobRun event (which runs in the Helper console app). All data to be passed between the Worker in the desktop app and any of its Worker Helpers must use the parameters and return values of the events. This also applies to project constants. For example, if you have a constant on your main App, that constant is not available to the Worker. Instead put the constant on the Worker itself. .. _/topics/data_processing/faster_processing_using_the_worker_class/excluding_project_items_from_worker_helpers: Excluding project items from Worker helpers ******************************************* By default, all modules and non-UI classes are copied into your Worker Helper so they can be used. However, if any of those classes refers to Desktop commands or features you will get compile errors. You can choose to exclude entire project items by listing them in the Project Items to Include property in the Inspector for the Worker. Enter one project item per line. For nested modules and classes, list each module and class separately on its own line. In addition you can use the :doc:`Compatibility Flags` to mark individual methods or properties so that they are not included in the Worker Helper. For example, to exclude a method, select the method and click the Advanced tab in its Inspector and turn off the checkboxes next to Console. This will prevent the method from being included in the Worker Helper. You may find this useful if you have classes or modules that have some methods that use desktop features (for example, :ref:`FolderItem.ShowOpenFileDialog`) but you don't want to refactor everything out to separate project items. .. _/topics/data_processing/faster_processing_using_the_worker_class/handling_errors: Handling errors *************** A variety of things can cause an error, including: * A Worker Helper losing connection with its Worker in the main app. This can happen if the main app crashes, quits or is force-quit. * An unhandled exception in a Worker Helper. * A Worker losing connection with one of its Helpers. This can happen if the Worker Helper crashes or is force-quit for some reason. When an error occurs, the Error event in the Worker is called. The parameter has details about the job that was being processed when the error occurs. When an error occurs in a Worker's Helper, the Helper app quits. You can restart Worker Helpers by calling the Worker.Start method. This method will restart any Worker instances that have stopped normally or because of an error and they will call JobRequested. If you find that a Worker Helper is failing repeatedly for the same JobData then you should probably remove that JobData (and perhaps log that it was not processed) so that it is not sent out when a job is requested. Be sure you are catching possible exceptions in your JobRun code. To help diagnose errors in Worker Helper apps, try using :ref:`System.DebugLog` to write information to the system log. Since Worker Helpers run as separate console apps (when built) they do not retain any state from the main app. If you have to do activation, such as with plugins, then you will also need to ensure the Activate code is called by JobRun. .. _/topics/data_processing/faster_processing_using_the_worker_class/see_also: .. seealso:: :doc:`Worker` class Databases ========= .. toctree:: :maxdepth: 1 :name: sec-databases Getting started accessing databases Connecting to a database Database basics for beginners Adding, updating and deleting rows Transactions Supported engines Protecting your database from attack Considerations when using a database with a web application DBKit: An easy way to quickly connect a database to your user interface =================================== Getting started accessing databases =================================== Whether you have never worked with a database or are a seasoned professional, building applications that communicate with a database is easy in Xojo. .. _/topics/databases/getting_started_accessing_databases/database_beginners: Database Beginners ------------------ If you're entirely new to databases or specifically SQL databases, you'll want to learn the :doc:`basic database terminology` and then understanding how to connect to a database and how to :doc:`add, update and delete rows`. Creating a database ------------------- If you don't already have a database to which you wish to connect, you'll need to create one. Creating a database is not something that Xojo does and it very much depends on which database you are using. `TablePlus `_ makes it easy to create databases for all the :doc:`database engines that Xojo supports` and runs on Linux, macOS, Windows and even iOS. .. _/topics/databases/getting_started_accessing_databases/connecting_to_a_database: Connecting to a database ------------------------ The easiest way to connect to a database is using the :doc:`DatabaseConnection` object as this requires no code. You can also connect to a database via code using the :ref:`Connect` method of the :doc:`Database` class. Making all or nothing updates to a database ------------------------------------------- Database engines allow you to make changes to multiple rows including adding new rows and deleting others and decide afterwards whether or not you wish to keep these changes or discard them. This is accomplished through something called a :doc:`Transaction`. Next steps ---------- Once you understand the basics, have created a database and have a connection to your database, the next step is to start using it in your project. Some additional items to explore are: .. csv-table:: :header: "Item", "Description" :widths: auto ":doc:`DBKit`", "A set of classes that make it easy to build database applications." ":doc:`MySQLCommunityServer`", "The class used to access a database on a MySQL and MariaDB database servers." ":doc:`ODBCDatabase`", "The class used to access a database via an ODBC driver. This will allow you to connect to databases that Xojo does not natively support." ":doc:`PostgreSQLDatabase`", "The class used to access a database on a PostgreSQL database server." ":doc:`SQLiteDatabase`", "The class used to access a SQLite database." ":doc:`Database`", "The parent class of all the provided database classes." .. tip:: If you need to host a database on the Internet, :doc:`Xojo Cloud` provides support for MySQL, PostgresSQL and SQlite. .. _/topics/databases/getting_started_accessing_databases/more_resources: More Resources -------------- There are videos available that explain details about specific database engines. * `Using SQLite `_ * `SQLite `_ * `PostgreSQL `_ * `Designing Delightful Databases `_ * `Database Development `_ .. _/topics/databases/getting_started_accessing_databases/example_projects: Example projects ---------------- There are several database example projects included with Xojo. Choose File > New Project, click on Examples in the New Project dialog box and then explore the Databases folder. ======================== Connecting to a Database ======================== The first step is to connect to the database you wish to use. This is done with Xojo's DatabaseConnection object: 1. Create a new project in Xojo or open the project you've created in which you wish to connect to a database. 2. Choose Insert > Database Connection and then choose the database type to which you wish to connect. 3. A new Database Connection item will appear in your project's Navigator. Its default name will based upon the database type you choose. You can of course rename it in the Inspector. 4. In the Navigator, expand the Database Connection item. .. image:: https://documentation.xojo.com/topics/databases/images/getting_started_navigator.png :scale: 50% :align: center Database connection stages ^^^^^^^^^^^^^^^^^^^^^^^^^^ The Database Connection has four connections, one for each stage of your applications development. This provides the ability to have a different database for use with each stage. For example, you may have a database you personally use while developing your app (Development), another for use by your beta testers (Beta) and yet another for use by the ultimate end users when once your app is shipping (Final). You can of course use just one for all stages. That's entirely up to you. By default, Xojo will automatically switch to the connection for the appropriate stage when you change the Stage Code of your project by going to Build Settings in the Navigator, clicking on Shared Settings and changing the Stage Code in the Inspector. If you do not want your app to switch automatically, click on the Database Connection you created, then in the Inspector change the Stage from Automatic to whichever stage you want it to always use. To configure a connection for a stage: 1. Click on the stage (Development for example). 2. In the Inspector, you then provide the information your app will need to connect to the database. This depends on the type of database. If it's a SQLite database, use the File property to select the SQLite database file you will be using. If it's MySQL, PostgreSQL or ODBC, you will need to provide the IP address, UserName, Password and other information needed to make the connection. .. image:: https://documentation.xojo.com/topics/databases/images/getting_started_inspector.png :scale: 50% :align: center 3. Click the Connect button in the Database Viewer's toolbar to view the tables and columns of your database. .. image:: https://documentation.xojo.com/topics/databases/images/getting_started_table_viewer.png :scale: 25% :align: center In the code of your project, you can now access the database using the name of the database connection in your project's Navigator. For example, say you added a connection to a SQLite database and did not change the default connection name. In your application's Opening event, you could make sure you're connected to your database like this: .. code:: xojo If SQLiteDatabase1.IsConnected then MessageBox("You are connected to the database.") Else MessageBox("Something went wrong. You're not connected to the database.") End If .. note:: Database Connection is not currently supported for Android. .. _/topics/databases/connecting_to_a_databases/sqlite_databaseconnection_when_building_your_app: SQLite DatabaseConnection when building your app ------------------------------------------------ When you use a SQLite DatabaseConnection in your project and you build your app, Xojo will copy your SQLite database into the Resources directory/folder of the built app. You should treat this copy of the database file as read only as applications are often in read-only locations on the end user's computer. If you are planning to store a copy of the database on the user's computer for either read-only or read-write access, you will need to decide where the copy will be stored. This of course depends on whether the user is going to have a single copy of the database or multiple copies where they are treated more like documents. If it's a single copy, at the point when you want to connect to the database (usually when the app is launched), you can use :doc:`SpecialFolder` to determine if the database file can be found at the location you are expecting to find it. The ApplicationData folder (which can be accessed via SpecialFolder) is a common location. Ideally, your app should create a folder with your app's name so that you have a unique folder in which to store the database and potentially other files. An example is provided that demonstrates this. In Xojo, go to the example projects and then open the "Installing a SQLite Database" in the Databases folder. .. _/topics/databases/connecting_to_a_databases/how_do_i_connect_to_a_database_server_from_a_mobile_app?: How do I connect to a database server from a mobile app? -------------------------------------------------------- Most desktop and web apps maintain a connection to the database server while the application is running. On both Android and iOS, if a connection to any server continues too long, the OS will disconnect it as it will quickly drain the battery. Additionally, due to the nature of mobile devices being mobile, Internet connectivity can be somewhat unreliable making continuous connections impractical. However, you can still connect to a database server! You just have to do it with a web service. The general concept is that you would create a web service and companion API (using Xojo or another tool) that connects to the database server. The mobile app connects to the web service (using :doc:`URLConnection`). To learn more, watch :doc:`this video` about accessing web services. ============================= Database basics for beginners ============================= The techniques described in the :doc:`File Management` topic are fine for dealing with your app data in many cases. But there will be times when you need something faster and more structured. That's where a database becomes useful. A database is a structured way of organizing data that make it easier to find the data later. Databases consist of tables, columns and data. The most common type of databases used with Xojo are **relational databases**, of which Xojo has built-in support for :doc:`SQLite`, :doc:`PostgreSQL` and :doc:`MySQL`. In addition you can use :doc:`ODBC` to connect to just about any other database. Other types of databases include **NoSQL** databases. These are not discussed here, but there are open-source Xojo projects that provide support for several including `Redis `_ and `MongoDB `_. .. _/topics/databases/database_basics_for_beginners/tables,_columns_and_data: Tables, columns and data ------------------------ A relational database has its data stored in tables. A table is a container for some common set of data. A table has one or more columns that define the data it may contain. Data is stored within the table as rows, with a value for each column. Each row is a single set of data. For example, you could have a table called Team that has these columns: ID, Name, Coach, City. Here is an example of the Team table with sample data. .. csv-table:: :header: ID, Name, Coach, City :widths: auto 1, Seagulls, Mike, Albany 2, Pigeons, Mark, Springfield 3, Crows, Matt, Houston The ID column is required by most databases to ensure that each row in the table is uniquely identifiable. This unique column is also often called the Primary Key. .. _/topics/databases/database_basics_for_beginners/relationships: Relationships ------------- A database usually consists of many tables. And these tables are often related in some way, which is why these are called relational databases. For example, to track the players for each team you would have another table, called Player, that is related to the Team table. Here is an example of a Player table with sample data: .. csv-table:: :header: ID, TeamID, Name, Position :widths: auto 1, 1, Bob, 1B 2, 1, Tom, 2B 3, 2, Bill, 1B 4, 2, Tim, 2B 5, 3, Ben, 1B 6, 3, Ty, 2B Notice that the Player table has its own ID column as well. But in addition, it also has a TeamID column. This column defines the relationship between Player and Team. It clearly tells you the team to which the player belongs. For example, you can see that Bob and Tom both have TeamID = 1. Looking in the Team table from above, you can see that the team with ID = 1 is the Seagulls. So both Bob and Tom are on the Seagulls team. You can look up the teams for the other players using the same technique. The collection of your tables is called the "database schema" or "database design". .. _/topics/databases/database_basics_for_beginners/database_engines: Database engines ---------------- A database design as described above is implemented using a “database engine”. This is the specific database product that is used to store your database design and its associated data. Xojo has built-in support for several database engines, which are usually just referred to as the database: SQLite, PostgreSQL, MySQL and ODBC. You are not limited to the built-in database support, however. You can also use many other databases using ODBC, 3rd party libraries, plugins or components. There are generally two types of databases that you will use: embedded and server. .. _/topics/databases/database_basics_for_beginners/embedded_databases: Embedded databases ****************** An embedded database is a database where the engine is built into your app with the database usually in a file or series of files. You do not need to install any other software in order to access the database; the necessary software is embedded in your app. Embedded databases usually only allow a single user to access the data at one time because the engine is embedded in the app and there would be no way for multiple apps to coordinate access to a shared database file. SQLite is one of the most popular databases in the world and is an example of an embedded database. This type of database is often used in apps such as Mail clients, web browsers, photo management software and anything else that needs to manage a lot of data but does not need to share it with others. You can also use an embedded database such as SQLite with web apps that have light to medium usage. .. _/topics/databases/database_basics_for_beginners/server_databases: Server databases **************** Server databases are more powerful databases that often have many advanced features, the most significant of which is multi-user access. A server database allows multiple users to access the database at one time. Examples of server databases include: PostgreSQL, MySQL and ODBC. Other advanced features associated with server databases: stored procedures, backups, user login, permissions and scalability. A server database is usually installed on its own dedicated server. Your app communicates with this server, either directly for internal apps running on a LAN or using a web service as an intermediary for apps running on the internet. Database servers are used by apps that need to share data among multiple users such as a payroll system, billing system or other business apps. Database servers are often used by large-scale enterprise apps and any other types of app that have multiple users or large numbers of users. .. _/topics/databases/database_basics_for_beginners/sql_(structured_query_language): SQL (Structured Query Language) ------------------------------- Database engines all share a similar command structure, which is called SQL, which stands for Structured Query Language and is often pronounced as "sequel". SQL is the way to send commands to the database to do things such as create tables or get data from tables. For simple SQL commands, SQL is often the same across different databases. But as you create more complex SQL, you'll find that each database has their own slightly different variant of SQL. This can make it challenging to switch between database engines (such as SQLite and PostgreSQL, for example) because the SQL used to send commands to the database may be different. Regardless, there are several common SQL commands that are always available, even if their specific syntax changes slightly depending on the database you are using. These commands are: SELECT, INSERT, UPDATE, DELETE, COMMIT and ROLLBACK. This section shows some example of generic SQL that ought to work across most databases. When you write SQL, as you will see below, it often ends in a semi-colon (;) character which indicates the "end of the SQL command". This is optional with some databases, but a semi-colon will be used in code samples for consistency. To learn more about general SQL, visit the `W3Schools SQL Tutorial `_. .. _/topics/databases/database_basics_for_beginners/select: SELECT ****** The SELECT command is used to retrieve specific data from one or more tables. Using the Team tables from above, this command gets the names of all the teams: .. code:: SQL SELECT Name FROM Team; A SELECT command can also use a WHERE clause to limit the data retrieved from the table. This command fetches the data for only the team in Albany: .. code:: SQL SELECT * FROM Team WHERE City = 'Albany'; You can combine data from multiple tables by "joining" them together. This command gets the name of all the players on the Pigeons by combing both the Team and Player tables: .. code:: SQL SELECT Name FROM Player INNER JOIN Team ON Team.ID = Player.TeamID Where Team.Name = 'Pigeons'; With Xojo, you send SQL commands to the database by using the SelectSQL method of the :doc:`Database` class. The results are stored in the :doc:`RowSet` class, which you can then loop through to get at the data. This code gets the team names and adds them to an array, which you could then use to display: .. code:: xojo ' db is a database to which you are connected Var rows As RowSet Try rows = db.SelectSQL("SELECT * FROM Team;") Catch error As DatabaseException 'An error occurred so deal with it here 'The error message can be found in error.Message Return End Try Var teams() As String Var teamName As String For Each row As DatabaseRow In rows teamName = row.Column("Name").StringValue teams.Add(teamName) ' Some database may require you to specify the encoding of the data you received. ' The code below sets the encoding as UTF-8. ' teams.Add(teamName.DefineEncoding(Encodings.UTF8)) Next .. _/topics/databases/database_basics_for_beginners/insert: INSERT ****** The INSERT command is used to add rows to a table. Using the Team table, this command adds the Seagulls team: .. code:: SQL INSERT INTO Team (Name, Coach, City) VALUES ('Seagulls', 'Mike', 'New York'); In Xojo, you send an INSERT command using the ExecuteSQL method of the Database class: .. code:: xojo Var sql As String = "INSERT INTO Team (Name, Coach, City) VALUES ('Seagulls', 'Mike', 'New York');" db.ExecuteSQL(sql) .. _/topics/databases/database_basics_for_beginners/update: UPDATE ****** The UPDATE command is used to modify existing rows in a table. This code changes the name of the Coach for the Seagulls in the Team table: .. code:: SQL UPDATE Team SET Coach = 'Mary' WHERE Team = 'Seagulls'; Generally, you can only update values in a single table at a time. You can however update multiple values in the table at once: .. code:: SQL UPDATE Team SET Coach = 'Mary', City = 'Boston' WHERE Team = 'Seagulls'; In Xojo, you send an UPDATE command using the ExecuteSQL method of the Database class: .. code:: xojo Var sql As String = "UPDATE Team SET Coach = 'Mary// WHERE Team = 'Seagulls';" db.ExecuteSQL(sql) .. _/topics/databases/database_basics_for_beginners/delete: DELETE ****** The DELETE command removes rows from a table. This example removes the Seagulls row from the Team table: .. code:: SQL DELETE FROM Team WHERE Name = 'Seagulls'; .. warning:: Be careful to ensure that your WHERE clause is present and accurate. In the above example, if you did not include it, then all the rows in Team table would be deleted! .. _/topics/databases/database_basics_for_beginners/commit,_rollback_(transactions): COMMIT, ROLLBACK (Transactions) ******************************* Changes to a database are made in what is called a transaction. This is a block of processing that either all completes successfully or none completes successfully. When you are using a transaction, changes made to a database are not made permanent until you **commit**. This serves several purposes. First, it ensures that data integrity is always maintained. If an error occurs partway through some changes to several tables, you do not want the data to be missing in some tables. A failure allows you to revert everything back to its initial state, sort of like an Undo. Second, for databases that can have multiple users, changes made in a transaction are not usually visible to other users until the transaction is marked as completed (by committing). This prevents people from seeing data before it is ready. Lastly, using transactions can improve performance when adding or changing data in the database as the commit operation can be time consuming so if you do it less often (by only committing data that has been added in large chunks) you can improve performance. Starting a transaction varies depending on the database you are using. Some databases start a transaction for you automatically and some require you to use a specific command, such as: .. code:: SQL BEGIN TRANSACTION; In Xojo code, you would use the ExecuteSQL method of the Database class to send this command: .. code:: xojo db.ExecuteSQL("BEGIN TRANSACTION;") Regardless, to complete a transaction you use the COMMIT command. To cancel (or undo) a transaction you use the ROLLBACK command. In Xojo, Commit and Rollback are methods on the Database class. You can them like this: .. code:: xojo db.CommitTransaction db.Rollback To learn more about transactions, refer to the :doc:`Transactions` topic. .. _/topics/databases/database_basics_for_beginners/see_also: .. seealso:: :doc:`Adding, updating and deleting rows`, :doc:`Transactions` topics ================================== Adding, updating and deleting rows ================================== Once you have connected to a database (refer to the previous sections for the database you are using), the process of doing common database operations is nearly always the same regardless of the database engine you are using. Typically you need to do these actions: * Retrieve data * Add data * Change data * Delete data The SQL for these operations was briefly described in :doc:`Database Basics for beginners` and this topic has more details. .. _/topics/databases/adding,_updating_and_deleting_rows/retrieve_data: Retrieve data ------------- .. _/topics/databases/adding,_updating_and_deleting_rows/rowset: RowSet ****** The :doc:`RowSet` class is used to retrieve data from a database. You supply the SQL SELECT command to get data from one or more tables and then use RowSet to loop through the results. This code gets the name of all the teams and adds them to an array: .. code:: xojo Var teams() As String Var rs As RowSet Try rs = db.SelectSQL("SELECT * FROM Team;") For Each Row As DatabaseRow In rs teams.AddRow(rs.Column("Name").StringValue) Next rs.Close Catch error As DatabaseException MessageBox(error.Message) End Try The SelectSQL method returns a :doc:`RowSet`. You should always use a :doc:`Try..Catch` statement to check for errors when using SelectSQL. A :doc:`DatabaseException` could be because of a database error or something as simple as a typo in your SELECT statement. The For Each loop iterates through the rows in the RowSet until you reach the end. The Column method is used to get the :doc:`DatabaseColumn` for the column. This contains a column value for the current row in the :doc:`RowSet`, in this case the Name column. The :doc:`Column` method is used to get column values based on the column name. You can also use the :ref:`ColumnAt` method to get column values based on the position of the column in the SELECT statement. .. _/topics/databases/adding,_updating_and_deleting_rows/sql: SQL *** Since you use SelectSQL to get :doc:`RowSets`, your SQL mostly consists of SELECT statements. The syntax for SELECT statements is generally like this: .. code:: SQL SELECT column1, column2 FROM table WHERE column = value ORDER BY sortColumn; While the syntax of SELECT statements is similar across databases, there can be some variation depending on the specific database being used. Be sure to refer to the documentation for the database you are using to learn about its SELECT statement. You can just supply the SQL directly as a string (as done in the code above), which works fine for desktop apps. But you do not want to do that with web apps. Because of a hacking technique called :doc:`Protecting your database from attack`, you instead want to make use of Prepared SQL Statements to make your SQL more secure. .. _/topics/databases/adding,_updating_and_deleting_rows/prepared_sql_statements_and_database_binding: Prepared SQL statements and database binding ******************************************** All of the database class support prepared statements that work generally the same way. A Prepared SQL Statement passes the SQL and its arguments separately to the database which then binds them together to create an SQL statement that cannot be affected by SQL Injection. This code shows how you would get the names of all the players on the Seagulls team without using Prepared SQL Statements: .. code:: xojo Var sql As String sql = "SELECT * FROM Player WHERE Team = 'Seagulls';" Var rs As RowSet rs = db.SelectSQL(sql) With a Prepared SQL Statement, you instead only supply a placeholder for the'seagulls' parameter value (usually a "?", but it varies depending on the database you are using). The actuae'seagulls' value is supplied later: .. code:: xojo Var sql As String = "SELECT * FROM Player WHERE Team = ?;" Var rs As RowSet rs = db.SQLSelect(sql, "Seagulls") As you can see, it takes slightly more thought to create but this is worthwhile because it is much safer than just using straight SQL. The replacement character (usually a "?" but it varies by database type) can only be used to replace a single value. For usage with an "IN" clause or with other statements that take multiple values, you'll need to provide the appropriate number of replacement characters. For example, to use with an "IN" clause: .. code:: xojo Var sql As String = "SELECT * FROM Player WHERE Team IN (?, ?);" Var rs As RowSet rs = db.SelectSQL(sql, "Seagulls", "Pigeons") You can also use Prepared Statements with INSERT and UPDATE commands. .. _/topics/databases/adding,_updating_and_deleting_rows/add_data: Add data -------- You can add new data to your database using two different methods. You can use the :doc:`DatabaseRow` class to add new rows to the database. Or you can directly use the SQL INSERT statement. .. _/topics/databases/adding,_updating_and_deleting_rows/databaserow: DatabaseRow *********** The DatabaseRow class is used to add new rows to a specific table. Use the various "Column" methods on the class to assign values to the columns. Then call the :ref:`Database.AddRow` method of the Database class to insert the record into the specified table. This code adds a team to the Team table: .. code:: xojo Var row As New DatabaseRow row.Column("Name") = "Seagulls" row.Column("Coach") = "Mike" row.Column("City") = "Albandy" Try db.AddRow("Team", row) Catch error As DatabaseException MessageBox(error.Message) End Try :doc:`DatabaseRow` works on any database and does not require changes should you change your project to use a different database engine. .. _/topics/databases/adding,_updating_and_deleting_rows/insert_sql: INSERT SQL ********** You can also create the SQL for the INSERT statement manually, but you need to use the correct INSERT syntax for the database you are using. Generally speaking, INSERT syntax looks like this: .. code:: SQL INSERT INTO table (column1, column2) VALUES (value1, value2); You can build up this INSERT command using string concatenation. This code adds a team to the Team table: .. code:: xojo Var tableName = "Team" Var teamName = "Seagulls" Var coachName = "Mike" Var cityName = "Albany" Var insertSQL As String insertSQL = "INSERT INTO " + tableName + "(" + _ "Name, Coach, City) VALUES ('" + _ teamName + "', '" + coachName + "'," + cityName + _ ");" But you should really use a prepared SQL statement for better results, more security and simpler code. This is how the above code looks for SQLite using a prepared statement: .. code:: xojo Var tableName = "Team" Var teamName = "Seagulls" Var coachName = "Mike" Var cityName = "Albany" Var insertSQL As String insertSQL = "INSERT INTO " + tableName + "(Name, Coach, City) VALUES (?, ?, ?);" Try db.ExecuteSQL(insertSQL, teamName, coachName, cityName) Catch error As DatabaseException MessageBox(error.Message) End Try As you can see, this is simpler to read because you don't have as much string concatenation code to manage. It's also less error-prone as you won't have to worry about making sure you get all the quotes correct. .. _/topics/databases/adding,_updating_and_deleting_rows/change_data: Change data ----------- You can change data in two ways. The first way is to directly modify the data in a :doc:`RowSet` that your retrieved from a SELECT. Or you can directly use the SQL UPDATE command. .. _/topics/databases/adding,_updating_and_deleting_rows/edit_and_updating_a_rowset: Edit and updating a RowSet ************************** You can choose to edit the current row of a :doc:`RowSet` and then save those changes back to the database. You do this by calling the :ref:`RowSet.EditRow` method. This makes the current row editable so that you can change any of the column values. After you have changed the values, you can update the database with the changes. This code gets a :doc:`RowSet` with the values for the Pigeons team and then changes the team name to the Eagles: .. code:: xojo Var rs As RowSet Try rs = db.SelectSQL("SELECT * FROM Team WHERE Name = 'Pigeons';") rs.EditRow rs.Column("Name").StringValue = "Eagles" rs.SaveRow rs.Close Catch error As DatabaseException MessageBox(error.Message) End Try Note that if you use the :ref:`RowSet.MoveToPreviousRow` method to go back in the :doc:`RowSet`, the value it contains will be what you have changed. The RowSet will still contain the original values. Another restriction is that MySQL will reset the :doc:`RowSet` when you call the :ref:`RowSet.SaveRow` method. For these reasons, most of the time you will only use :ref:`RowSet.EditRow` and :ref:`RowSet.SaveRow` with :doc:`RowSets` that have a single row. .. _/topics/databases/adding,_updating_and_deleting_rows/update_sql: UPDATE SQL ********** Of course, you can also use direct SQL for this. Like with other SQL, you need to use the UPDATE command and match the syntax required by your database. Generally, it looks like this: .. code:: SQL UPDATE table SET column1 = value1 WHERE column2 = value2; You use ExecuteSQL to run the UPDATE command. This code changes the team named "Pigeons" to "Eagles": .. code:: xojo Var updateSQL As String = "UPDATE Team SET Name = 'Eagles' WHERE Name = 'Pigeons';" Try db.ExecuteSQL(updateSQL) Catch error As DatabaseException MessageBox(error.Message) End Try .. _/topics/databases/adding,_updating_and_deleting_rows/delete_data: Delete data ----------- You can delete data from a table in two ways. If you have a :doc:`RowSet` you can use its :ref:`RemoveRow` method, or you can directly use the DELETE SQL command. .. _/topics/databases/adding,_updating_and_deleting_rows/delete_using_a_rowset: Delete using a RowSet ********************* You can delete the current row in a RowSet by calling the :ref:`RowSet.RemoveRow` method: .. code:: xojo rs.RemoveRow .. _/topics/databases/adding,_updating_and_deleting_rows/delete_sql: DELETE SQL ********** The SQL for deleting data is relatively simple: .. code:: SQL DELETE FROM table WHERE column = value; You use ExecuteSQL to delete data: .. code:: xojo Var deleteSQL As String = "DELETE FROM Team WHERE Name = 'Seagulls';" Try db.ExecuteSQL(deleteSQL) Catch error As DatabaseException MessageBox(error.Message) End Try It is important that you always include the "WHERE" clause when deleting data. If you forget it then you will end up deleting **all** the rows in the table! .. _/topics/databases/adding,_updating_and_deleting_rows/error_handling: Error handling -------------- As you may have noticed in many of the code samples, proper error handling is essential when dealing with databases. Without error handling, you will have no way to know if your database commands completed successfully. An error might result in invalid data and possible app crashes. Always use the :doc:`Try..Catch` statement so you can check a :doc:`DatabaseException` after every set of database commands. If a :doc:`DatabaseException` occurs, then the Message property of the :doc:`DatabaseException` contains a description of the error, which you should display or log. .. code:: xojo Try db.ExecuteSQL("DELETE FROM Team WHERE Name = 'Seagulls';") Catch error As DatabaseException System.DebugLog(error.Message) MessageBox(error.Message) End Try .. _/topics/databases/adding,_updating_and_deleting_rows/database_schema_information: Database schema information --------------------------- The database schema is the definition of the tables, it columns, indexing and other things related to how the database is implemented. At times it can be useful to get this information directly from the database. The :doc:`Database` class has three methods to return this information: :ref:`Tables`, :ref:`TableColumns` and :ref:`TableIndexes`. .. _/topics/databases/adding,_updating_and_deleting_rows/tables_method: Tables method ************* The :ref:`Tables` method of the :doc:`Database` class returns a :doc:`RowSet` with one column containing the names of all the tables in the database. This code adds the table names to a ListBox: .. code:: xojo Var tables As RowSet = db.Tables If tables <> Nil Then While Not tables.AfterLastRow ListBox1.AddRow(tables.ColumnAt(1).StringValue) tables.MoveToNextRow Wend tables.Close End If .. _/topics/databases/adding,_updating_and_deleting_rows/tablecolumns: TableColumns ************ Similarly, :ref:`TableColumns` returns a :doc:`RowSet` with information for all the columns (fields) on the specified table. The :doc:`RowSet` results can vary depending on the database, but these columns are typically available: ColumnName, FieldType, IsPrimary, NotNull and Length. This code displays the information for each column in the Team table in a ListBox: .. code:: xojo Var columns As RowSet = db.TableColumns("Team") If columns <> Nil Then While Not columns.AfterLastRow ListBox1.AddRow(columns.ColumnAt(1).StringValue, columns.ColumnAt(2).StringValue, columns.ColumnAt(3).StringValue, columns.ColumnAt(4).StringValue, columns.ColumnAt(5).StringValue) columns.MoveToNextRow Wend columns.Close End If Note: FieldType is an Integer that describes the data type of the column. Refer to the Data Types section below for a table that maps the integer to a data type. .. _/topics/databases/adding,_updating_and_deleting_rows/tableindexes: TableIndexes ************ :ref:`TableIndexes` returns a :doc:`RowSet` with the name of the indexes on a table. The :doc:`RowSet` has one column, the name of the index. .. _/topics/databases/adding,_updating_and_deleting_rows/data_types: Data types ********** Each database has its own set of data types that it uses. These types are identified as an Integer in the :ref:`Database.TableColumns` FieldType column. These integer values are also used when defining your own data source for use with Reports. The following table is a complete list of data types and their corresponding values. Note that not all databases use every data type. .. csv-table:: :header: "Column Data Type", "Value" :widths: auto "Null","0" "Byte","1" "SmallInt","2" "Integer","3" "Char","4" "Text / VarChar","5" "Float","6" "Double","7" "Date","8" "Time","9" "TimeStamp","10" "Currency","11" "Boolean","12" "Decimal","13" "Binary","14" "Long Text / BLOB","15" "Long VarBinary / BLOB","16" "String","18" "Int64","19" "Unknown","255" .. _/topics/databases/adding,_updating_and_deleting_rows/see_also: .. seealso:: :doc:`Database`, :doc:`DatabaseColumn`, :doc:`DatabaseRow`, :doc:`RowSet` classes; :doc:`Database Basics for beginners` topic ============ Transactions ============ Database transactions were briefly covered in the Database Concepts section. But when do you actually need to use database transactions? The answer is "it depends". A transaction is a collection of changes to the database. It can be a single change, such as an INSERT statement, or it can be many changes such as a combination of many INSERT and UPDATE statements. When changes are made within a transaction, they are generally not permanent (and not visible to other connections looking at the database) until you commit them. In order to use transactions effectively, you have to understand how they work with the database engine you are using. Some databases start a transaction for you automatically. And some will also automatically commit for you after each change so you don't have to actually commit manually. Some databases let you use a "read uncommitted" mode where data that has not been committed can be viewed by other connections. This is usually a special case use for specific performance reasons. .. _/topics/databases/transactions/data_integrity: Data integrity -------------- Generally, you do not want to commit after every database change as this can cause your data to become inaccurate should an error occur with updates, as seen by the following Banking example. .. _/topics/databases/transactions/banking_example: Banking example *************** Suppose you want to transfer $10 from a savings account to a checking account. Here are the starting amounts: .. csv-table:: :header: "Account", "Amount" :widths: auto Savings, $100 Checking, $50 If you remove $10 from savings and commit, you have a database that looks like this: .. csv-table:: :header: "Account", "Amount" :widths: auto Savings, $90 Checking, $50 There is now $10 in limbo. The next step is to add the $10 to checking and commit resulting in this: .. csv-table:: :header: "Account", "Amount" :widths: auto Savings, $90 Checking, $60 This is how every works when nothing goes wrong. But having that $10 in limbo is a big gamble. If your app or database crashes before it can do the last step, you end up with the $10 being lost and your database integrity compromised. Transactions prevent this problem. Look at the example again. Here are the starting amounts: .. csv-table:: :header: "Account", "Amount" :widths: auto Savings, $100 Checking, $50 You start a transaction and you remove $10 from savings. This is the same as before except you did not commit the change here so the data is not final just yet. .. csv-table:: :header: "Account", "Amount" :widths: auto Savings, $90 Checking, $50 Now you can add $10 to checking, again without committing to get this as before: .. csv-table:: :header: "Account", "Amount" :widths: auto Savings, $90 Checking, $60 As the final step, you can now commit these changes. This tells the database to apply the changes in one single step (the transaction). Because you changed the values in a single transaction, the data was never permanently in the database in its intermediate form. If something bad happened before you were able to commit (or even when trying to commit) then the database transaction would be been cancelled (called a **rollback**) and the data would have remained in its original state with $100 in savings and $50 in checking. This is how transactions give you database integrity. .. _/topics/databases/transactions/data_isolation: Data isolation -------------- As mentioned above, by using a transaction you prevent the database from making your changes permanent until they have all completed successfully. This is important for data integrity. But if you are using a multiuser database it is also important for data isolation purposes. When multiple users are connected to the database, you always want them to see the data in its current state. Data that is in the middle of a being changed is not really current until the changes are completed. So you do not want user A to see changes that user B is in the process of making. By using transactions you prevent this problematic situation from occurring. .. _/topics/databases/transactions/performance: Performance ----------- If you commit after every database change, you force your database to do a lot of work behind the scenes to make the data permanently available. This is not a big problem when dealing with small amounts of data, but it can really add up when dealing with lots of data. For example, if you are importing thousands of rows of data into a specific table, committing after each row could cause your import to take several minutes because of all the overhead. Switching to a transaction that only commits at the end (or even just every 1000 rows) could result in a tremendous improvement. It's possible for times to drop from several minutes down to several seconds when using a transaction in this manner. Transactions give you better performance. .. _/topics/databases/transactions/using_transactions: Using Transactions ------------------ So now you know that transactions are often the way to go, how do you use them? Unfortunately, that varies depending on the database engine. With SQLite (:doc:`SQLiteDatabase`), if you do not explicitly start a transaction, then your command is put in an implicit transaction which essentially means that each database change is made permanent for you automatically. As I've shown you above, this may not be what you want. With SQLite, you use the "BEGIN TRANSACTION" SQL command to start a transaction. Other database engines have similar commands. When you are finished with the transaction and want to make the changes permanent, you send the "COMMIT" command. Or if you need to cancel the transaction, you send the "ROLLBACK" command. Xojo code for the Banking Example might look like this: .. code:: xojo ' Assuming there is a property called db that is ' connected to a SQLite database and defined as: ' db As SQLiteDatabase db.ExecuteSQL("BEGIN TRANSACTION;") ' Withdraw 10 Try db.ExecuteSQL("UPDATE Savings SET Type = 'Withdrawal', " + _ "Amount = -10 WHERE AccountNum = '123456';" Catch error As DatabaseException MessageBox(error.Message) db.Rollback Return End Try ' Deposit 10 Try db.ExecuteSQL("UPDATE Checking SET Type = 'Deposit', " + _ "Amount = 10 WHERE AccountNum = '123456';") db.CommitTransaction Catch error As DatabaseException MessageBox(error.Message) db.Rollback Return End Try .. _/topics/databases/transactions/see_also: .. seealso:: :doc:`Database Basics for beginners` topic Supported Engines ================= .. rst-class:: forsearch Database .. rst-class:: forsearch MySQL .. rst-class:: forsearch ODBC .. rst-class:: forsearch SQLite .. toctree:: :maxdepth: 1 :name: sec-supported_engines MySQL ODBC PostgreSQL SQLite Other Databases ===== MySQL ===== .. rst-class:: forsearch MySQL MySQL is a powerful, cross-platform, open-source database server. To use it, you need to ensure the MySQLCommunityServerPlugin file is in the Plugins folder (it is there by default). If you have previously deleted it, you can find it in the Extras > Database Plugins Resources folder. The plugin supports connecting to MySQL Community Edition from Windows, Mac and Linux. You can connect to MySQL from Desktop, Web and Console projects. .. note:: Connecting from a mobile app is not supported as holding a connection open is generally not supported by iOS itself due to the constant energy usage that would produce. You can learn more about MySQL at their web site: `www.MySQL.com `_] .. _/topics/databases/supported_engines/mysql/licensing: Licensing --------- MySQL's licensing is considerably more complex than licensing for other database servers. For more information about its licensing options, refer to their web site: `MySQL Licensing `_ .. _/topics/databases/supported_engines/mysql/connecting_to_mysql: Connecting to MySQL ------------------- To connect to MySQL, you need to have a MySQL server installed on either your computer or an accessible server. You 'll need to know several things about this installation, including: * The Host IP address or name * The Port being used (usually 3306) * The name of the database on the server * The username to use to connect to the server * The password to use to connect to the server With this information, you can connect to the database on the server using the MySQLCommunityServer class: .. code:: xojo Var db As New MySQLCommunityServer db.Host = "192.168.1.172" db.Port = 3306 db.DatabaseName = "BaseballLeague" db.UserName = "broberts" db.Password = "streborb" Try db.Connect ' Use the database Catch error As DatabaseException ' Connection error MessageBox(error.Message) End Try .. _/topics/databases/supported_engines/mysql/secure_connections: Secure connections ****************** You can also connect to a MySQL database using SSL for a secure connection. You do this using the SSLMode property (and optionally other SSL properties) to specify the type of secure connection to use: .. code:: xojo Var db As New MySQLCommunityServer db.Host = "192.168.1.172" db.Port = 3306 db.DatabaseName = "BaseballLeague" db.UserName = "broberts" db.Password = "streborb" db.SSLMode = True Var keyFile As FolderItem keyFile = New FolderItem("MySQLKeyFile") db.SSLKey = keyFile Var certFile As FolderItem certFile = New FolderItem("MySQLCertificateFile") db.SSLCertificate = certFile Var authFile As FolderItem authFile = New FolderItem("MySQLAuthFileFile") db.SSLAuthority = authFile Var authPath As FolderItem authPath = New FolderItem("SSLCACertFile") db.SSLAuthorityFolder = authPath Var cipher As String cipher = "DHE-RSA-AES256-SHA" db.SSLCipher = cipher Try db.Connect ' Use the database Catch error As DatabaseException MessageBox(error.Message) End Try .. _/topics/databases/supported_engines/mysql/creating_a_table: Creating a table ---------------- This SQL creates the Team table used in previous examples: .. code:: SQL CREATE TABLE Team (ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, Name TEXT, Coach TEXT, City TEXT); In place of the TEXT data type, which allows an unlimited length string, you might also use the VARCHAR data type which allows you to specify a maximum size for the string: .. code:: SQL CREATE TABLE Team (ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, Name VARCHAR(100), Coach VARCHAR(100), City VARCHAR(100)); .. _/topics/databases/supported_engines/mysql/auto-incrementing_primary_keys: Auto-incrementing primary keys ------------------------------ If a table has the AUTO_INCREMENT attribute assigned to a primary key, then that column auto-increments when a row is added to the table. When you INSERT data into a table with a primary key, you omit the primary key from the INSERT SQL: .. code:: SQL INSERT INTO Team (Name) VALUES ('Seagulls'); After adding a row to the database, you can get the value of the last primary key value by calling the GetInsertID method: .. code:: xojo Var lastValue As Integer lastValue = db.LastInsertedRowID .. _/topics/databases/supported_engines/mysql/see_also: .. seealso:: :doc:`MySQLCommunityServer` class; :doc:`Database Basics for beginners`, :doc:`Adding, updating and deleting rows` topics ==== ODBC ==== .. rst-class:: forsearch ODBC ODBC (Open Database Connectivity) is a database driver standard available on Windows, Mac and Linux. Using the ODBC plugin you can connect to any database for which you have an ODBC driver. ODBC drivers are available for almost any database. To use ODBC, you need to copy/move the ODBCPlugin file from the Extras > Database Plugin Resources folder to the Plugins folder. You can use ODBC with Desktop, Web and Console projects. .. note:: Connecting from a mobile app is not supported as holding a connection open is generally not supported by iOS itself due to the constant energy usage that would produce. ODBC can be a great way to connect to databases that do not have a built-in Xojo database class, such as: * :doc:`Microsoft Access` * :doc:`Visual FoxPro` * :doc:`FileMaker` * Firebird * IBM iSeries / DB 2 .. _/topics/databases/supported_engines/odbc/connecting_to_a_database_using_odbc: Connecting to a Database using ODBC ----------------------------------- .. image:: https://documentation.xojo.com/topics/databases/supported_engines/images/odbc_mac_odbc_administrator.png How you connect to an ODBC database depends on the database you are using. The first thing you need to do is configure the ODBC driver using the appropriate ODBC configuration tool for your operating system. On Windows you use the ODBC Data Source Administrator in the Control Panel or Settings. On Mac, you use the ODBC Administrator app in the Application/Utilities folder. In this tool you install the ODBC driver and enter the necessary credentials to connect to the database. On Linux, you may have to edit configuration files. Consulting the docs for your Linux distribution. This results in a DSN (Data Source Name). You can use the DSN to connect to the database in conjunction with the ODBCDatabase class in two ways. You can have your app prompt the user to choose a DSN using the operating system browser for selecting a DSN or you can supply the name of the DSN manually. This code supplies a blank DataSource property, which prompts the user for the DSN using the system dialog: .. code:: xojo Var db As New ODBCDatabase db.DataSource = "" Try db.Connect ' Use the database Catch error As DatabaseException ' Connection error ' or the user selected ' Cancel from the ODBC browser End Try .. youtube:: r3aKqKywvis Alternatively, you can provide a DSN name and supply credentials. This example uses the existing TestDSN to connect to the database: .. code:: xojo Var db As New ODBCDatabase db.DataSource = "TestDSN" db.UserName = "broberts" db.Password = "streborb" Try db.Connect ' Use the database Catch error As DatabaseException ' Connection error End Try Lastly, if you know the precise format used by the ODBC driver, you can create the DSN manually: .. code:: xojo Var db As New ODBCDatabase db.DataSource = "DSN=TestDSN;UID=broberts;PWD=streborb" Try db.Connect ' Use the database Catch error As DatabaseException ' Connection error End Try .. _/topics/databases/supported_engines/odbc/sources_for_odbc_drivers: Sources for ODBC drivers ------------------------ Before you can connect to any database using ODBC, you will need to obtain an ODBC driver. Many database vendors make ODBC drivers available for free. Other sources include: * `Actual Technologies `_ * `OpenLink `_ * `DataDirect `_ .. _/topics/databases/supported_engines/odbc/using_firebird: Using Firebird -------------- `Firebird `_ is a completely free (cost and license), cross-platform database with some interesting features. Although Xojo does not have a native plugin for connecting to Firebird, you can use the ODBC plugin in conjunction with the Firebird ODBC drivers to use Firebird with Xojo. Firebird can be used two ways, as a database server or as an embedded database. This shows you how to use both using Firebird running on Windows. .. _/topics/databases/supported_engines/odbc/connect_to_the_server: Connect to the server ********************* .. image:: https://documentation.xojo.com/topics/databases/supported_engines/images/odbc_firebird_odbc_server_setup.png Once you download and install Firebird (which should only take about a minute on Windows 10 using the default settings), you can then get the ODBC drivers, which have to be downloaded separately. Note, there are only ODBC drivers available for Windows and Linux on the Firebird site. You can find updated drivers (not free, but they have a 30-day trial), including ones for Mac from `devart `_. After installing the ODBC drivers, you can go to the ODBC Control Panel and add a data source by selecting the Firebird/Interbase driver from the list. For the DSN name, use “FirebirdServerTest”. Use this value for the Database property: .. code:: xojo 10.0.1.16:C:Program FilesFirebirdFirebird_2_5examplesempbuildemployee.fdb Use “SYSDBA” for the Database Account and “masterkey” as the password. You don't need to change any other settings. Use the “Test connection” button in the window to verify that you can connect. .. _/topics/databases/supported_engines/odbc/connect_directly_to_a_database_file: Connect directly to a database file *********************************** .. image:: https://documentation.xojo.com/topics/databases/supported_engines/images/odbc_firebird_odbc_local_file_setup.png If you did not already install the ODBC drivers from above, you will need to do so now. After installing the ODBC drivers, you can go to the ODBC Control Panel and add a data source by selecting the Firebird/Interbase driver from the list. For the DSN name, use “FirebirdEmbeddedTest”. Firebird includes a sample database, which should be located here: C:\\Program Files\\Firebird\\Firebird_3_0\\examples\\empbuild\\EMPLOYEE.FDB Copy this file to the Desktop. Use “SYSDBA” for the Database Account and “masterkey” as the password. Click the “Test Connection” button to verify that everything is correct. If so, you are now ready to connect using Xojo. .. _/topics/databases/supported_engines/odbc/connecting_using_xojo: Connecting using Xojo ********************* Now that you have created your ODBC DSN entries, you can use this with the Xojo ODBC plugin to connect to the database. The code to do this is similar to most Xojo database code. I 'm going to have a single method that is used to connect using the specified DSN. This method is called ConnectToFirebird(dsn As String): .. code:: xojo Var db As New ODBCDatabase db.DataSource = dsn Try db.Connect ListBox1.RemoveAllRows Var SQL As String = "SELECT emp_no, full_name, job_code, job_country FROM employee;" Var rs As RowSet rs = db.SelectSQL(SQL) If rs <> Nil Then For Each row As DatabaseRow In rs ListBox1.AddRow(rs.Colum("full_name").StringValue) Next rs.Close End If Catch error As DatabaseException ' a database error occurred End Try .. image:: https://documentation.xojo.com/topics/databases/supported_engines/images/odbc_firebird_example_data.png This method connects to the sample database and adds all the names in the employee table to a ListBox. You can call this method and supply the name of the DSN to use to connect. Add a button to test the embedded connection and add this code to its Pressed event handler: .. code:: xojo ConnectToFirebird("FirebirdEmbeddedTest") Now add a button to test the server connection and add this code to its Pressed event handler: .. code:: xojo ConnectToFirebird("FirebirdServerTest") Run the app and click the buttons to see the ListBox get populated. .. _/topics/databases/supported_engines/odbc/learning_more: Learning more ************* Now that you know how to use Firebird, you can spend some time with `its documentation `_ to learn more about it. .. _/topics/databases/supported_engines/odbc/see_also: .. seealso:: :doc:`ODBCDatabase`, :doc:`ODBCPreparedStatement` classes; :doc:`ODBCConstant` module ========== PostgreSQL ========== .. rst-class:: forsearch PostgreSQL PostgreSQL is a free, powerful, cross-platform, open-source database server. To use it, you need to make sure the PostgreSQLPlugin file is installed in the Plugins folder. If you have deleted it, you can find it in the Extras > Database Plugin Resources folder. You can connect to PostgreSQL from Desktop, Web and Console projects. .. note:: Connecting from a mobile app is not supported as holding a connection open is generally not supported by iOS itself due to the constant energy usage that would produce. You can learn more about PostgreSQL at its official web site: `www.PostgreSQL.org `_ .. _/topics/databases/supported_engines/postgresql/connecting_to_postgresql: Connecting to PostgreSQL ------------------------ To connect to PostgreSQL, you need to have a PostgreSQL server installed on either your computer or an accessible server. You 'll need to know several things about this installation, including: * The Host IP address or name * The Port being used (usually 5432) * The name of the database on the server * The username to use to connect to the server * The password to use to connect to the server With this information, you can connect to the database on the server using the :doc:`PostgreSQLDatabase` class: .. code:: xojo Var db As New PostgreSQLDatabase db.Host = "192.168.1.172" db.Port = 5432 db.DatabaseName = "BaseballLeague" db.UserName = "broberts" db.Password = "streborb" db.AppName = "MyApp" Try db.Connect ' Use the database Catch error As DatabaseException ' DB Connection error MessageBox(error.Message) End Try .. _/topics/databases/supported_engines/postgresql/secure_connections: Secure connections ****************** You can also connect to a PostgreSQL database using SSL for a secure connection. You do this using the SSLMode property to specify the type of secure connection to use: .. code:: xojo Var db As New PostgreSQLDatabase db.Host = "192.168.1.172" db.SSLMode = PostgreSQLDatabase.SSLRequire db.Port = 5432 db.DatabaseName = "BaseballLeague" db.UserName = "broberts" db.Password = "streborb" db.AppName = "MyApp" If db.Connect Then ' Use the database End If .. youtube:: 3zNeePbHn48 .. _/topics/databases/supported_engines/postgresql/creating_a_table: Creating a table ---------------- This SQL creates the Team table used in previous examples: .. code:: SQL CREATE TABLE Team (ID INTEGER NOT NULL PRIMARY KEY, Name TEXT, Coach TEXT, City TEXT); In place of the TEXT data type, which allows an unlimited length string, you might also use the VARCHAR data type which allows you to specify a maximum size for the string: .. code:: SQL CREATE TABLE Team (ID INTEGER NOT NULL, Name VARCHAR(100), Coach VARCHAR(100), City VARCHAR(100)); .. _/topics/databases/supported_engines/postgresql/case_sensitivity: Case sensitivity ---------------- PostgreSQL converts your SQL to lowercase by default. So take a look at this SQL: .. code:: SQL SELECT FullName FROM Person; This gets converted to: .. code:: SQL SELECT FullName FROM Person; The only time case is maintained is when you use quotes ("") around the names in the SQL statement. It is best to avoid using quotes in this manner since you'll be forced to use the quotes everywhere so that the case-sensitivity is maintained. .. _/topics/databases/supported_engines/postgresql/auto-incrementing_primary_keys: Auto-incrementing primary keys ------------------------------ PostgreSQL does not allow you to create a primary key that auto-increments. But the equivalent functionality is available by using Sequences. A Sequence is a database object that manages unique values for use by primary keys. You use the sequence when you create new rows in a table. This SQL declares a sequence for the Team table with values starting at 1: .. code:: SQL CREATE SEQUENCE TeamSeq START 1; You use the Sequence in INSERT SQL statements like this: .. code:: SQL INSERT INTO Team (ID, Name) VALUES (nextval('TeamSeq'), 'Seagulls'); The nextval, lastval and currval functions are used to access the next value in the sequence, the last value in the sequence and the current value of the sequence respectively. .. _/topics/databases/supported_engines/postgresql/large_objects: Large objects ------------- Large Objects, or BLOBS, allow you to store non-traditional data in the database such as files, pictures and anything that is binary. Large Objects are stored independently of tables and are referenced using their own unique identifier. To work with BLOBs in PostgreSQL, you use the PostgreSQLLargeObject class. This example saves a file to a LargeObject: .. code:: xojo db.SQLExecute("BEGIN TRANSACTION;") ' Create the Large Object and save its reference Var oid As Integer oid = db.CreateLargeObject ' Open the newly created Large Object Var lo As PostgreSQLLargeObject lo = db.OpenLargeObject(oid) ' Write the file to the Large Object Var bs As BinaryStream bs.Open(inputFile) Var data As String While Not bs.EndOfFile data = bs.Read(1000) lo.Write(data) Wend bs.Close lo.Close db.SQLExecute("END TRANSACTION;") PostgreSQL requires that all large object operations be performed inside of a transaction as shown in the above example. .. _/topics/databases/supported_engines/postgresql/notifications: Notifications ------------- Another feature of PostgreSQL is the Listen and Notify protocol. With Listen and Notify, you can have the PostgreSQL Server generate a notification for events that an app can listen for. To listen for notifications, call the Listen method with the name of the notification to listen for: .. code:: xojo db.Listen("LogInNotification") To actually ask the PostgreSQL Server if any of the notifications being listened for have arrived, call the CheckForNotifications method. Typically you want to do this with a timer so that your app checks for notifications regularly: .. code:: xojo db.CheckForNotifications When actual notifications arrive, the ReceivedNotificationEvent is called with the name of the notification. Send notifications using the Notify method like this: .. code:: xojo db.Notify("LogInNotification") .. _/topics/databases/supported_engines/postgresql/additional_information: |beginnosearch| Additional information ---------------------- For more information about PostgreSQL: * `Video: PostgreSQL `_ * In the IDE, choose New Project > Examples > Databases > Supported Engines > PostgreSQL * Official Site: `www.postgresql.org `_ * Official Docs: `www.postgresql.org/docs `_ * `Postgres.app `_ |endnosearch| .. _/topics/databases/supported_engines/postgresql/see_also: .. seealso:: :doc:`PostgreSQLDatabase` class; :doc:`Database Basics for beginners`, :doc:`Adding, updating and deleting rows` topics SQLite ====== .. toctree:: :maxdepth: 1 :name: sec-sqlite Column Renaming and Deleting Overview Full Text Searching Write-Ahead Logging SQLite Basics SQLiteDatabase for Beginners SQLiteDatabase for Beginners Quiz Answers SQLite Basics Quiz Answers Using SQLite ============================ Column renaming and deleting ============================ SQLite 3.25 and later added the ability for the ALTER table command to rename a column. This version of SQLite is available starting with Xojo 2018r4. Using ALTER TABLE makes renaming a column very easy. The syntax is as you might expect: .. code:: SQL ALTER TABLE MyTable RENAME COLUMN OldColumn TO NewColumn; In Xojo code you send this command to the database using the ExecuteSQL method. Here's an example that changes a column name: .. code:: xojo Var SQL As String = "ALTER TABLE Team RENAME COLUMN Coach To HeadCoach;" DB.ExecuteSQL(SQL) If you need to delete a column you will want to use the technique described below. .. _/topics/databases/supported_engines/sqlite/column_renaming_and_deleting/sqlite_3.24.x_and_earlier: SQLite 3.24.x and earlier ------------------------- In earlier versions of SQLite, the ALTER TABLE command cannot be used to rename an existing column on a table. The workaround is to create a new table with the columns named the way you want and then copy the data from the old table to the new table. For example, suppose you created a table with this SQL: .. code:: SQL CREATE TABLE team(Name TEXT, Coach TEXT, City TEXT); You later realize that the City column ought to instead be called Location. These are the steps you can do: 1. Rename the original table with this SQL: .. code:: SQL ALTER TABLE team RENAME TO team_orig; 1. Create the replacement table with the original name and corrected column name with this SQL: .. code:: SQL CREATE TABLE team(Name TEXT, Coach TEXT, Location TEXT); #Copy the data from the original table to the new table with this SQL: .. code:: SQL INSERT INTO team(Name, Coach, Location) SELECT Name, Coach, City FROM team_orig; 1. Drop the original table with this SQL: .. code:: SQL DROP TABLE team_orig; With these four steps you can manually change any SQLite table. Keep in mind that you will also need to recreate any indexes, viewers or triggers on the new table as well. You can also use the above steps to remove a column from a table as SQLite has no ability to do that. In this case you would create the new table without the column you want to delete and then don't copy that column's data over to it. Typically you will run the above commands using your favorite SQL tool. But you can also send the commands from Xojo by using the Database.ExecuteSQL method. There is also a way to combine steps 2 and 3 into a single SQL command that creates the table and populates its data in a single step. The SQL looks like this: .. code:: SQL CREATE TABLE team AS SELECT Name, Coach, City AS Location FROM team_orig; .. _/topics/databases/supported_engines/sqlite/column_renaming_and_deleting/see_also: .. seealso:: :doc:`SQLiteDatabase` class ======== Overview ======== .. rst-class:: forsearch SQLite SQLite is the built-in database engine. You access it using the SQLiteDatabase class. SQLite is an open-source, public-domain embedded database that is used by all types of software. It is lightweight, fast and easy to use. It works great for desktop, web and iOS apps. Unlike most databases, SQLite does not have a strictly typed columns. You can put data of any type in any column, regardless of the type the column has defined. You can learn more about SQLite by visiting their web site: `www.SQLite.org `_ .. _/topics/databases/supported_engines/sqlite/overview/the_database_property: The Database property --------------------- When working with a SQLite database you will want to have a database property available so you can easily use it throughout your app. For desktop and iOS projects you'll likely want this property to be on the App object or in a module. For web projects you usually want it to be on the :doc:`Session` object. .. code:: xojo DB As SQLiteDatabase If that is on the App object you'd refer to it like this: .. code:: xojo App.DB In a module you'd access it like this: .. code:: xojo Module1.DB ' If property is protected DB ' if property is global And on the Session object in a web project like this: .. code:: xojo Session.DB .. _/topics/databases/supported_engines/sqlite/overview/creating_a_database: Creating a database ------------------- SQLite databases are single files that typically exist on the same computer as the running app. For desktop apps, the SQLite database is usually in the Application Data folder for the operating system. For web apps, the database often resides alongside the web app itself on the server. In iOS apps, the database is often placed in the Documentation or Caches folder. To work with SQLite databases you use the :doc:`SQLiteDatabase` class. This desktop code creates a new SQLite database in the Application Data folder and could be on the App.Opening event: .. code:: xojo Var dbFile As FolderItem dbFile = SpecialFolder.ApplicationData.Child("MyDatabase.sqlite") ' DB As SQLiteDatabase is a property on the App object App.DB = New SQLiteDatabase App.DB.DatabaseFile = dbFile App.DB.CreateDatabase ' Use the database If the database already exists, then CreateDatabase will connect to the existing database instead. Do not place your database file alongside the desktop app itself. The app location is often read-only (especially for non-admin users) which will prevent you from being able to save any changes to the database. .. _/topics/databases/supported_engines/sqlite/overview/creating_a_table: Creating a table ---------------- As covered in Database Concepts, tables are used to store data in the database. The following SQL command creates the Team table in the SQLite database: .. code:: SQL CREATE TABLE Team (ID INTEGER, Name TEXT, Coach TEXT, City TEXT, PRIMARY KEY(ID)); SQL commands such as CREATE TABLE are sent to SQLite using the ExecuteSQL method of the SQLiteDatabase class. This code can be used to send the above SQL to SQLite: .. code:: xojo Var dbFile As FolderItem dbFile = SpecialFolder.ApplicationData.Child("MyDatabase.sqlite") App.DB = New SQLiteDatabase App.DB.DatabaseFile = dbFile Try App.DB.CreateDatabase ' Create the table Var SQL As String SQL = "CREATE TABLE Team (ID INTEGER, Name TEXT, Coach TEXT, City TEXT, PRIMARY KEY(ID));" Try App.DB.ExecuteSQL(SQL) Catch error As DatabaseException MessageBox("Error: " + error.Message) End Try Catch error As IOException MessageBox("The database file could not be created: " + error.Message) End Ty .. _/topics/databases/supported_engines/sqlite/overview/connecting_to_a_database: Connecting to a database ------------------------ If you are connecting to a database that already exists, you can instead call the Connect method: .. code:: xojo Var dbFile As FolderItem dbFile = SpecialFolder.ApplicationData.Child("MyDatabase.sqlite") If dbFile.Exists Then App.DB = New SQLiteDatabase App.DB.DatabaseFile = dbFile If App.DB.Connect Then ' Use the database End If End if .. _/topics/databases/supported_engines/sqlite/overview/retrieving,_adding,_changing_and_deleting_data: Retrieving, adding, changing and deleting data ---------------------------------------------- For information on how to retrieve, add, change or delete data, refer to the :doc:`Adding, updating and deleting rows` topic. .. _/topics/databases/supported_engines/sqlite/overview/transactions: Transactions ------------ As covered in :doc:`Transactions`, a Transaction is a block of processing. With SQLite, by default each command operates as its own implicit transaction. There may be times when you instead want to group many commands into a single transaction. This can be for data integrity purposes, but it also has the benefit of improving performance as well. For example, if you update 1,000 rows each of which are in their own transaction then the database has to do more work as it has to create and commit a transaction for each row. Instead you can manually start a single transaction, do your 1,000 row updates and then commit once at the end. This is much faster as the database only has to create and update the single transaction. To start a transaction in SQLite, you use the "BEGIN TRANSACTION" SQL command, which you can send using the ExecuteSQL method: .. code:: xojo App.DB.ExecuteSQL("BEGIN TRANSACTION;") Then when you are done, call the Commit method: .. code:: xojo App.DB.CommitTransaction .. _/topics/databases/supported_engines/sqlite/overview/auto-incrementing_primary_keys: Auto-incrementing primary keys ------------------------------ With SQLite, if a table has a single column specified as the INTEGER primary key, then that column auto-increments when a row is added to the table. This column is said to map to the internal rowid column that is on all SQLite tables. However, just because SQLite has an internal rowid column, you should not rely on it as your primary key. Rowid values can be changed behind the scenes by SQLite and this could possibly corrupt any relationships in your database. Always create a separate primary key for your tables. When you INSERT data into a table with a primary key, you omit the primary key from the INSERT SQL: .. code:: SQL INSERT INTO Team (Name) VALUES ('Seagulls'); The above SQL is sent to SQLite using this code: .. code:: xojo ' App.DB refers to a connected SQLiteDatabase App.DB.ExecuteSQL("INSERT INTO Team (Name) VALUES ('Seagulls');)" After adding a row to the database, you can get the value of the last primary key value by calling the LastRowID method: .. code:: xojo Var lastValue As Integer lastValue = App.DB.LastRowID .. _/topics/databases/supported_engines/sqlite/overview/encryption: Encryption ---------- SQLite databases can be encrypted. An encrypted database cannot be viewed at all unless you know the encryption key. .. _/topics/databases/supported_engines/sqlite/overview/encrypting_a_database: Encrypting a database ********************* To encrypt a new database, specify a value for the EncryptionKey property before you call CreateDatabase or before you call Connect. This creates a new encrypted database: .. code:: xojo Var dbFile As FolderItem dbFile = SpecialFolder.ApplicationData.Child("MyDatabase.sqlite") App.DB = New SQLiteDatabase App.DB.DatabaseFile = dbFile App.DB.EncryptionKey = "MySecretKey123!" App.DB.CreateDatabase ' Use the database To encrypt an existing database, call the Encrypt method, supplying the encryption key as a parameter: .. code:: xojo Var dbFile As FolderItem dbFile = SpecialFolder.ApplicationData.Child("MyDatabase.sqlite") App.DB = New SQLiteDatabase App.DB.DatabaseFile = dbFile App.DB.CreateDatabase ' Encrypt the database App.DB.Encrypt("MySecretKey123!") You can also use the Encrypt method to change the encryption key of an encrypted database. .. _/topics/databases/supported_engines/sqlite/overview/decrypting_a_database: Decrypting a database ********************* Remember, you do not need to decrypt a database in order to use it -- just connect using the encryption key as shown above. To decrypt an encrypted database, call the Decrypt method after you have connected to the database: .. code:: xojo Var dbFile As FolderItem dbFile = SpecialFolder.ApplicationData.Child("MyDatabase.sqlite") App.DB = New SQLiteDatabase App.DB.DatabaseFile = dbFile App.DB.EncryptionKey = "MySecretKey123!" App.DB.CreateDatabase App.DB.Decrypt ' Decrypt the database End If Once you have decrypted the database, you no longer need to use the encryption key to access it. .. _/topics/databases/supported_engines/sqlite/overview/multiple_user_support: Multiple user support --------------------- SQLite is not technically a multiple user database. But by enabling a feature called Write-Ahead Logging (WAL), you can improve performance when multiple users are accessing the database from the same app. This is most useful with web apps because they can easily have multiple users connected to the web app, each of which may be connecting to the database. The `SQLite developers do not recommend `_ that you use SQLite on a shared network drive with separate apps sharing it. This can result in data corruption. .. _/topics/databases/supported_engines/sqlite/overview/large_objects_(blob): Large objects (BLOB) -------------------- Large objects in a database are also called BLOBs (Binary Large Objects). You can add large objects to a database using the DatabaseRecord class, but there is a limitation on the amount of available memory. If you need to store large objects in a database, but want to be able to read the data from the database sequentially, you use the :doc:`SQLiteBLOB` class in conjunction with the CreateBlob and OpenBlob methods on the SQLiteDatabase class: .. code:: xojo Var blob As SQLiteBlob blob = App.DB.OpenBlob("Team", "Logo", 1, True) If blob <> Nil Then ' Read BLOB Var data As String While Not blob.EndOfFile ' Read 1000 bytes at a time data = blob.Read(1000) Wend ' Do something with the data End If .. _/topics/databases/supported_engines/sqlite/overview/attaching_other_sqlite_databases: Attaching other SQLite databases -------------------------------- Normally when you connect to a SQLite database, you are connecting to a single file. With SQLite it is possible to connect to multiple SQLite database files using one connection. You do this by "attaching" the additional databases. The AttachDatabase method attaches the specified database file and lets you assign a prefix to use for all the tables in the attached database. The DetachDatabase method is to remove the attached database. .. code:: xojo dbFile = New FolderItem("ExtraDB.sqlite") If App.DB.AddDatabase(dbFile, "extra") Then Var rs As RowSet rs = App.DB.SQLSelect("SELECT * FROM extra.Table") ' Process results... End If .. _/topics/databases/supported_engines/sqlite/overview/determining_the_sqlite_version: Determining the SQLite version ------------------------------ It can sometimes be helpful to know exactly which version of SQLite is being used by your app. You can check this using the LibraryVersion property. .. code:: xojo MessageBox(App.DB.LibraryVersion) .. _/topics/databases/supported_engines/sqlite/overview/videos: Videos ------ These videos also cover SQLite and its features: * `Using SQLite `_ * `SQLite Features `_ * `Advanced SQLite `_ * `Connecting to Databases `_ * `Database Development `_ .. _/topics/databases/supported_engines/sqlite/overview/see_also: .. seealso:: :doc:`SQLiteDatabase`, :doc:`SQLiteBLOB` classes; :doc:`Database Basics for beginners` topic =================== Full text searching =================== SQLite supports full text searching, but what is it? Full text searching is a fast way to look for specific words in text columns of a database table. Without full text searching, you would typically search a text column using the LIKE command. For example, you might use this SQL command to find all books that have "cat" in the description: .. code:: SQL SELECT Title FROM Book WHERE Desc LIKE '%cat%'; But this select actually finds rows where the Desc column has the letters "cat" in it, even if it is in another word, such as "cater". Also, using LIKE does not make use of any indexing on the table. The table has to be scanned row by row to see if it contains the value, which can be slow for large tables. .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/full_text_searching_sqlite_fts5.png Full text seach is a way to avoid these two issues. With SQLite, you enable full text search by creating what is called a "virtual table" using one of the FTS engines included with SQLite: FTS4 or FTS5. FTS5 support was added to the SQLiteDatabase class with Xojo 2016 Release 3 and has more advanced searching features, including ranking and highlighting of results and is what is described here. To create an FTS5 virtual table, you use the SQL CREATE VIRTUAL TABLE command. This SQL command creates a virtual BookSearch table using FTS5: .. code:: SQL CREATE VIRTUAL TABLE BookSearch USING fts5(ID, Title, Desc); This SQL creates a "fake" table that is hooked up to the full text search engine. You can now populate this table with the data you want to search, usually copying it from data in a normal table with SQL like this: .. code:: SQL INSERT INTO BookSearch SELECT ID, Title, Desc FROM Book; With the data in place, you are now able to search it using a SELECT statement with the special MATCH keyword. For example, this SQL searches for all books that have the word "cat" in the any of the columns: .. code:: SQL SELECT Title FROM BookSearch WHERE BookSearch MATCH 'cat'; And you can also search specific columns in the FTS table by prefixing the column name to the match criteria. This SQL searches just the Desc column for "cat": .. code:: SQL SELECT Title FROM BookSearch WHERE BookSearch MATCH 'Desc:cat'; To rank your search results by relevance (most relevant to least relevant) you can use an ORDER BY with the special rank value. This SQL ranks search results: .. code:: SQL SELECT Title FROM BookSearch WHERE BookSearch MATCH 'cat' ORDER BY rank; You can do wildcard searching with the "*" character. This SQL searches the column for all text that starts with "prog", so it will find "program", "programming", etc: .. code:: SQL SELECT Title FROM BookSearch WHERE BookSearch MATCH 'prog*'; As a final tip, you can even highlight the matched text in the results by using the highlight function. This SQL uses brackets to highlight the results found in the Desc column (the 2 indicates the third column, which is Desc): .. code:: SQL SELECT Title, highlight(BookSearch, 2, '<', '>') AS HighlightDesc FROM BookSearch WHERE BookSearch MATCH 'cat'; Use the SQLSelect method of the SQLiteDatabase class to send any of the above commands to a SQLite database. There are even more advanced search capabilities you can perform, which you can read about in the official SQLite docs for FTS5: SQLite FTS5 FTS 3 and FTS4 also remain available. You can find more information about them on the SQLite site: SQLite FTS3 and FTS4 .. youtube:: YIV85oDdTOo .. _/topics/databases/supported_engines/sqlite/full_text_searching/see_also: .. seealso:: :doc:`SQLiteDatabase` class; `FTS5 `_, `FTS 3/4 `_ topics at SQLite.org =================== Write-Ahead logging =================== To make :doc:`SQLite` even faster you can enable a feature called Write-Ahead Logging (aka WAL). Normally when you update your SQLite Database within a transaction, the original data is copied to a separate rollback file. The new data is written directly to the DB file. This results in two disk writes for every DB change. When you COMMIT, the rollback file is removed. Should you ROLLBACK, then the data is restored from the rollback file. WAL can be faster because it reverses this. With WAL each change to the SQLite Database is written to a separate "write-ahead logging file" (which typically ends in "-wal"). This results in just a single disk write. Additionally, because of the separate files, an app with multiple threads is able to read from the DB while it is being written to. And vice versa. This is an especially nice benefit for web apps that need a DB, but do not require a database server. To enable WAL, you can use the SQLite Pragma command: .. code:: SQL PRAGMA journal_mode=WAL; In Xojo code, you send the Pragma command using ExecuteSQL: .. code:: xojo DB.ExecuteSQL("PRAGMA journal_mode=WAL;") Or more simply you can set the :ref:`SQLiteDatabase` property to True: .. code:: xojo DB.WriteAheadLogging = True The data in the separate WAL file will at some point have to be transferred back to the original database. This is called a "checkpoint". You can do these manually or let SQLite handle them automatically, which [according to the SQLite docs](https://www.sqlite.org/wal.html) happens when the WAL file reaches a threshold of 1000 pages. You can manually initiate a checkpoint using the wal_checkpoint Pragma command: .. code:: SQL PRAGMA schema.wal_checkpoint; In Xojo code, you can send this Pragma command to the DB using the ExecuteSQL command: .. code:: xojo DB.ExecuteSQL("PRAGMA schema.wal_checkpoint;") There are downsides to using WAL. Performance for reads can suffer when there is a large WAL file because data has to be searched in two places. The [official SQLite docs on WAL](https://www.sqlite.org/wal.html) also list some of the other downsides. But for most types of usage, WAL is worth using. Test it with your apps to see if they get a benefit. .. youtube:: YIV85oDdTOo .. _/topics/databases/supported_engines/sqlite/write-ahead_logging/see_also: .. seealso:: :ref:`SQLiteDatabase.WriteAheadLogging` property; `SQLite Write-Ahead Logging `_ documentation ============= SQLite basics ============= Learn the foundations to using Xojo with SQLite - the universal, public domain and embedded database engine used for all kind of apps and solutions. No previous experience with database management needed. Once you have finished this tutorial, you 'll have the foundations to create your own multiplatform database apps and to better explore and understand the related Xojo classes. In this tutorial, we will create a basic but functional database app to manage customers and invoices. Read each section and `download the companion Xojo Project `_. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/introduction: Introduction ------------ SQLite and Xojo are a great team for managing large amounts of information easily. Users of these Xojo apps don't have to install any additional components or libraries. In addition, Xojo solutions using SQLite can be easily deployed to any of the supported platforms without the need of making changes in the base code (or with minimal changes). A few reasons the SQLite a database engine so popular is: * **It is an embedded library**. This means that the users of the apps don't need to use additional hardware or install additional or external components in order to use them. What's more, thanks to Xojo you can forget about the need to include or add any SQLite libraries to the deployed executable package. Xojo takes care of everything, offering the same behavior in all the supported platforms! * **It is Public Domain**. This means that you will not have to pay a license or royalties for using SQLite in your developed solutions, even if they are commercial. * **SQLite has a low footprint**, so your deployed executables will not need a lot of storage; this is paramount if you need to deploy your Xojo solutions on embedded hardware with strict requirements. * **It is truly Universal**. SQLite is included by default in a wide range of devices, even in the main Operating Systems used for Desktop, Smartphones, tablets, Smart TV, etc. * The databases generated with SQLite are common files, whose size is only limited by the Operating System used to store them. Xojo + SQLite offers great flexibility: * We can connect with one or several already existing SQLite databases, working with the data contained in their respective tables. * We can design an SQLite database from scratch, using the integrated Database Editor in the Xojo IDE. * We can create an SQLite database from scratch via code from the app. In order to accomplish these operations, among others involved with database management, Xojo offers a Class that makes everything simpler: * :doc:`SQLiteDatabase`. This is the class we need to use for Console, Desktop, Web and Mobile (iOS & Android) apps. Xojo adds the most stable, tested and trustable SQLite release to the framework, avoiding some of the problems, backwards compatibility issues or bugs that may arise when adopting the latest available releases. In addition, sharing the same database library (or engine) release also means that our apps will be 100% portable between all the supported platforms. Thus, the most important fact we have to remember is that, while we may not be using the latest available SQLite release, we will not be using a release far away either; and with the assurance that we will get the same behavior no matter that the solution will be running under macOS, Windows or Linux, for example. Of course, Xojo also updates the implemented SQLite library nearly every new release. For example, when this course was published, we already knew that the Xojo 2018.1 release will support 256 bits ciphering, being significantly more strong in comparison with the 128 bits used until Xojo 2017r3 release. Anyway, if you find yourself in a situation where you need additional features not included in the Xojo provided SQLite implementation, you can turn to any of the available third parties add-ons and components that expand the use of SQLite even more. For example, Monkeybread Software offers two Multiplatform plug-ins. The first of these is the MBS Xojo SQL Plugin; and the second one is MBS SQLite Extension, adding the following features over the ones you can find in the default SQLite implementation: * Additional mathematical functions as Round or Pow * Functions to save Blob values (large Binary Objects) in files, outside of the own database file; something that is always preferred and even a good practice. * Functions to calculate Blob values checksums in the server * String functions not using accented characters for text search * Use of Regular Expressions for search Independent of the SQLite implementation you decide to use, notice that all the database operations made on SQLite use the SQL language, in fact the SQL/92 and SQL/99 subset. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/multiuser_support: Multiuser support ***************** Unlike other database engines that offer a Client/Server architecture, SQLite was not developed from its inception to support several simultaneous accesses over a database file residing under a network server. However, you may be surprised to know that through the WAL (Write Ahead Logging) feature activation —also supported by Xojo— it is possible to provide this functionality if there are not too many simultaneous accesses, and the use from the clients (apps) is done not directly accessing the database file residing in a network server, but through an app, residing in the same server network that the database file. In fact, that it is better to act as an intermediate, communicating between the clients and the database file itself. An example of this configuration can be a Web app created with Xojo that communicates with a SQLite database located in the same server, or any other Xojo app acting as a server to one (or more) SQLite databases. You also can find third parties products able to serve SQLite database files from a server, as for example CubeSQL from SQLabs and Valentina Server from Valentina, among others. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/in-memory_databases: In-memory databases ******************* A peculiarity to SQLite databases is that they don't necessarily need to be create or saved as files on physical storage; in fact, we can create them as in-memory databases. This gives us an idea about the kind of uses, the flexibility and performance you can get… if the hardware you use has enough memory to support them! (as probably is the case with the current available hardware). You can also create and use this kind of in-memory databases in Xojo apps, with the advantage that you will find the features that enables the apps to dump, save or backup these in-memory databases to physical files. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/sqlite_database_anatomy: SQLite database anatomy ----------------------- What makes a SQLite database after all? As we have seen, it's no more than a regular file whose content represent one or more tables. At the same time, these tables are composed of a series of columns whose label or name (identification) and type are used to define their structure. Then, we will populate these columns, or some of them, through the creation of records (or rows) for the defined tables in our database file. If you think about it, this representation doesn't differ too much from the typical spreadsheet, while it is true that the database files have other aspects that separate them from this other kind of software. Anyway, it helps to visually understand the underlying basic structure of a SQLite database. In fact, it is through the table definition as we start to give structure to the database. For that, during the table definition it is mandatory to name or label for every column; once done that, we can start creating new records for the newly created table. However, the same way we have at our disposal several kind of Types in Xojo that we can use in order to define our variables or labels, and that will be used both to identify and limit the kind of data we can assign to them, or in combination with other variables, properties and method parameters or events, SQLite also offers their own Types set, but with a main and very important difference: Xojo is strict in the management of their Types. This means that, once you declare a variable, for example as Text, you will not be able to assign a different type to it, just other Text values or previously converted to Text values. Under SQLite the Type declared in association with a column is just intentional. This is, when we create the SQLite database tables we can assign any of the supported types to the columns, but this will not impede to insert or update the columns of the records using values with completely different types to the expected or declared ones. However, when possible, the SQLite engine will try to implicitly convert the received value to the one declared for the column, but this behavior is not guaranteed at all! Once we know this, we also have to notice that the available data Types are: * Null * Integer * Real * Text * Blob As you can see, SQLite lacks for example the Boolean type, while internally manages it as 1 or 0 for the columns defined as Integer, or as True and False for the columns defined as Text. In addition, Xojo provides the mechanism (as we will see in next sections) that allows us to expand, in certain way, the amount of types we can work with when dealing with SQLite databases, easing this way the retrieval and saving of data into the records with the expected results. When it comes to the structure creation for a database, it is possible to add new tables to an already created database, delete any of the existing ones, and modifying any of the already defined tables in the database, but in this last case only by adding new columns and not deleting any of the previously defined columns. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/creating_our_first_database: Creating our first database --------------------------- As we have seen, we can create the SQLite database structure using any tool we want to, ranging from any of the existing commercial or free SQLite database Editors, or even using the command line from the Terminal or system prompt. However, the Xojo IDE includes its own database Editor, and this will be the one we will use in order to create our first SQLite database! For that, choose the Insert > Database > New SQLite Database… from the menu. As result, we will get a Dialog to enter the name of the database file and select the path or folder to save it (for this tutorial we will save the file on the same folder that our Xojo Project). After confirmation, Xojo will present the Database Editor, adding the corresponding object reference as a Database Type —i.e.: an alias to the file on disk— to the Navigator (1), showed in the following screenshot. If we want, we can even change the name of the referenced object from the Inspector (A), but notice that we will be renaming the internal reference used by the Xojo code and not the name of the real file on disk: .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlite_basics_sqlite-create-a.png .. warning:: The database files created and added to the Xojo project in this way will expect to always find the database file in the same absolute path. This means that, when deploying the apps using database files added in this way, we will have to be sure that the deployed SQLite file will be reachable using the same path (or location), and the final user has the proper read and write privileges to access the database file. The operations available in the Database Editor are not as complete as those found in the dedicated SQLite management apps, but they are good enough to put the main wires to our database in order to prototype our apps, or to define the structure of really simple SQLite database based apps. For these cases, we will be able to add new tables (2), new column definitions for the selected table (3), and apply the changes made on the Database (4); using the buttons found in the upper toolbar. .. warning:: Don't forget to apply the changes made after adding or modifying new tables or columns. It may seem something trivial, but the flexibility to change between the objects listed under the Navigator will result in losing the changes made to the database if they are not previously saved. As we add new columns (or fields) to the database, we 'll see that the Inspector Panel will bring a new range of options we can use to define the name of the column, the associated Type and other attributes. Let's see what they mean and which of these make sense when used in combination with a SQLite database: .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlite_basics_sqlite-create-b.png * **Type**. You will notice that this popup menu offers more types that the ones supported by SQLite seen in the previous section. It is recommendable to always adhere to the only types officially supported by SQLite. * **Primary Key**. All the SQLite tables have one or several columns to uniquely identify every record in the table. In fact, all the tables include implicitly and by default a column named rowid that, if there is no other column doing the same function, will act as the Primary Key for the table. If during our table definition we create a column of Integer type, also activating this checkbox in the Inspector Panel, then rowid will become an alias for the column defined by us. Notice that, in our example, the id column is acting as the Primary Key, being visually indicated with the icon of a little key under the Key column in the Editor. * **Mandatory**. When we activate this Constraint, we are telling SQLite that we don't want to admit the creation of new records that omits a valid value for this column. It's a good way to make sure that the record will have all the needed information we expect to find in future operations on it. Thus, if SQLite finds that the operation omits a value for this column, the operation will not be completed and we will get an error that we can catch from the Xojo code. * **Index**. When this option is activated for a Column, the associated table will create an Index based on it. The purpose of this index is improving some operations, like the queries' performance, or those resulting from combining data, as we can get when using the SQL instruction Join. A table may create indexes from just one column or multiple columns. In the second case, the order we use to include the columns in the index is very important because SQLite will sort the data based on the first defined column for the index. The assignation, quantity or kind of indexes created for the tables in the database is kind of an art, and will depend mostly of the operations and queries we will expect to do on the stored data. * **Default Value**. We also can define the default value assigned to a column when it's not provided as result of the new record creation and insertion. This is a good way to make sure that we will not have empty fields; also to get a known and expected value if none is provided. We only need to be sure of assigning a default value compatible with the Type defined for the column. * **Length**. This has no relevance when working with SQLite database because SQLite doesn't accept field length restrictions, in comparison to what's doable when using other database engines. Having said this, SQLite imposes length limits for Text and Blob columns, but they are big enough to not be considered a real limitation for most of the SQLite based solutions. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/creating_the_schema_with_sql_…_from_the_editor: Creating the schema with SQL … from the editor ---------------------------------------------- We can create the database schema (tables structure) adding the tables and columns as we have shown in the previous section, but this has two main downsides: it is slow and doesn't allow access to all of the flexibility of SQL. The good news is that if we have enough SQL syntax knowledge, we can use the toolbar button to execute complex SQL sentences. This button will not be enabled until we have added and selected a Table. .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlite_basics_sqlite-create-c.png However this is not a big issue, mainly because the newly added tables are not saved to the database until we expressly push the corresponding button. Thus, in order to create the same schema for our example database, we only need to add a new empty table (we don't have to change its name either), clicking the SQL Statements button (5) after that, and writing the following sentence in the resulting Dialog box: .. code:: SQL CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT, age INTEGER); Push the Execute button, close the dialog and you will see how the database will reflect the just created table, with the structure and column definitions as result of the operation. This requires less time in comparison to the same procedure using the Database Editor buttons combined with the Panel Inspector attributes. Let's take advantage of this feature again to create a second table. While the last table is still selected, use the SQL Statements button again and enter this sentence: .. code:: SQL CREATE TABLE invoices(id INTEGER PRIMARY KEY AUTOINCREMENT, customer TEXT NOT NULL, amount INTEGER); Execute the sentence and be sure that the database has the two tables: person and invoices. Once this is done, we will have finished our example database. Remember: while the integrated database editor is useful to define simple database or for prototyping, you 'll probably want to use specific tools or apps devoted to the creation, edition and management of more complex SQLite databases projects. Nevertheless, the integrated Database Editor doesn't offer options to add new records to the database. This is something we will take care of in the next section! .. _/topics/databases/supported_engines/sqlite/sqlite_basics/creating_a_reference_to_the_database_file: Creating a reference to the database file ----------------------------------------- In this step we are going to create an internal reference to the SQLite database file created in the Database Editor. For that, select the App item in the Navigator and add a new property to it using the following values: * **Name:** db * **Type:** SQLiteDatabase * **Scope:** Public We will use this property to refer (and operate with) the database along all our application windows. In order to assign and **Connect** to the database file, add the **Opening** Event Handler to the App item from **Insert > Event Handler…** Once added, put the following snippet of code in the resulting Code Editor: .. code:: xojo db = New SQLiteDatabase db.DatabaseFile = New FolderItem("test.sqlite") Try db.Connect Catch e As DatabaseException MessageBox(e.Message) End Try The first line of code creates a new SQLiteDatabase instance and assigns it to our **db** property. Then, we need to inform our db property (keep in mind that is a SQLiteDatabase) what is the real database file we want to work with. For that we assign a new FolderItem instance, initialized with the name of the database we created and saved previously from the Database Editor. In this case, we are using just the name of the file for the FolderItem constructor, because it will default to search for it in the same folder where our Xojo project is saved. The most important thing here is the **db.Connect** line, in charge to "connect" and open the database. If everything goes ok in the call to this method, then we will be able to start to do things like insert, update or delete records, create new tables… or any of the SQLite supported operations. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/designing_the_ui_for_our_database_app: Designing the UI for our database app ------------------------------------- In order to see how we can work with our example database, we will go through an example app that will offer a set of basic features reflecting the database schema: we will be able to add new customers, new invoices and also access all the invoices for the currently selected customer, calculating the sum. The best part is that, along the way, we will learn how to use other methods from the SQLiteDatabase class, and other related database classes, with a minimum amount of code. Layout the user interface for our example app as shown in the following screenshot. This is the same desktop example that we previously added to the SQLite database. Don't forget to name the highlighted controls via the Inspector Panel. The remaining controls will have their default names: * All buttons are DesktopButton instances. * All the labels are, well, Label instances (including the one placed at the right of "ID", and whose default text has been deleted). * We will use three DesktopTextField instances, both for showing and capturing the data associated with their respective labels: "Name", "Surname" and "Age" (all of the with their **Enabled** property set to **False**). .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlite_basics_customers.png At this point, we have all the UI elements our app will use for the following operations: * Add New Records * Insert Record * Update Record * Delete Record * Move to the First Record * Move to the Previous Record * Move to the Next Record * Move to the Last Record Of course, at first our database is empty, without records. But it will no be always the case! After we have added some records into it, the next time we run the app probably expect that the window displays (for example) the last record in the database! So let's get rid of that adding the **Opening ** Event Handler to the **Customers** window, and adding the following code in the resulting Code Editor: .. code:: xojo Try ' Check if we have existing records in the database ' If so, then fill the fields with the data from ' the last one. Var rs as RowSet = App.db.SelectSQL("select * from person where id = (select max(id) from person)") UpdateUI(rs) nameField.Enabled = True surnameField.Enabled = True ageField.Enabled = True Catch e As DatabaseException MessageBox(e.Message) end try Some interesting things about this fragment of code: * We instruct our database instance (through the db property) to get a selection of Rows (records) by calling the **SelectSQL** method on it. As you can see, the parameter is a string with a proper SQL sentence. * As result of calling the SelectSQL method, we get a :doc:`RowSet` instance whose contents will be the rows matching the SQL sentence (or no rows at all if there are not matches, for example). We assign this RowSet to our **rs** variable. Then will update the UI with the name, surname, age and ID of the matching record. This is something that we need to do in several places, so it's better to create a method for that in the **Customers** window (let's call it **UpdateUI**) passing along the **rs** variable, instead of writing repetitive code "here" and "there". So, after adding the new **UpdateUI** method we can add the following code in the resulting Code Editor: .. code:: xojo Try If rs.AfterLastRow = False Then IDLabel.Text = rs.Column("id").StringValue nameField.Text = rs.Column("name").StringValue surnameField.Text = rs.Column("surname").StringValue ageField.Text = rs.Column("age").StringValue InsertButton.Enabled = False updateButton.Enabled = True deleteButton.Enabled = True End If Catch e As DatabaseException MessageBox(e.Message) End Try The interesting thing here is the **if rs.AfterLastRow = False** comparison. A RowSet instance can be a valid one even if it has no rows on it! So, if the **AfterLastRow** property is set to **False**, that does means that we have (at least) a valid row whose columns we can access to retrieve his associated data. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/adding_new_records,_inserting,_updating_and_deleting_rows: Adding new records, inserting, updating and deleting rows --------------------------------------------------------- Once we update the App UI on every run, is time to implement the functionality that will let us to Add new records, Update or Delete them. Let's start selecting the **New Record** button and adding the **Pressed** Event Handler to it. Instead of adding a new record to the database, the action from this button will reset the UI fields and other button status using the following code: .. code:: xojo ' enable fields and restart their values nameField.Enabled = True surnameField.Enabled = True ageField.Enabled = True IDLabel.Text = "" nameField.Text = "" surnameField.Text = "" ageField.Text = "" ' Enable the buttons so they are active ' When a new record has been created InsertButton.Enabled = True UpdateButton.Enabled = False DeleteButton.Enabled = False .. _/topics/databases/supported_engines/sqlite/sqlite_basics/insert_record: Insert record ************* Select now the **Insert** button in the Layout Editor and add the **Pressed** Event Handler to it. This will be in charge of adding a new record to the database, using the fields data to fill-in the expected columns in the record. Write the following code in the associated Code Editor: .. code:: xojo Var PersonRecord As New DatabaseRow PersonRecord.Column("Name").StringValue = nameField.Text PersonRecord.Column("Surname").StringValue = surnameField.Text PersonRecord.Column("Age").IntegerValue = ageField.Text.ToInteger Try App.db.AddRow("person", PersonRecord) IDLabel.Text = App.db.LastRowID.ToString Me.Enabled = False deleteButton.Enabled = True UpdateButton.Enabled = True Catch e As DatabaseException MessageBox(e.Message) End Try In the first line of code we create a new instance of **DatabaseRow**, so we can use the associated variable to populate the columns we are interested in with the data entered by the user in the respective TextFields. Observe that, in order to add a new record in the database, we only need to call the **AddRow** method passing along the name of the database table ("person") and the database row instance. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/updating_record: Updating record *************** Let's see now how we can update a record. Select the UpdateButton button and add the Pressed event to it, writing the following code in the resulting Code Editor: .. code:: xojo Try Var rs As RowSet = App.db.SelectSQL("select * from person where id="+idLabel.Text) rs.EditRow rs.Column("name").StringValue = nameField.Text rs.Column("surname").StringValue = surnameField.Text rs.Column("age").StringValue = ageField.Text rs.SaveRow Catch End Try For updating, we retrieve a **RowSet** matching the record whose ID column is the same as the record we are showing in the UI (and displayed by the idLabel control). So here, we only need to call the **EditRow** method before assigning new values to the columns we are interested in, and call then the **SaveRow** method to make that changes permanent on the database. That easy! .. _/topics/databases/supported_engines/sqlite/sqlite_basics/deleting_records: Deleting records **************** The last CRUD (Create, Read, Update, Delete) operation related to databases is the one in charge of deleting the current record. So, add a the **Pressed** Event Handler to the **DeleteButton** button and write the following code in the resulting Code Editor .. code:: xojo Try Var rs As RowSet = App.db.SelectSQL("select * from person where id="+IDLabel.Text) rs.RemoveRow ' Display last available row rs = App.db.SelectSQL("select * from person where id = (select max(id) from person)") Var name, surname, age, id As String If rs <> Nil And rs.AfterLastRow <> True Then UpdateUI(rs) Else ' no more rows in the database pbUpdate.Enabled = False pbInsert.Enabled = False me.Enabled = False nameField.Text = "" surnameField.Text = "" ageField.Text = "" idLabel.Text = "" End If Catch e As DatabaseException MessageBox(e.Message) End Try As you can see in the two first lines of code, deleting the current record is as easy as retrieving it from the database (getting a RowSet matching the SQL query), and calling then the **RemoveRow** on the own RowSet. However, when we delete the current record, we expect the UI to update displaying a new existing record from the database. It could be the previous one or the next one. In this case, we are retrieving the last one. After that we call our **UpdateUI** method, passing along the RowSet with the last record (if any). If there are no more records in the database, the we delete .. _/topics/databases/supported_engines/sqlite/sqlite_basics/navigating_records:_first,_previous,_next_and_last: Navigating records: first, previous, next and last -------------------------------------------------- Navigating between existing records mainly involves using the right SQL query on every case, using for that the same Database classes we did see previously. Let's start adding the **Pressed** Event Handler to the **FirstButton** button, writing the following code in the resulting Code Editor: .. code:: xojo Var rs As RowSet = App.db.SelectSQL("select * from person where id = (select min(id) from person)") UpdateUI(rs) Write the following code in the Pressed Event Handler of the **PreviousButton** button: .. code:: xojo Var id As String = If(IDLabel.Text.IsEmpty, "0", IDLabel.Text) Var rs As RowSet = App.db.SelectSQL("select * from person where id < " + id + " order by id desc limit 1") UpdateUI(rs) This code for the Pressed Event Handler of the **NextButton** button: .. code:: xojo Var id As String = If(IDLabel.Text.IsEmpty, "0", IDLabel.Text) Var rs As RowSet = App.db.SelectSQL("select * from person where id > " + id + " order by id asc limit 1") UpdateUI(rs) And this snippet of code for the **LastButton** Pressed Event Handler: .. code:: xojo Var rs As RowSet = App.db.SelectSQL("select * from person where id = (select max(id) from person)") UpdateUI(rs) .. _/topics/databases/supported_engines/sqlite/sqlite_basics/working_with_two_tables: Working with two tables ----------------------- In this section we will add a second window to the project. This window will be in charge of adding new records to the Invoices table. It will also serve to show us how to relate values from two tables, because the new invoice records have to be paired to the id of an existing customer from the "person" table. Add a new window to the project. Next, set the Type property to Modal Dialog, and the Title property to New Invoice, both of them under the Frame section of the Inspector Panel. Change also the Name property to Invoice, under the ID section. Next, layout the new window as showed in the following screenshot, leaving all the names for the control instances as their default values, except the ones highlighted in red. .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlite_basics_invoice.png The record insertion itself will be done via the code associated with the Pressed event added to the AddButton button: .. code:: xojo Try Var invoiceRecord As New DatabaseRow invoiceRecord.Column("customer").StringValue = lbCustomerID.Text invoiceRecord.Column("amount").StringValue = amountField.Text App.db.AddRow("invoices", invoiceRecord) Catch e As DatabaseException MessageBox(e.Message) End Try Self.Close We said that our new invoice has to be associated with an id from the current customer in the main window; so we have to assign that information to the Label control named lbCustomerID from the Invoice window. How we can do this? Really easily! Again select the main window (**Customers)**, choose the button labeled New Invoice and add the Pressed Event to it with the following code in the resulting Code Editor: .. code:: xojo Invoice.Show Invoice.lbCustomerID.Text = IDLabel.Text With the first line of code we will show the Invoice as a modal Window. Once active, we just need to assign the content from the Label control named lDLabel, from the main window, to the Label control named lbCustomerID. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/iterating_a_rowset: Iterating a RowSet ------------------ Let's add a third (and last) window to the project. This is the one in charge of listing all the invoices for the current customer, displaying every item in its own ListBox row, and providing the total accumulated amount at the end of this list. Once the window has been added to the project, use the following settings in the Inspector: * Name: Invoices. * Type: Movable Modal. * Title: Invoices Listing. Next, layout the user interface as shown in the following screenshot, leaving all the controls' identifications with their default names, except for those highlighted in red color for the Label and the ListBox: .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlite_basics_customer_invoices.png Add a new method to the window using the following signature: .. code:: xojo getInvoicesForCustomer(id As String) Next, write the following fragment of code in the resulting Code Editor: .. code:: xojo lInvoices.RemoveAllRows lbID.Text = id Var rs As RowSet = App.db.SelectSQL("SELECT id, amount FROM invoices WHERE customer=" + id) Try Var total As Double Var t As String For Each row As DatabaseRow In rs lInvoices.AddRow row.Column("id").StringValue t = row.Column("amount").StringValue lInvoices.CellTextAt(lInvoices.LastRowIndex, 1) = t total = total + t.CDbl Next lTotal.Text = Format(total, "##########.00") Catch e As DatabaseException MessageBox(e.Message) End Try The first thing we can see is that we are assigning the value from the received parameter to the Label control named lbID. This is the one matching the ID of the current record from the Customers window. The most interesting thing, however, is what we find in the next lines of code: In first place we get a RowSet with all the records from the "invoices" table whose "customer" column matches the id we got from the displayed record in the Customers window. Then, we iterate all the records in the "**For each row As DatabaseRow in rs**" loop, adding their fields to both columns of the ListBox1 instance, accessing the record fields as we have seen in the previous section. Remember that **AfterLastRow** will be false if the RowSet is not pointing beyond the last row in the set. We will take advantage of iterating the records to total the amount from every invoice, using for that the CDbl command that returns a Double type from a text String. Then, we show the total addition using the Format function for that. We will want to update the displayed values in the case the user adds a new "Invoice" to the same customer while this window is still open. To solve that, we will add a new Pressed Event Handler to the button labeled "Update", and putting the following code in the resulting Code Editor: .. code:: xojo getInvoicesForCustomer(lbID.Text) Finally, add the Pressed Event to the Button1 control and insert the following instruction in the corresponding Code Editor: self.Close. We just need to select the main Customers Window, select the pbInvoicesListing, adding the Pressed Event to the button and putting the following code in the resulting Code Editor: .. code:: xojo Invoices.Show Invoices.getInvoicesForCustomer(idLabel.Text) The first line of code will show the Invoices window and the second line of code will call the method we have created, previously passing as argument the ID for the current record. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/the_last_word: The last word ------------- Throughout this course we have reviewed the main foundations of SQLite database, their structure and the available Types for tables definition. We have seen how we can use the integrated Database Editor to create from scratch new, basic, SQLite databases and the way we can create and define their tables and their fields. Lastly, we have seen one simple way to create a functional database front-end or user interface able to Create, Insert, Modify and Delete records from the database. We also have introduced the RowSet and DatabaseRow classes, and how we can access a Row columns using the available methods; and how we can execute complete queries using SQL sentences in order to retrieve a RowSet with the matching records in the database. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/quizzes: Quizzes ------- Take these quizzes to test your knowledge of SQLite basics with Xojo. Check your answers on the :doc:`SQLite Basics Quiz Answers` page. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/quiz_1:_introduction_to_sqlite: Quiz 1: Introduction to SQLite ****************************** Use this Quiz to check what you have learned about SQLite database foundations. Notice that the questions may have more than one right answer. Question 1.1: Check all the right affirmations. * SQLite is a Client/Server database engine. * SQLite can be used for free. * SQLite doesn't requires additional installations. * SQLite doesn't support multiple users. Question 1.2: Xojo always implements the latest available SQLite Library. * No. * Yes. Question 1.3: Xojo supports multiple SQLite databases use. * No. * Yes, they always have the same number of Tables in their structure. * Yes. Question 1.4: SQLite supports in-memory databases. * Yes, they are always saved to disk before exiting the app. * No. * Only if it is also backed by a database on disk. * Yes. Question 1.5: Xojo supports saving an in-memory database to disk. * No. We have to have previously created a new database in disk, backing the in-memory data to it. * Yes. Question 1.6: What is the name of the Class (or Classes) in Xojo we can use to work with SQLite databases? * XQLite. * SQLiteDesktopBase. * SQLiteDatabase. * iOSDatabase. Question 1.7: Is it possible to place a SQLite database in a Server? Check all the right answers. * Only using third-party products. * Using third-party products. * The access to the database file is always made from an app located on the same server. * We need to activate the WAL feature. * No. Question 1.8: What are the advantages of using the WAL feature on SQLite? * Multiple simultaneous accesses from multiple users. * A better integrity protection for the stored database data. * Reduced memory consumption. * It is mandatory in order to create and use in-memory databases. Question 1.9: Can we use RegEx (Regular Expressions) in the SQLite queries? * Only if we add third-party components or add-ons. * Yes, Xojo includes RegEx support for SQLite. * No. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/quiz_2:_sqlite_database_structure: Quiz 2: SQLite Database Structure ********************************* This quiz lets you check the knowledge acquired about SQLite databases structure. Notice that the questions may have more than one right answer. Question 2.1: Does SQLite use strict Types checking for columns definition? * Yes. * No. * In some cases. Question 2.2: A table definition consist fundamentally of… * Cells. * Rows. * Columns and Rows. * Columns. Question 2.3: The SQLite data Types supported by SQLite are: * Nil. * Integer. * Double. * Text. * Null. * Boolean. * Real. * Picture. * Bolb. * Blob. Question 2.4: Once a table has been created, we can… * Delete columns. * Modify some of the current columns. * Add new columns to the table. Question 2.5: SQLite admits creating columns that will be empty during the database use. * Yes. * No. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/quiz_3:_integrated_database_editor: Quiz 3: Integrated Database Editor ********************************** Verify with this quiz the acquired knowledge about SQLite database creation using the integrated Database Editor. Notice that the questions may have more than one right answer. Question 3.1: Xojo just supports adding already created SQLite databases to the project. * That's true for databases using a different SQLite library version than the one used by Xojo. * No. * Yes. Question 3.2: Once we have created a new SQLite database using the integrated Database Editor, the path to the database file will be resolved at execution time; so we can rely on this for our products' deployment. * No. It is an absolute path that we can't modify. * Only for multiplatform deployments. * Only if the database file has been moved from its original location. Question 3.3: Xojo automatically saves every change made using the integrated Database Editor, so we can rely on this. * Yes. * Only when changing from table in the same database. * No, we have to make sure to manually save all the changes. Question 3.4: We can limit the field length for SQLite tables using the Length property. * This has no effect in SQLite. * Of course, we can find this ability under the Inspector Panel and it is applied to the database file schema. * It only applies to Integer Type columns. Question 3.5: Index creation for a Table… * Allows ordering alphabetically the records. * Extends the kind of queries we can make on the database. * Improves the queries performance over columns included in indexes. .. _/topics/databases/supported_engines/sqlite/sqlite_basics/quiz_4:_rowset: Quiz 4: RowSet ************** This quiz lets you verify the acquired knowledge about the RowSet associated to a database query. Notice that the questions may have more than one right answer. Question 4.1: A RowSet is… * The class we use to get the rows (records) from a database query. * A way to insert new records in a database table. * A way to update records. * Something we can use to create new tables in the database. Question 4.2: How can we get a fresh RowSet? * Getting a new instance as result from a database query. * Through the Column method. * Calling the EditRow method. * Setting it to Nil. ============================ SQLiteDatabase for beginners ============================ In the :doc:`SQLite Basics` lesson we learned that SQLite is the database engine that allows us to create really powerful and simple multiplatform apps without needing to write a bunch of code. In this next course we will see how to create and work with SQLite databases via code, both in memory database and file based SQLite databases. We will also see how to access and use the SQLite database files created using other tools and apps, and how to use the most frequently used features like the creation of new records or retrieving the data set matching a given criteria. If you didn't go through the :doc:`SQLite Basics` lesson, I sincerely recommend you to complete it before advancing to this one. Read each section and watch the accompanying videos (in Spanish with English subtitles). .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/classes_for_using_sqlite_databases: Classes for Using SQLite databases ---------------------------------- Xojo offers a class you can use when working with SQLite database. This is :doc:`SQLiteDatabase` which you use for the following types of Xojo projects: * Console. * Desktop. * Web * iOS * Android For both 32 and 64 bits deployments. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/creating_an_in-memory_database: Creating an In-memory database ------------------------------ Probably the first thing you visualize when talking about databases is a categorized and well structured set of data stored in a physical medium, generally a disk; nevertheless —as we saw in the SQLite Basics Tutorial— SQLite also can create in-memory databases, that are only limited by the amount of available RAM in the computer and, mainly, for the selected architecture for the deployment: as 32 or 64-bit. For 32-bit deployment the total amount of available memory for the app will be around 2 or 3 GB, for 64-bit apps the upper limit will not be a problem at all. In fact, the use of in-memory databases provides a great advantage when implementing some features because they are faster both in retrieving records and in creating, editing or deleting existing data, when compared to those based on files. Regardless of the targeted platform, we need to follow these simple steps in order to create an in-memory database: Create a new instance from the SQLiteDatabase class, making sure that the instance will be in scope when we want to use and access the database. Some good candidates are a global property with public Scope under a Module or the App object due to the fact that these are global and their scope lasts until we decide to quit the app. The second step is using the method to Connect with the database…and that's all! For example, this is what we do in the following snippet of code, where the dbsource variable is a property with public scope that has been added to the App object: .. code:: xojo dbsource = New SQLiteDatabase Try dbSource.Connect Catch e As DatabaseException MessageBox(e.Message) End Try Note that the Connect method returns a boolean value indicating the success or failure of the operation: True if it succeeded, and False if there has been an error during the operation. Thus, it's always a good practice to check the returned value by the Connect method both for in-memory and file based SQLite databases; as it is wrapping the code in a Try…Catch block. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/how_to_create_a_table: How to create a table --------------------- After connecting to the SQLite database we will be all set to work with it. Because this is an in-memory database created from scratch, the first thing we need to do is to define the table or tables in charge to store our records (data). For this, the SQLiteDatabase Class provide the ExecuteSQL method that get as parameter the string with the SQL statement we want the database to execute. Remember that **ExecuteSQL** is the method that we have to use when we don't need to retrieve data as result of the operation, as when creating tables and also when inserting new records (rows). Thus, we can use the following SQL statement in order to create a basic table composed of a unique and autoincremented ID field, and two additional text fields to store the name and surname of every database record: .. code:: xojo dbsource.ExecuteSQL("CREATE TABLE test(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT)") Good Practice: It is considered a good practice to make sure that there is not an error when executing any operation against the database. This is something we can do simply wrapping any SQLiteDatabase operation in a Try…Catch block. Of course, we can create as many tables as we need for our databases, and even modify them once they had been created both for adding new columns, and for modifying the name of existing table columns. For example, we can execute the following SQL statement to modify the just created table, adding a new column with the name age: .. code:: xojo dbsource.ExecuteSQL("ALTER TABLE test ADD age INTEGER") .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/creating_new_records_(rows): Creating new records (rows) --------------------------- Xojo provides several ways we can use to add new records to any of the available database tables. Next, we will see how we can do it using the :doc:`DatabaseRow` Class and also directly using the SQL language. The simplest way of adding new rows to a database table is using an instance of the DatabaseRow class. Once we have a valid instance, we just need to assign the desired value to the column we are interested in. The class also provides the methods for assigning and retrieving several kind of value types for the column: boolean, currency, real, integer and 64-bits integer, picture and BLOB (RAW data, generally a big volume of data). As you probably know, SQLite is agnostic about the type defined for a column, so we generally use the generic Column method both for getting and setting the value of a column. For example, this fragment of code will add a total of ten records to the 'test' table created for our in-memory database: .. code:: xojo Var nr As DatabaseRow For n As Integer = 0 To 10 nr = New DatabaseRow nr.Column("Name").StringValue = "Name: " + n.ToString nr.Column("Surname").StringValue = "Surname: " + n.ToString dbsource.AddRow("test", nr) Next .. Note:: SQLite is case-insensitive, meaning it treats the use of uppercase and lowercase letters the same when referring the table fields. We also can insert new records in the database executing an SQL statement. For example, these lines of code will produce the same result: .. code:: xojo Try For n As Integer = 0 To 10 dbsource.ExecuteSQL("insert into test(name,surname) values('Name: " + n.ToString + "', 'Surname: " + n.ToString + "')") Next Catch e As DatabaseException MessageBox(e.Message) End Try As you can see, the main advantage of this approach is that you write less code. The counterpart is that it can be less clear when you read the code! In addition, note the use of the single quotes to enclose the values passed as strings as part of the SQL statement. If we forget any of these details, the SQL operation will fail and we will get an error as response, instead of creating new records in the database. But there is still a better approach! As you can see, we are providing the data to insert in the record "as-is". That is not a bad thing if the data is generated in code (because you're probably in control about what is going to be inserted). But, what happened if the data to insert in a new record comes from the user? In that cases we can be faced to scenarios where the user introduces some specific characters as part of the input itself that can result in what is know as SQL injection attacks. And that is not a good think at all. In order to prevent these scenarios we can use other syntax for the **ExecuteSQL** method, known as a Prepared Statement. When using this syntax, we provide the '?' character as a placeholder of the value to be inserted in the SQL statement itself, followed by the real values in the expected order. So, for example, the previous snippet would be like this: .. code:: xojo Try For n As Integer = 0 To 10 dbsource.ExecuteSQL("INSERT INTO TEST(name, surname) values(?,?)", "Name: " + n.ToString, "Surname: " + n.ToString) Next Catch e As DatabaseException MessageBox(e.Message) End Try .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/knowing_the_last_added_row: Knowing the last added row -------------------------- It is very common for most of SQLite database tables to include a column that uniquely identifies every record. This way it is easier to use that value to retrieve a given record or to execute any operation affecting only those records we are interested in. The SQLiteDatabase class allows us to know which is the last record added to the database simply calling the **LastRowID** method. Note that, as for the nature of SQLite, all the tables have by default a column named **RowID**. If your tables doesn't have a column defined as primary key, something that's advisable, then the LastRowID method will return the value from the by default column; otherwise it will return the value for the column you've defined for the table and that will work, in fact, as an alias for the RowID column. Thus, we just need to employ the following line of code in order to know the value of the RowID corresponding to the last added row: .. code:: xojo MessageBox(dbsource.LastRowID.ToString) .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/retrieving_rows: Retrieving rows --------------- Sooner or later we will need to retrieve a set of rows (records) matching a specified criteria. We can get just one record or a group of records. For that kind of operation, we use the :doc:`RowSet` class to work with the retrieved records from a SQL query. As you probably remember, that's the one we have been working with briefly in the previous SQLite Basics course. RowSet provides the methods we need to move from one record to another in the row set: moving to the next, returning to the previous one, setting the record pointer cursor again to the first record of the group or jumping to the last one. The available class methods also allows us to know the total amount of records in the set using the **RowCount** method, delete the current record with the **RemoveRow** method or updating any of the record columns using the **SaveRow** method, always we have invoked the **EditRow** method previously, in first place, to instruct the **DatabaseRow** instance to block the record while we are modifying it, so it can't be modified also by other users. the row will be unlocked when we use the methods **MoveToNextRow**, to advance to the next record, **MoveToPreviousRow** to go back to the previous record, or if we close the **RowSet** executing the **Close** method or because the RowSet has become out of scope. Take care of this when you start to work with RowSet instances: * Make sure that the RowSet instance is in scope as long as you need to access the records it contains. If you get the RowSet into a method's variable, then you should know that such row set will be deleted from memory once you exit the method. * Once the last record from the RowSet has been reached, you should invoke the **MoveToFirstRow** method before iterating the records again from the beginning. * Sometimes you will get a non Nil **RowSet** object, but without records on it; so it is important to check not only if the **RowSet** returned from the database query is not Nil but also that the value returned with the **RowCount** method is not equal to zero or if the RowSet is not **AfterLastRow**. For example, we can use this fragment of code to retrieve a RowSet with all the records added to our in-memory database: .. code:: xojo Var rs As RowSet rs = dbsource.SelectSQL("SELECT * FROM test") If rs <> Nil And rs.RowCount <> 0 Then ' we can also check against rs.AfterLastRow = False ' We can access to the records returned by the database query End If Or if we want to retrieve a RowSet with a single record matching, in this case, the unique value stored by the id field: .. code:: xojo rs = dbsource.SelectSQL("SELECT * FROM test WHERE id = 8") .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/iterating_the_rows_from_a_rowset: Iterating the rows from a RowSet -------------------------------- Generally speaking, the most frequent operation once we get a **RowSet** is adding, in full or part, the information of the records to an UI control, so the user of the app can interact with these items, editing them, adding new records or deleting them (among other operations). The code you will use for that will iterate all the records, something we can do in this way (observe that **RowCount** is 1 based, while the RowSet index is 0 base, so we need to subtract 1 to the value returned by **RowCount**): .. code:: xojo Var x As Integer = rs.RowCount - 1 For n As Integer = 0 To x MessageBox("ID: " + rs.Column("id").StringValue + EndOfLine + "Name: " + rs.Column("name").StringValue + EndOfLine + "Surname: " + rs.Column("surname").StringValue) rs.MoveToNextRow Next The recommended practice is iterating the RowSet using a For…Each loop: .. code:: xojo For Each row As DatabaseRow In rs MessageBox("ID: " + row.Column("id").StringValue + EndOfLine + "Name: " + row.Column("name").StringValue + EndOfLine + "Surname: "+ row.Column("surname").StringValue) Next As we can see, we get in both cases the value for the column (field) of the table calling the **Column** method on the **RowSet**, and passing along the name of the column we are interested in as the parameter. In fact, the value returned by this method is a **DatabaseRow** instance that provides all the properties we can use to retrieve (and assign) the values for the column using all the supported data types. In our example, we use StringValue in order to get the field value as a String, hiding the use of the underlaying DatabaseRow instance thanks to the use of the dot notation that chains the result from the Column call with the access to the StringValue property on the instance returned from the first method. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/editing_and_updating_rows: Editing and updating rows ------------------------- Once we have a RowSet, we can update its contents, using the **EditRow** method in combination with the **SaveRow** method. For example, the following snippet of code will modify the name and surname of the record whose id value is equal to 8: .. code:: xojo If rs.Column("id").IntegerValue = 8 Then rs.EditRow rs.Column("name").StringValue = "John" rs.Column("surname").StringValue = "Doe" rs.SaveRow End If In addition, we can get the same result without getting a RowSet in first place. For that, we just need to execute the appropriate SQL statement on the database instance, using the ExecuteSQL method for that. This example gives the same result as the previous one: .. code:: xojo dbsource.ExecuteSQL("UPDATE test SET name = ?, surname = ? WHERE id = ?", "John", "Doe", 8) Once again, note the use of placeholders in the SQL statement and the real values in the expected order after it. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/deleting_records: Deleting records ---------------- Another operation we do frequently when using databases is deleting existing rows. As we did see, when we work with a RowSet we can use the **RemoveRow** method. This method will delete the current record pointed by the RowSet cursor. In addition, you can always execute the SQL statement to delete any record matching the provided criteria. In that case we don't need to get a RowSet in advance. As you can see, this options are more flexible, but the first one is the most simple as long the record to delete is part of a valid RowSet. For example, if we would like to delete the current record from a RowSet instance assigned to the rs variable, we just need to execute this: .. code:: xojo rs.RemoveRow But if we prefer to use the most flexible formula via a SQL statement, then we have to use the following line of code to delete the record whose id value is equal to 8: .. code:: xojo dbsource.ExecuteSQL("DELETE FROM test WHERE id = ?",8) Or delete any record that matches the provided criteria, as for example all the records whose name field is equal to Javier and the age is greater than 40: .. code:: xojo dbsource.ExecuteSQL("DELETE FROM test WHERE name = ? AND age > ?","Javier",40) And if we would like to delete all the records from a database table, then we only need to provide the table name: .. code:: xojo dbsource.ExecuteSQL("DELETE FROM test") ' deletes all the records from the table .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/creating_file-based_sqlite_databases: Creating file-based SQLite databases ------------------------------------ By now, we have been working with an in-memory database, the fastest option from the performance point of view. But in general we will want to access a file based SQL database, probably created using other tools or that has been created from scratch by our code. In both cases, the way to do it is very similar to the one we did see already to create an in-memory based database. The main difference is that we need to provide a :doc:`FolderItem` object to the database instance. This one will point to the database file itself. For example, the following snippet of code will create an SQLite database on a file on disk with the name "test.sqlite" under the Documents folder for any of the supported operating systems: .. code:: xojo DBSourceDisk = New SQLiteDatabase Var file As FolderItem = SpecialFolder.Documents.Child("test.sqlite") If file <> Nil Then Try DBSourceDisk.DatabaseFile = file DBSourceDisk.CreateDatabase Catch e As DatabaseException MessageBox("Error while creating and connecting to the database file.") End Try Else MessageBox("Error creating the file on disk.") End If ' From this point on, you can use the file based database In this example, the DBSourceDisk property is created in a Module with the scope set to Global. As you can see, it doesn't matter that the variable pointing to the FolderItem gets out of scope once the Method execution ends; that's because it has been assigned previously to the DatabaseFile property on our database instance. Please, put special attention to the **CreateDatabase** method; it does a double task: * Creates the database file. * Connects to the database, so we don't need to call the Connect method. In addition, if you want to Connect to an existing SQLite database file, then this method will work as if you had called the **Connect** method. Try to execute all the operations we have seen until now: creation of a table, adding new records, executing queries, getting RowSets, editing and deleting records on the file based database, etc. By the way, try to open the file using a Text Editor and you will discover that the records data and even the database schema is clear and in a human readable format. We will see how we can solve this ciphering the stored data on the database file. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/database_backup: Database backup --------------- There is an important concern especially when dealing with in-memory databases: we will lose all the stored data once we quit the application, or if the computer that runs the app suddenly shutdowns or freezes. However, for these and other issues we can find and use the **Backup** method found in the SQLiteDatabse. This method will copy both the database structure and the available records on the provided SQLite database based on file. It is important to notice that if the database instance provided as the target for the backup contains data, it will be overwritten with the data found in the database we are going to backup. In addition, the **Backup** method can work in synchronous or asynchronous modes. With the first mode, the app execution will not resume until the backup task is finished. In asynchronous mode, the backup process will not interrupt the responsiveness of the application, but the counterpart is that its implementation requires a bit more of work from our side. For example, we can use the following code to start a synchronous backup from the in-memory database, pointed by the dbsource instance, to the file based database, pointed by the DBSourceDisk instance: .. code:: xojo dbsource.Backup(DBSourceDisk, Nil, -1) MessageBox("Backup Finished") As we can see, the Backup method gets a total of three parameters: * A valid target SQLite database instance. * A handler pointing to an object (that implements the :doc:`SQLiteBackupInterface` interface) and that will be called to inform about the backup process when executed in asynchronous mode. Pass Nil for this parameter when executed in synchronous mode. * Operation frequency, set in milliseconds. This is the amount of time between chunks of data saved to the target SQLite database. Higher values means a more responsive application. Use -1 to set the synchronous mode, as shown in the example. Of course, you can use the Backup method not only to copy in-memory SQLite databases to a file, but also for creating backups from file-based SQLite databases. It is important to notice that the synchronous mode will pause the app execution until the backup has finished. Thus, our example will not display the "Backup Finished" message until the backup has finished. This pause can represent an inconvenience in some cases, because the app will not be responsive to the user interactions in UI. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/asynchronous_backup: Asynchronous backup ------------------- The second backup mode implies passing along an instance of a class implementing the :doc:`SQLiteBackupInterface` class interface, that has to be in scope at least until the backup process has finished. In fact, this will be the object that will receive the information about the Backup progress via the three methods defined by the Class Interface: * **Complete**. This is the method called when the backup process is complete. * **Error**. If there is an error during the backup process, then our instance will get it through the errorCode parameter. * **Progress**. This is the method called at regular intervals, providing the percentage of progress via the Progress parameter. The second provided parameter, Cancel, that is passed by reference, will let us to cancel the backup process assigning the boolean value True to it. For example, we can add a new class to our example project named MySQLiteProgress; clicking the Interfaces button on the associated Inspector Panel in order to select and implement the methods from the SQLiteBackupInterface class interface. Once confirmed, we will see how Xojo adds the three methods to the just created class. Please, write the following line of code for the Complete method: .. code:: xojo MessageBox ("Backup Finished") And add the following line of code for the Error method: .. code:: xojo MessageBox ("Error: ") + errorCode.ToString Next, we can add a property named SQLiteBackupProgress, using MySQLiteProgress as the data type, and Public as the Scope value), to a Global Module or to the App object so it will be in scope during all the app life. Now, we can change the backup code so it works in asynchronous mode: .. code:: xojo SQLiteBackupProgress = New MySQLiteProgress dbsource.BackUp(DBSourceDisk, SQLiteBackupProgress) As you can see from the previous code, we create in first place a new instance from our class, in charge of receiving the messages related with the backup progress. Then, we use that instance as the parameter for the Backup method, using the by default value for the frequency parameter (10). .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/ciphering_sqlite_databases: Ciphering SQLite databases -------------------------- Try to use a Text Editor to open the file of the SQLite database created in the previous example (as result of the backup). You will see that both the database scheme and the records data are in a human-readable form (plain text). .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlitedatabase_for_beginners_unencripted_sqlite_database.png Maybe this is not a problem in some cases, depending on how we expect to use the database; but I'm pretty sure that in most of the cases you will want your data protected and ciphered so no one can read or change it. The good news is that it's really simple to create ciphered databases using Xojo. In addition, since Xojo 2018r1 we can do it using a stronger ciphering length of 256 bits in comparison with the 128 bits used in previous releases. This is really important to keep the information secure in the database. However, Xojo will use by default the ciphering length of 128 bits; so, in order to use the 256 bits length, we need to use the string 'aes256:' as a prefix when providing the password. When ciphering a SQLite database in Xojo we have to deal with three possible scenarios: * Ciphering a new database created from scratch via code. * Connecting to an already ciphered database. * Ciphering an already created SQLite database. For the first case, we need to assign the password to use in the ciphering process in the **EncryptionKey** property of the SQLite database instance, before calling the **CreateDatabase** method. For example, we can modify the code we used previously for the file based SQLite database creation, so it is ciphered: .. code:: xojo If file <> Nil Then DBSourceDisk.DatabaseFile = file DBSourceDisk.EncryptionKey = "aes256:5uper_2ecurePa55m0r9!" Try dbsourcedisk.CreateDatabase Catch error As IOException MessageBox("Error connecting to the database") End Try Else MessageBox("Error found while creating the file") End If Run the example project. It will create again the backup from the in-memory database to the file based SQLite database. If you try to open now the database file using a Text Editor, both the database scheme and the database data itself are not in a human-readable form anymore! .. image:: https://documentation.xojo.com/topics/databases/supported_engines/sqlite/images/sqlitedatabase_for_beginners_cyphered_sqlite_database.png In order to connect to an already ciphered database, we need to use the same password used during the ciphering process before calling the Connect method. For example: .. code:: xojo DBSourceDisk.DatabaseFile = file DBSourceDisk.EncryptionKey = "aes256:5uper_2ecurePa55m0r9!" If Not dbsourcedisk.Connect Then MessageBox("Error connecting to the database") End If Finally, we use the Encrypt method to ciphering an already available SQLite database after connecting to it using the Connect method. For example: .. code:: xojo DBSourceDisk.DatabaseFile = file If Not dbsourcedisk.Connect Then DBSourceDisk.Encrypt("aes256:5uper_2ecurePa55m0r9!") MessageBox("Error connecting to the database") End If The Encrypt method also provides two additional functions, always called after Connect: * **To modify the password of an already ciphered database**. For this we provide the string used as the new password as the parameter. * **Decrypt a previously ciphered database**. For this we pass an empty string as the parameter. For example, try this: .. code:: xojo If file <> Nil Then DBSourceDisk.DatabaseFile = file DBSourceDisk.EncryptionKey = "aes256:5uper_2ecurePa55m0r9!" Try dbsourcedisk.CreateDatabase Catch error As IOException MessageBox("Error connecting to the database") End Try Else MessageBox("Error found while creating the file") End If DBSourceDisk.Encrypt("") What happens when executing this code? Open the resulting database file with a Text Editor and you will see that the contents are legible again, because we have unencrypted the data using Encrypt("") just right after we ciphered it a couple of lines before. Of course, we can find the Decrypt method in Xojo to decrypt a database that has been previously ciphered. In that case, the Decrypt method needs to be executed once we have connected to the ciphered database. For example: .. code:: xojo If file <> Nil Then DBSourceDisk.DatabaseFile = file DBSourceDisk.EncryptionKey = "aes256:5uper_2ecurePa55m0r9!" If Not dbsourcedisk.Connect Then MessageBox("Error connecting to the database") End If Else MessageBox("Error found while creating the file") End If BBSourceDisk.Decrypt Of course, we don't need to decrypt the database contents in order to use it! That is, once we have connected to a ciphered SQLite database we will be able to execute any SQL statement with the SQLExecute and SQLSelect methods, and use the available **RowSet** and **DatabaseRow** classes (among others) as we did see. Thus, we only need to decrypt a previously ciphered database when we really want to make all the content available in a clear or human readable form, but not for a regular use. However, note that there is still a weak point in the code itself, because we are providing the password as a human readable String that can be retrieved using a Text Editor or a specific development tool over our compiled project. Thus, is a common practice to encrypt the password used both for ciphering a database and/or accessing an already ciphered database, and assign it to a constant; deciphering such password constant when it's time to be used in combination with the Encrypt method or the EncryptionKey property. This way, the password would not be readable even when we use a Text Editor or development tool in order to extract the strings from our compiled app. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/multiuser_sqlite_databases: Multiuser SQLite databases -------------------------- Although SQLite does not support multiple users accessing from different apps, you can enable Multiuser support from a single app (usually a web app allowing multiple users to connect to it) by setting the :ref:`WriteAheadLogging` property from the database instance to **True** after connecting to it with Connect method. This enables a feature called Write-Ahead Logging (WAL). In addition, and even if the database is not expected to be accessed by multiple users at once, the activation of this feature improves the overall performance by reducing the writings on the file used by the database. .. warning:: Do not use SQLite files on shared network drives. The `SQLite organization does not support or recommend this `_ as it is significantly slower and may cause data corruption. Thus, activating the multiuser mode is as simple as this: .. code:: xojo DBSourceDisk.DatabaseFile = file If Not dbsourcedisk.Connect Then MessageBox "Error connecting to the database" End If DBSourceDisk.WriteAheadLogging = True The activation of the WAL mode (Write-Ahead Logging), or multiuser mode, has several implications: * The database can't be opened only in Read mode (so make sure you are creating the database file in a path with read/write permissions). * After connecting to the database, the app will create two additional files along the main database file: .sqlite-wal and .sqlite-shm. * The WAL mode doesn't work on network operating systems. * The database only can be accessed by processes hosted on the same computer where is located the database file. This can be the case of a Web Server app, where it has to process several requests from clients, retrieving data form the SQLite as response for each one of these sessions. In this case, both the web server app and the SQLite database are located on the same computer. * The WAL feature requires SQLite 3.7.0 or later. The changes stored by the temporary files (.sqlite-wal and .sqlite-shm) will be made effective on the main database file when the database is closed by code or by exiting the app that works with it. Then, these temporary files will be deleted. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quizzes: Quizzes ------- Take these quizzes to test your knowledge of SQLite with Xojo. Check your answers on the :doc:`Answers` page. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_1:_available_sqlite_classes: Quiz 1: available SQLite classes ******************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 1.1: SQLiteDatabase is available for all the supported platforms. * Yes. * No. Question 1.2: What are the platforms supported by SQLiteDatabase? * Desktop, Web * Web, Console, Mobile (iOS & Android). * Desktop, Web, Mobile (iOS & Android) * Desktop, Console, Web * Desktop, Web, Mobile (iOS & Android), Console Question 1.3: SQLite can be used only in single user mode. * No. * Yes. Question 1.4: SQLite works in 32-bit mode even when the app is deployed as 64-bit. * True. * False. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_2:_in-memory_sqlite_databases: Quiz 2: In-memory SQLite databases ********************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 2.1: A Xojo app only can use one in-memory database at once. * Yes. * No. Question 2.2: Sort the following lines of code to create an in-memory database * Exit * MessageBox ("Error Creating the Database") * dbsource = New SQLiteDatabase * If Not dbsource.Connect Then * End If Question 2.3: We only can create in-memory databases that are going to use less than 2 GB of RAM * True. * False. Question 2.4: Once a table has been created, we can… * Delete columns. * Modify some of the current columns. * Add new columns to the table. Question 2.5: Once we create a SQLite database instance and assign it to a variable we can start using it. * Yes. * No. * Yes, always that the variable or property is in scope during database use. Question 2.6: We still can use an in-memory database the next time we run the app. * Yes, always it has valid data * No, the database is deleted once we exit the app. Question 2.7: Once we create an SQLiteDatabase we just need to use the Connect method to start using it. * Yes. * No. * Yes, always the Connect method has returned True as result. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_3:_tables: Quiz 3: tables ************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 3.1: SQLite sets a maximum number of tables for a database. * No. * Yes. Question 3.2: In order to create a table in Xojo we will use the SelectSQL method from the SQLiteDatabase. * True. * False. Question 3.3: After creating a Table using ExecuteSQL we will get the result of the operation as a Boolean value. * True. * False. Question 3.4: Once a Table has been created, it is possible to modify the name of any of the defined columns * True. * False. Question 3.5: Once a Table has been created, it is possible to deleted any of its columns. * True. * False. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_4:_databaserow: Quiz 4: DatabaseRow ******************* Use this Quiz to check what you have learned. Notice that the questions may have more than one right answer. Question 4.1: The name of the Class to add new records to the database is… * SQLiteRecord. * DatabaseField. * Record. * DatabaseRow. Question 4.2: We have to observe the correct use of lowercase and uppercase letters in the name when referring the database columns. * Yes. * No, it doesn't matter when using SQLite. Question 4.3: The method name of the SQLiteDatabase class to add new records is… * AddRow. * InsertField. * AddDatabaseRecord. * InsertRecord. Question 4.4: We can add new records using a SQL statement executed with SelectSQL on the database instance. * True. * False. Question 4.5: Sort the lines to add a record using the DatabaseRow Class. * dbsource.AddRow("prueba", nr) * Var nr As New DatabaseRow * nr.Column("name").StringValue = "Name: " + n.ToString .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_5:_retrieving_records: Quiz 5: retrieving records ************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 5.1: The name of the class we use to navigate and access the records from a database query is… * DatabaseRecord. * DataGroup. * Records. * RecordList. * RowSet. Question 5.2: We can iterate the records from a RowSet as many times as we need. * Yes. * No. * Yes, always we move the cursor pointer to the first record in first place after each iteration. Question 5.3: Once we get a RowSet we can access to it from any point of our app. * Yes. * No. * It depends if the variable or property that references the RowSet is in scope. Question 5.4: We just need to test the RowSet for Nil in order to start using it * True. * Not always. * We also need to make sure that AfterLastRow is False, for example. Question 5.5: RowSet offers methods to Delete, Edit and Insert records among other operations. * Yes. * No. * Only Delete and Edit, among other actions. Question 5.6: What are the method names we can use to navigate the RowSet? * MoveToFirstRow, MoveToNextRow, MoveToPreviousRow, MoveToLastRow. * JumpFirst, Next, Rewind, JumpLast. * MoveFirst, Next, Previous, MoveLast. * MoveNext, MovePrevious Question 5.7: The method we use to know the number of records in a RowSet is… * RowCount. * Records. * NumberOfRecords. * TotalRecords Question 5.8: A RowSet is closed when… * We call the Close method on the instance. * The instance gets out of Scope. * The App quits. * A RowSet never closes, it's always available. * We get an Error. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_6:_editing_rows: Quiz 6: editing rows ******************** Use this Quiz to check what you have learned. Some questions may have more than one right answer Question 6.1: What method should we use to access a column value from a record? * Value. * Record. * DataField. * Column. Question 6.2: Can we directly access a column value using the Column method? * Yes, sure. * No, we have to query the database using SQLSelect. * No, we have to use a DataRecord instance. * No, we do it using a DatabaseRow instance. Question 6.3: What RowSet method should we call before changing a record data? * Prepare. * Rollback. * DataEdit. * EditRow. Question 6.4: What's the main advantage of using ExecuteSQL instead of a RowSet method when updating a record? * None, in fact it is preferable and easier to use RowSet. * SQLExecute provides greater flexibility. * SQLExecute provides greater flexibility and returns a new RowSet with the data already updated. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_7:_file_based_sqlite_databases: Quiz 7: file-based SQLite databases *********************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 7.1: Sort the lines of code to create a file based SQLite database * DBSourceDisk.DatabaseFile = file. * MessageBox "Error Creating the Database". * DBSourceDisk = New SQLiteDatabase. * If Not DBSourceDisk.CreateDatabase Then * If file <> Nil Then * End If * End If * Var file As FolderItem = SpecialFolder.Documents.Child("tet.sqlite") * Else * MessageBox "Error Found while Creating the File" Question 7.2: What's the effect when calling the 'CreateDatabase' on a file that already has SQLite data on it? * It will delete the current contents, initializing again the file in order to be used by the SQLite database instance. * The app will raise an Exception. * The app will create a copy of the file, initializing it so it can be used by the SQLite database instance. * It will act as if the method 'Connect' was called. Question 7.3: SQLite creates the database file encrypted by default. * True. * False. * It's true, but using a 128 bits length. Question 7.4: What SQL statement we will use to delete all the records from a Table? * DELETE FROM table_name. * DELETE * FROM table_name. * DELETE all FROM table_name. * DELETE FROM table_name WHERE id = *. * DROP TABLE table_name. Question 7.5: Can we change the file used by a SQLite database instance on the fly? * Yes. * No. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_8:_backup: Quiz 8: backup ************** Use this Quiz to check what you have learned. Notice that the questions may have more than one right answer. Question 8.1: We only can backup in-memory databases * Yes. * No. Question 8.2: The backup is done over… * A valid FolderItem instance that is in scope during all the process. * Another in-memory or file based database. * Another file based and initialized SQLite database. Question 8.3: What modes can we use to backup a SQLite database? * Synchronous and Asynchronous modes. * Synchronous, Direct and Muliuser modes. * Synchronous and Asynchronous modes. Question 8.4: What measure of time should we use to set the period in a Synchronous backup? * Seconds. * Microseconds. * Milliseconds. * We have to pass -1 as the value for the parameter. Question 8.5: Once the backup has finished, the SQLite database instance will use the file of the target database. * True. * False. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_9:_asynchronous_backup: Quiz 9: asynchronous backup *************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 9.1: Why is it preferable to use asynchronous backups instead of synchronous? * The app is not blocked. * It only works with big database files. * It provides more control, for example the ability to cancel the backup process. * We can receive information about the process progress and possible errors. Question 9.2: What is the name of the Class Interface that we have to implement in the class used as backup handle? * SQLiteBackupInterface. * SQLiteBackupProgress. * SQLiteBackupInformation. Question 9.3: Do we have to implement (add code) in all the methods added by the SQLiteBackupInterface? * Yes. * No. Question 9.4: The object used as backup handle, has to be in scope during all the process? * Yes. * No. * Only if we had implement code on any of the Class Interface methods. Question 9.5: It's mandatory to provide the time period parameter to the Backup method? * No. * Yes. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners/quiz_10:_ciphering_sqlite_databases: Quiz 10: ciphering SQLite databases *********************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 10.1: Xojo uses by default a ciphering length of 256 bits? * Yes. * No. Question 10.2: What prefix string should we use in the password to enable the 256 bits ciphering length? * aes256: * 256: * aes256_ * maxSecure: Question 10.3: What method should we use in order to encrypt an already existing SQLite database file? * Encrypt. * EncryptionKey. * CipherDB. * Encrypt(""). Question 10.4: How can we decrypt an already ciphered database once we are connected to it? * Encrypt(""). * Decrypt. * Wipe. * EncryptionKey = "". ========================================= SQLiteDatabase for beginners quiz answers ========================================= These are the answers for the Quizzes in the :doc:`SQLiteDatabase for Beginners` Lesson. Correct answers are marked in **bold**. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_answers: Quiz answers ------------ .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_1:_available_sqlite_classes: Quiz 1: Available SQLite classes ******************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 1.1: SQLiteDatabase is available for all the supported platforms. * **Yes**. * No. Question 1.2: What are the platforms supported by SQLiteDatabase? * Desktop, Web * Web, Console, Mobile (iOS & Android). * Desktop, Web, Mobile (iOS & Android) * Desktop, Console, Web * **Desktop, Web, Mobile (iOS & Android), Console** Question 1.3: SQLite can be used only in single user mode. * **No**. * Yes. Question 1.4: SQLite works in 32-bit mode even when the app is deployed as 64-bit. * True. * **False**. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_2:_in-memory_sqlite_databases: Quiz 2: In-memory SQLite databases ********************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 2.1: A Xojo app only can use one in-memory database at once. * Yes. * **No**. Question 2.2: Sort the following lines of code to create an in-memory database * dbsource = New SQLiteDatabase * If Not dbsource.Connect Then * MessageBox("Error Creating the Database") * Exit * End If Question 2.3: We only can create in-memory databases that are going to use less than 2 GB of RAM * True. * **False**. Question 2.4: Once a table has been created, we can… * Delete columns. * Modify some of the current columns. * **Add new columns to the table**. Question 2.5: Once we create a SQLite database instance and assign it to a variable we can start using it. * Yes. * No. * **Yes, always the variable or property is in scope during database use.** Question 2.6: We still can use a in-memory database the next time we run the app. * Yes, always it has valid data * **No, the database is deleted once we exit the app.** Question 2.7: Once we create an SQLiteDatabase we just need to use the Connect method to start using it. * Yes. * No. * **Yes, always the Connect method has returned True as result.** .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_3:_tables: Quiz 3: tables ************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 3.1: SQLite sets a maximum number of tables for a database. * **No**. * Yes. Question 3.2: In order to create a table in Xojo we will use the SQLSelect method from the SQLiteDatabase or iOSSQLiteDatabase classes. * True. * **False**. Question 3.3: After creating a Table using SQLExecute we will get the result of the operation as a Boolean value. * True. * **False**. Question 3.4: Once a Table has been created, it is possible to modify the name of any of the defined columns * **True.** * False. Question 3.5: Once a Table has been created, it is possible to deleted any of its columns. * True. * **False**. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_4:_databaserecord: Quiz 4: DatabaseRecord ********************** Use this Quiz to check what you have learned. Notice that the questions may have more than one right answer. Question 4.1: The name of the Class to add new records to the database is… * SQLiteRecord. * DatabaseField. * Record. * **DatabaseRecord**. Question 4.2: We have to observe the correct use of lowercase and uppercase letters in the name when referring the database columns. * Yes. * **No, it doesn't matter when using SQLite**. Question 4.3: The method name of the SQLiteDatabase and iOSSQLiteDatabase classes to add new records is… * AddRecord. * InsertField. * AddDatabaseRecord. * **InsertRecord**. Question 4.4: We can add new records using a SQL statement executed with SQLSelect on the database instance. * True. * **False**. Question 4.5: Sort the lines to add a record using the DatabaseRecord Class. * Var nr As New DatabaseRecord * nr.Column("nombre") = "Nombre " + n.ToString * dbsource.InsertRecord("prueba", nr) .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_5:_retrieving_records: Quiz 5: retrieving records ************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 5.1: The name of the class we use to navigate and access the records from a database query is… * DatabaseRecord. * DataGroup. * Records. * RecordList. * **RecordSet**. Question 5.2: We can iterate the records from a RecordSet as many times as we need. * Yes. * No. * **Yes, always move the cursor pointer to the first record in first place.** Question 5.3: Once we get a RecordSet we can access to it from any point of our app. * Yes. * No. * **It depends if the variable or property that references the RowSet is in scope.** Question 5.4: We just need to test the RecordSet for Nil in order to start using it * True. * Not always. * **We also need to make sure that AfterLastRow is False, for example.** Question 5.5: RecordSet offers methods to Delete, Edit and Insert records among other operations. * Yes. * No. * **Only Delete and Edit, among other actions.** Question 5.6: What are the method names we have to use to navigate the RecordSet? * **MoveFirst, MoveNext, MovePrevious, MoveLast.** * JumpFirst, Next, Rewind, JumpLast. * MoveFirst, Next, Previous, MoveLast. * MoveNext, MovePrevious Question 5.7: The method we use to know the number of records in a RecordSet is… * **RecordCount**. * Records. * NumberOfRecords. * TotalRecords Question 5.8: A RecordSet is closed when… * **We call the Close method on the instance.** * **The instance gets out of Scope.** * **The App quits.** * A RecordSet never closes, it's always available. * We get an Error. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_6:_editing_records: Quiz 6: editing records *********************** Use this Quiz to check what you have learned. Some questions may have more than one right answer Question 6.1: What method should we use to access a column value from a record? * Value. * Record. * DataField. * **Field**. Question 6.2: Can we directly access a column value using the Field method? * Yes, sure. * No, we have to query the database using SQLSelect. * No, we have to use a DataRecord instance. * **No, we do it using a DatabaseField instance.** Question 6.3: What RecordSet method should we call before we can modify a record data? * Prepare. * Rollback. * DataEdit. * **Edit**. Question 6.4: What's the main advantage of using SQLExecute instead of RecordSet when updating a record? * None, in fact it is preferable and easier to use RecordSet. * SQLExecute provides greater flexibility. * **SQLExecute provides greater flexibility and returns a new RecordSet with the data already updated.** .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_7:_file_based_sqlite_databases: Quiz 7: file-based SQLite databases *********************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 7.1: Sort the lines of code to create a file based SQLite database * DBSourceDisk = New SQLiteDatabase. * Var file As FolderItem = SpecialFolder.Documents.Child("tet.sqlite") * If file <> Nil Then * DBSourceDisk.DatabaseFile = file. * Try * DBSourceDisk.CreateDatabase * Catch error As IOException * MessageBox("Error Creating the Database".) * End Ty * Else * MessageBox("Error Found while Creating the File") * End If Question 7.2: What's the effect when calling the 'CreateDatabase' on a file that already has SQLite data on it? * It will delete the current contents, initializing again the file in order to be used by the SQLite database instance. * The app will raise an Exception. * The app will create a copy of the file, initializing it so it can be used by the SQLite database instance. * **It will act as if the method 'Connect' was called.** Question 7.3: SQLite creates the database file encrypted by default. * True. * **False**. * It's true, but using a 128 bits length. Question 7.4: What SQL statement we will use to delete all the records from a Table? * **DELETE FROM table_name.** * DELETE * FROM table_name. * DELETE all FROM table_name. * DELETE FROM table_name WHERE id = *. * DROP TABLE table_name. Question 7.5: Can we change the file used by a SQLite database instance on the fly? * Yes. * **No**. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_8:_backup: Quiz 8: backup ************** Use this Quiz to check what you have learned. Notice that the questions may have more than one right answer. Question 8.1: We only can backup in-memory databases * Yes. * **No**. Question 8.2: The backup is done over… * A valid FolderItem instance that is in scope during all the process. * Another in-memory or file based database. * **Another file based and initialized SQLite database.** Question 8.3: What modes can we use to backup a SQLite database? * Synchronous and Asynchronous modes. * Synchronous, Direct and Muliuser modes. * **Synchronous and Asynchronous modes.** Question 8.4: What measure of time should we use to set the period in a Synchronous backup? * Seconds. * Microseconds. * Milliseconds. * **We have to pass -1 as the value for the parameter.** Question 8.5: Once the backup has finished, the SQLite database instance will use the file of the target database. * True. * **False**. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_9:_asynchronous_backup: Quiz 9: asynchronous backup *************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 9.1: Why is it preferable to use asynchronous backups instead of synchronous? * **The app is not blocked.** * It only works with big database files. * **It provides more control, for example the ability to cancel the backup process.** * **We can receive information about the process progress and possible errors.** Question 9.2: What is the name of the Class Interface that we have to implement in the class used as backup handle? * **SQLiteBackupInterface**. * SQLiteBackupProgress. * SQLiteBackupInformation. Question 9.3: Do we have to implement (add code) in all the methods added by the SQLiteBackupInterface? * **Yes**. * No. Question 9.4: The object used as backup handle, has to be in scope during all the process? * **Yes**. * No. * Only if we had implement code on any of the Class Interface methods. Question 9.5: It's mandatory to pass the time period parameter to the Backup method? * **No**. * Yes. .. _/topics/databases/supported_engines/sqlite/sqlitedatabase_for_beginners_quiz_answers/quiz_10:_ciphering_sqlite_databases: Quiz 10: ciphering SQLite databases *********************************** Use this Quiz to check what you have learned. Some questions may have more than one right answer. Question 10.1: Xojo uses by default a ciphering length of 256 bits? * Yes. * **No**. Question 10.2: What prefix string should we use in the password to enable the 256 bits ciphering length? * **aes256:** * 256: * aes256_ * maxSecure: Question 10.3: What method should we use in order to encrypt an already existing SQLite database file? * **Encrypt**. * EncryptionKey. * CipherDB. * Encrypt(""). Question 10.4: How can we decrypt an already ciphered database once we are connected to it? * **Encrypt("").** * **Decrypt**. * Wipe. * EncryptionKey = "". ========================== SQLite basics quiz answers ========================== These are the answers for the :doc:`SQLite Basics Quizzes`. Correct answers are marked in **bold**. .. _/topics/databases/supported_engines/sqlite/sqlite_basics_quiz_answers/quiz_answers: Quiz answers ------------ .. _/topics/databases/supported_engines/sqlite/sqlite_basics_quiz_answers/quiz_1:_introduction_to_sqlite: Quiz 1: Introduction to SQLite ****************************** Use this Quiz to check what you have learned about SQLite database foundations. Notice that the questions may have more than one right answer. Question 1.1: Check all the right affirmations. * SQLite is a Client/Server database engine. * **SQLite can be used for free.** * **SQLite doesn't requires additional installations.** * SQLite doesn't support multiple users. Question 1.2: Xojo always implements the latest available SQLite Library. * **No**. * Yes. Question 1.3: Xojo supports multiple SQLite databases use. * No. * Yes, they always have the same number of Tables in their structure. * **Yes**. Question 1.4: SQLite supports in-memory databases. * Yes, they are always saved to disk before exiting the app. * No. * Only if it is also backed by a database on disk. * **Yes**. Question 1.5: Xojo supports saving an in-memory database to disk. * No. We have to have previously created a new database in disk, backing the in-memory data to it. * **Yes**. Question 1.6: What is the name of the Class (or Classes) in Xojo we can use to work with SQLite databases? * XQLite. * SQLiteDesktopBase. * **SQLiteDatabase**. * iOSDatabase. Question 1.7: Is it possible to place a SQLite database in a Server? Check all the right answers. * Only using third-party products. * **Using third-party products**. * The access to the database file is always made from an app located on the same server. * **We need to activate the WAL feature**. * No. Question 1.8: What are the advantages of using the WAL feature on SQLite? * **Multiple simultaneous accesses from multiple users**. * **A better integrity protection for the stored database data**. * Reduced memory consumption. * It is mandatory in order to create and use in-memory databases. Question 1.9: Can we use RegEx (Regular Expressions) in the SQLite queries? * **Only if we add third-party components or add-ons**. * Yes, Xojo includes RegEx support for SQLite. * No. .. _/topics/databases/supported_engines/sqlite/sqlite_basics_quiz_answers/quiz_2:_sqlite_database_structure: Quiz 2: SQLite database structure ********************************* This quiz lets you check the knowledge acquired about SQLite databases structure. Notice that the questions may have more than one right answer. Question 2.1: Does SQLite use strict Types checking for columns definition? * Yes. * **No**. * In some cases. Question 2.2: A table definition consist fundamentally of… * Cells. * Rows. * Columns and Rows. * **Columns**. Question 2.3: The SQLite data Types supported by SQLite are: * Nil. * **Integer**. * Double. * **Text**. * **Null**. * Boolean. * **Real**. * Picture. * Bolb. * **Blob**. Question 2.4: Once a table has been created, we can… * Delete columns. * Modify some of the current columns. * **Add new columns to the table**. Question 2.5: SQLite admits creating columns that will be empty during the database use. * **Yes**. * No. .. _/topics/databases/supported_engines/sqlite/sqlite_basics_quiz_answers/quiz_3:_integrated_database_editor: Quiz 3: Integrated Database Editor ********************************** Verify with this quiz the acquired knowledge about SQLite database creation using the integrated Database Editor. Notice that the questions may have more than one right answer. Question 3.1: Xojo just supports adding already created SQLite databases to the project. * That's true for databases using a different SQLite library version than the one used by Xojo. * **No**. * Yes. Question 3.2: Once we have created a new SQLite database using the integrated Database Editor, the path to the database file will be resolved at execution time; so we can rely on this for our products' deployment. * **No. It is an absolute path that we can't modify**. * Only for multiplatform deployments. * Only if the database file has been moved from its original location. Question 3.3: Xojo automatically saves every change made using the integrated Database Editor, so we can rely on this. * Yes. * Only when changing from table in the same database. * **No, we have to make sure to manually save all the changes**. Question 3.4: We can limit the field length for SQLite tables using the Length property. * **This has no effect in SQLite**. * Of course, we can find this ability under the Inspector Panel and it is applied to the database file schema. * It only applies to Integer Type columns. Question 3.5: Index creation for a Table… * Allows ordering alphabetically the records. * Extends the kind of queries we can make on the database. * **Improves the queries performance over columns included in indexes**. .. _/topics/databases/supported_engines/sqlite/sqlite_basics_quiz_answers/quiz_4:_rowset: Quiz 4: RowSet ************** This quiz lets you verify the acquired knowledge about the RowSet. Notice that the questions may have more than one right answer. Question 4.1: A RowSet is… * **The class we use to get the rows (records) from a database query**. * A way to insert new records in a database table. * **A way to update records**. * Something we can use to create new tables in the database. Question 4.2: How can we get a fresh RowSet? * **Getting a new instance as result from a database query**. * Through the Column method. * Calling the EditRow method. * Setting it to Nil. ============ Using SQLite ============ These questions are related to working with SQLite. .. _/topics/databases/supported_engines/sqlite/using_sqlite/where_can_i_find_more_information_about_sqlite_in_the_docs?: Where can I find more information about SQLite in the docs? ----------------------------------------------------------- First, be sure to check out the SQLite category: . We suggest you start with this :doc:`SQLite Basics Tutorial`. The Documentation has several SQLite topics: * :doc:`Overview` * :doc:`Column Renaming and Deleting` * :doc:`Full Text Searching` * :doc:`Write-Ahead Logging` .. _/topics/databases/supported_engines/sqlite/using_sqlite/do_you_have_resources_about_using_sqlite_and_xojo?: Do you have resources about using SQLite and Xojo? -------------------------------------------------- Yes, we have many `videos about using SQLite with Xojo `_. You can find blog posts about `Xojo and SQLite `_ on the Xojo Programming Blog. .. _/topics/databases/supported_engines/sqlite/using_sqlite/how_do_i_use_full_text_searching?: How do i use full text searching? --------------------------------- Full Text Searching gives you a way to quickly search for text in a table. The :doc:`Full Text Searching` topic describes how you can use this feature. You can also refer to these example projects: * Examples/Topics/Databases/SQLite/Full-Text Searching 4 * Examples/Topics/Databases/SQLite/Full-Text Searching 5 .. _/topics/databases/supported_engines/sqlite/using_sqlite/when_should_i_use_write-ahead_logging?: When should I use Write-Ahead Logging? -------------------------------------- The :doc:`Write-Ahead Logging` topic talks about write-ahead logging and when it makes sense to use it. .. _/topics/databases/supported_engines/sqlite/using_sqlite/how_can_i_rename_columns_in_a_sqlite_table?: How can I rename columns in a SQLite table? ------------------------------------------- SQLite has a limited ALTER TABLE command that does not directly allow you to rename a table column. You can workaround that by creating a new table and copying your data to it as described in the Renaming Columns topic. The ability to rename columns was added to SQLite 3.25 which is available starting with Xojo 2018 Release 4. =============== Other databases =============== In addition to the databases with built-in support described in the preceding sections, you can connect to just about any other database using a variety of methods. :doc:`ODBC` allows you to connect to any database for which you have an ODBC driver. Other database you can use include: * `CubeSQL `_: A cross-platform database server based on SQLite. * `Valentina Database `_: A cross-platform columnar-based database (both embedded and server). * `OpenBase SQL `_: A cross-platform database originally created for OpenStep/NeXT. * `MonkeyBread Plugins `_: MonkeyBread offers plugins to connect to databases using JDBC (Java Database Connectivity), a database connectivity standard similar to ODBC. Additionally, they have plugins to directly connect to a wide variety of databases. Xojo does not endorse any of these products. They are listed here for reference only. .. _/topics/databases/supported_engines/other_databases/see_also: .. seealso:: :doc:`ODBC` topic ==================================== Protecting your database from attack ==================================== SQL Injection is a hacking technique where access to your database is compromised by modifying SQL statements that are sent to the database. Generally, you work with databases by sending SQL commands. In particular the SELECT command is used to get data from a database. These SELECT statements are just text. This is normally not a problem, but it can be when you start to concatenate text to build an SQL statement. .. _/topics/databases/protecting_your_database_from_attack/the_problem: The problem ----------- In particular, the risk occurs when you build an SQL statement with user-supplied input. For example, say you prompt the user for some information to filter (it doesn't matter what) and then create an SQL statement by concatenating what they typed. You might have code that looks like this: .. code:: xojo Var SQL As String SQL = "SELECT * FROM Task WHERE UserName = 'Paul' AND Title = '" + userValue " + "';" If the user types nonsense data such as "Mow" as the value then the SQL looks like this: .. code:: SQL SELECT * FROM Task WHERE UserName = 'Paul' AND Title = 'Mow'; This would not return any rows. But what if the user types something a bit more nefarious, such as "Mow' OR 1;--"? This now results in SQL that looks like this: .. code:: SQL SELECT * FROM Task WHERE UserName = 'Paul' AND Title = 'Mow' OR 1;--;' Now there are two SQL statements. The first one returns all the data in the table (because of the "OR 1") and the second statement is a comment so is ignored. Now your app is displaying lots of data that the user was not meant to see (perhaps data from other users in this case). This is called SQL Injection because the user was able to inject their own SQL into your query, all because you were using concatenation. .. _/topics/databases/protecting_your_database_from_attack/use_prepared_statements: Use Prepared Statements ----------------------- So how do you avoid this? Instead of concatenating, you use the database's built-in ability to create prepared statements by binding values to parameters. To start you first declare the SQL to have parameters: .. code:: xojo Var SQL As String SQL = "SELECT * FROM Tasks WHERE UserName = 'Paul' AND TITLE = ?;" Then you create send the SQL to the database along with the values to bind to the parameter: .. code:: xojo Var results As RowSet = MyDB.SelectSQL(SQL, userValue) Because the value is treated as a parameter, if evildoers again try to inject their own SQL, the resulting SQL sent to the database is more like this: .. code:: SQL SELECT * FROM Tasks WHERE UserName = 'Paul' AND TITLE = 'Mow* OR 1;'" This SQL statement will return no rows, which is what you want. Any apps that embed user input into SQL statements should always use prepared SQL statements. Do not think you can just write your own code to sanitize user input as this still poses a risk. And there are also reasons to use prepared statements even when SQL injection is not a concern: some databases cache the parsing of the database command, which can result in performance improvements if the database command is used repeatedly throughout the app. Note that if you use :doc:`DatabaseRow` or :doc:`RowSet` :ref:`EditRow`/:ref:`SaveRow`, you are also protected from SQL injection. .. _/topics/databases/protecting_your_database_from_attack/see_also: .. seealso:: :doc:`Database`, :doc:`DatabaseRow`, :doc:`RowSet`, :doc:`PreparedSQLStatement` classes; :doc:`Adding, updating and deleting rows` topic =========================================================== Considerations when using a database with a web application =========================================================== Using databases with web apps is not much different than using them with desktop apps, but there are a few things to keep in mind. The most important change is that a web app allows multiple users. This means you'll want your database connection to be unique for each user that connects to the web app, rather than global to the app itself (as is common in desktop apps). The simplest way to ensure this is to create the connection to your database in the :ref:`WebSession.Opening` event handler, saving a reference to the connection as a property that you add to :doc:`WebSession`. Here is an example. In the Session object, add this property: .. code:: xojo DB As SQLiteDatabase In the Opening event handler, connect to the database: .. code:: xojo Var dbFile As FolderItem = New FolderItem("MyDatabase.sqlite") DB = New SQLiteDatabase DB.DatabaseFile = dbFile Try db.Connect Catch error As DatabaseException ' Display an error page and log the error ' You should not show specifics of the error to users ErrorPage.Show End Try Although the above code is connecting to a SQLite database, you would use the same technique when you are connecting to a different database such as PostgreSQL or MySQL. By having the DB property isolated to the session, you ensure that that database transactions are specific to each connected user's session and you reduce the risk of data being exposed to the wrong session. Once you have your project set up like this, you can refer to the database like this: .. code:: xojo Session.DB .. youtube:: Cshtk7VmEqw So to retrieve data, you would do something like this: .. code:: xojo Var rs As RowSet Try rs = Session.DB.SelectSQL("SELECT * FROM Person") Catch error As DatabaseException ' Save and log error in error.ErrorNumber and error.Message ' You should not display specifics of the error to your users Return End Try For Each row As DatabaseRow In rs ListBox1.AddRow(row.ColumnAt(1).StringValue) Next rs.Close .. _/topics/databases/considerations_when_using_a_database_with_a_web_application/protecting_your_database_from_hackers_with_prepared_statements: Protecting your database from hackers with Prepared Statements -------------------------------------------------------------- Whether you are using SQLite or another database server, to avoid a security risk called SQL Injection you should always use Prepared Statements with any SQL statements that have parameters supplied by user input. With Prepared Statements you supply the SQL statement and any user-provided data separately allowing the database engine to them determine if the data contains any SQL commands and reject it is that's the case. The :ref:`SelectSQL` method has built-in support for Prepared Statements. However, there are two situations where they won't work. First, if you need to perform the same Select repeatedly (in a loop for example), then it's not efficient to use the Prepared Statements that are built-in to the :ref:`SelectSQL` method because your code would be setting them up every time it did a Select. The second would be if you need to access a special column type specific to a particular database engine. In either of these cases, use the Prepared Statement classes. They are a little more work to use but are far more efficient when quickly and repeatedly performing the same query over and over: * :doc:`SQLitePreparedStatement` * :doc:`MySQLPreparedStatement` * :doc:`PostgreSQLPreparedStatement` * :doc:`ODBCPreparedStatement` For example, to query the Person table for specific values you would use a PreparedStatement like this: .. code:: xojo ' Code for SQLiteDatabase ' SQL statement uses ? in place of parameters to be supplied later ps = Session.DB.Prepare("SELECT * FROM Person WHERE Name = ? AND Age >= ?") ps.BindType(0, SQLitePreparedStatement.SQLITE_TEXT) ' Name type ps.BindType(1, SQLitePreparedStatement.SQLITE_INTEGER) ' Age type Try Var rs As RowSet = ps.SelectSQL("john", 20) ' values for parameters For Each row As DatabaseRow In rs MessageBox("Name: " + row.Column("Name").StringValue + _ " Age: " + row.Column("Age").StringValue) Next Catch error As DatabaseException MessageBox(error.Message) End Try .. _/topics/databases/considerations_when_using_a_database_with_a_web_application/using_sqlite_instead_of_a_database_server: Using SQLite instead of a database server ----------------------------------------- SQLite is a file-based database and can be a great alternative to a database server for small to medium web sites that do not have a significant amount of database writing. Because SQLite is file-based, multiple users connected to your web app cannot write to the file at the same time. If your database writes are infrequent, short and fast this may never be a problem. One way to help mitigate this is to enable Write-Ahead Logging by setting the :ref:`SQLiteDatabase.WriteAheadLogging` property to True for your SQLite database. This improves performance for database writes. The `When to Use section of the SQLite web site `_ has this to say about using SQLite with web sites: * SQLite works great as the database engine for most low to medium traffic websites (which is to say, most websites). The amount of web traffic that SQLite can handle depends on how heavily the website uses its database. Generally speaking, any site that gets fewer than 100K hits/day should work fine with SQLite. The 100K hits/day figure is a conservative estimate, not a hard upper bound. SQLite has been demonstrated to work with 10 times that amount of traffic. * The SQLite website (https://www.sqlite.org/) uses SQLite itself, of course, and as of this writing (2015) it handles about 400K to 500K HTTP requests per day, about 15-20% of which are dynamic pages touching the database. Dynamic content uses about 200 SQL statements per webpage. This setup runs on a single VM that shares a physical server with 23 others and yet still keeps the load average below 0.1 most of the time. .. _/topics/databases/considerations_when_using_a_database_with_a_web_application/permissions: Permissions *********** With SQLite, the database file exists on the database server alongside your web app (or in a related folder). Be sure that you have the correct permissions applied to the database file or you will not be able to save changes to it. For most Linux web servers, you will typically use "755" permissions, but this can vary depending on the server. When you are connected to the server, this can be done with a terminal command like this: chmod 755 MyDatabase.sqlite .. _/topics/databases/considerations_when_using_a_database_with_a_web_application/learn_more: Learn more ---------- To learn more about databases to use with web apps, refer to the topics below: * :doc:`Database Basics for beginners` * :doc:`Adding, updating and deleting rows` * :doc:`Overview` * :doc:`PostgreSQL` * :doc:`MySQL` DBKit ===== Introduction ------------ DBKit is a module of classes and example projects designed to show and help you make it easier to build desktop and web applications that are front-ends to databases. Specifically, it makes it easy to connect a database table to the user interface controls on a layout. DBKit also handles a lot of the interface for you. For example, it will enable/disable the controls you provide for editing data. It will enable/disable buttons for saving, deleting, etc. It automatically confirms with the user when they attempt to delete a record or switch records after editing without saving first. Once you set it up, adding controls for columns in your database is very simple. Most will require no code at all. They will just work. Unlike most other example projects, this one both demonstrates DBKit and provides the source to the DBKit module itself that you can then use in your own projects. DBKit is designed to be used with interfaces where there's a list of rows in a ListBox that the user can then select from to edit either in controls on the same layout or a separate layout. It can also be used in cases where no list of rows are displayed. See the DBKit-Desktop and DBKit-Web example projects for examples of the various ways in which it can be used. Supported project types ----------------------- It currently support desktop and web projects. Mobile project support will be coming in a later update. `Supported Databases` --------------------- DBKit supports :doc:`MySQL`, :doc:`PostgreSQL` and :doc:`SQLite`. It comes with all the source so you could potentially add support yourself for other database engines. Supported Xojo versions ----------------------- DBKit requires 2023r2 or later. DBKit version ------------- DBKit is in beta. Read the :ref:`release notes` for details about specific releases. Downloading DBKit ----------------- The current version of DBKit is available from Xojo's Project Chooser. In Xojo, choose File > New Project then click on Examples > Databases to find them. Testing DBKit ------------- Run the DBKit-Desktop or DBKit-Web projects. Both have three examples of DBKit in action: .. csv-table:: :header: "Name", "Description" :widths: auto Single, Displays a ListBox with customer records that when selected can be edited via various controls on the same layout. Separate, Displays the ListBox by itself. Double-clicking on a row will then display a separate layout for editing. This example also shows how to display rows from two different tables on the same layout using two TableConnection instances (one for each table). Row Only, Performs a query then displays the first row in the the EntryWindow/EntryPage layout without the need for a ListBox displayings rows for the user to choose from. Because DBKit is new, make sure you test it well. If you run into any `bugs or have feature requests`, report them at https://issues.xojo.com and make sure to put DBKit in the title. Installing DBKit ---------------- For **Desktop** projects, open the DBKit-Desktop example project. Click on the DBKit Module, choose Edit > Copy then go to your project and choose Edit > Paste. For **Web** projects, open the DBKit-Web example project. Click on the DBKit Module, choose Edit > Copy then go to your project and choose Edit > Paste. .. important:: Always copy DBKit from the DBKit-Desktop project for desktop projects and from the DBKit-Web project for web projects. They are identical but there's an issue in the Xojo IDE at the moment copying from on project type to another. No Row locking -------------- If you are going to be using DBKit with a database server and multiple, simultaneous users, be aware that this version of DBKit does not attempt to do any row locking. The user experience of requiring the user to deliberately indicate they wish to edit by clicking the Edit button is designed to help limit occurrences where two users accidentally editing the same row at the same time. For now, if this does happen, the version of the row saved by the last user to save will be the one stored. Getting started --------------- In most cases, there are a only a few things you'll need to know about DBKit to use it: 1. How to connect to your database. 2. How to configure DBKit.TableConnection class (which only requires a few method calls). 3. How to indicate which controls on your layout are to be used with DBKit. It is highly recommended you work through the :doc:`desktop` or :doc:`web` tutorial first as it will teach you all of this. It will take about 20 minutes. The rest of this page contains details you may want to know but certainly don't need to know. The DBKit.TableConnection Class ------------------------------- This class is the heart of DBKit. To use it you will drag it to a layout then use its methods and properties. Because of its design, you won't likely have to use many of DBKit.TableConnection's methods and properties as it will automatically interrogate your layout and connect the columns from the database table you specify to the your controls. If you are connecting to your database in your App or Session Opening events and storing a reference to the connection in a property, DBKit.TableConnection instances on your layouts will automatically find that connection and use it. You can of course override this by assigning the Connection property yourself. If your layout has more than one TableConnection instance, any DBKit controls that do not have their Table and Column properties explicitly set, will be assigned to the first TableConnection instance (the leftmost in the tray area of the layout). .. note:: Only the public properties, methods and events of DBKit.TableConnection are documented as they are the only ones you would directly use. The various private class members are not documented here though you can explore them by looking through the source code itself. DBKit.TableConnection properties ******************************** .. csv-table:: :header: "Name", "Description" :widths: auto AutoConnect, Causes the TableConnection to attempt to connect to the database when the layout opens. This is True by default. Connection, The connection to the database. ConnectionName, Used when you wish to connect to multiple database targets simultaneously. Assign a unique name to this property when you create the connection and then use the same name for this property for any TableConnection objects used on layouts that should use that named connection. CreateNewRow, Indicates that a new row should be created when the layout opens. QueryRows, Assign this property the RowSet from the query you perform for the rows you wish to display. Table, The name of the database table being used. This can be set in the Inspector. DBKit.TableConnection methods ***************************** It's unlikely you will need to be familiar with any of these methods as nearly all of these methods are used internally by DBKit and are only public because they are called by other DBKit classes. .. csv-table:: :header: "Name", "Description" :widths: auto BindAllControls, "Finds all the DBKit controls on the layout then connects them to their designated columns in the database." BindDeleteButton, Makes the passed control the button to be automatically managed for deleting rows. BindEntryControl, "Binds the column passed to the control passed. If the control is named identically to the column, only the control needs to be passed." BindListBoxColumn, "Binds the database column passed to the Listbox column specified." BindListBoxControl, Makes the passed control the that will display rows that can be selected for editing. BindMoveToFirstRow, Binds the button passed to the TableConnection making it the button that will be enabled/disabled automatically depending on which row is being edited. BindMoveToLastRow, Binds the button passed to the TableConnection making it the button that will be enabled/disabled automatically depending on which row is being edited. BindMoveToNextRow, Binds the button passed to the TableConnection making it the button that will be enabled/disabled automatically depending on which row is being edited. BindMoveToPreviousRow, Binds the button passed to the TableConnection making it the button that will be enabled/disabled automatically depending on which row is being edited. BindNavigationButton, Binds the SegmentedButton passed to the TableConnection make it the button that will be enabled/disabled automatically depending on which row is being edited. BindEditButton, Makes the passed control the button to be automatically managed for editing/saving changes to rows and saving new rows as well. BindNewButton, Makes the passed control the button to be automatically managed for creating new rows. BindSearchField, Makes the passed DBKit.SearchField the control that will be used by the user to search and display rows in the QueryRowsListBox control bound to the same TableConnection. BindToolbar, Makes the passed DBKit.Toolbar the toolbar whose buttons for adding, updating and deleting rows will be automatically managed by DBKit. BindUndoButton, Makes the passed control the button to be automatically managed for reverting/undoing changes to rows. BoundQueryResultsListBox, The QueryResultsListBox that is bound to this TableConnection. ConfirmDeleteRow, Deletes the current row after confirming first with the user. CheckForRowChange, Call in events that change control data to allow DBKit.TableConnection to know if the row has changed or not. ConfirmDeleteRow, Displays a dialog box asking the user to confirm they wish to delete the currently selected row. Connect, Connects to the database. Use this when you don't want to automatically connect with the AutoConnect property. Connected, Connects to the database passed returning True if the connection was successful and False if it was not. CurrentRow, Returns the currently loaded row as a RowSet. EditRow, "Allows the user to edit the record by enabling/making read-write the bound entry controls and changes the Edit button caption to Done." HasBoundListBox, Returns True if a QueryRowsListBox is bound to this TableConnection and False if one is not. LoadSelectedRow, Loads the selected row in the bound ListBox into the bound entry controls. MoveToFirstRow, "Makes the first row in the RowSet the current row. If BindMoveToFirstRow has been called, that button will disable/enable automatically." MoveToLastRow, "Makes the last row in the RowSet the current row. If BindMoveToLastRow has been called, that button will disable/enable automatically." MoveToNextRow, "Makes the next row in the RowSet the current row. If BindMoveToNextRow has been called, that button will disable/enable automatically." MoveToPreviousRow, "Makes the previous row in the RowSet the current row. If BindMoveToLastRow has been called, that button will disable/enable automatically." NewRow, Sets all entry controls to their null values in preparation for creating a new row. PrimaryKeyColumn, Returns the name of the database column that contains the table's primary keys. RemoveSelectedRow, "After confirming with the user, the selected row is removed/deleted." TableColumnType, When passed a column name, it returns the column type as an integer. UndoRowChanges, "Reloads the controls with the values from the row when it was loaded, undoing any changes the user has made." DBKit.TableConnection events **************************** .. csv-table:: :header: "Name", "Description" :widths: auto Closing, "The layout (window, webpage) is closing." ControlsStateChanged, Indicates when DBKit.TableConnection has enabled or disabled the entry controls. LoadingRow, The row in the RowSet passed is being loaded. Return False to prevent it from loading. Opening, "The layout (window, webpage) is opening." SavingRow, The row in the RowSet passed is being saved. Return False to prevent it from saving. Notes ----- Desktop versus web projects *************************** DBKit was created to be used nearly identically in desktop and web projects. The DBKit.TableConnection class is identical in for both desktop and web projects. You can copy it from one to the other. The included control subclasses, while nearly identical, are not cross-project compatible because their super classes are specific to the project type. Having said that, if you look at the way DBKit is used in the Desktop and Web example projects, you will see that the DBKit code is nearly identical. The only place where things are different is where they have to be, for example, on the web the WebFileUploader must be used to add photos. Connecting to a database ************************ To connect to a database, call DBKit.TableConnection's Connected function. For SQLite, pass it the database file. For MySQL and PostgreSQL, pass it a DBKit.TableConnection.DatabaseServer enumeration that indicates that the connection is MySQL or PostgreSQL followed by the IP address of the server, the port, the database name, the user and the password. If the connection succeeds, the Connected function returns True. This code from the DBKit-Desktop example project connects to the SQLite database being passed to it: .. code:: xojo app.db = New DBKit.TableConnection If app.db.Connected(SpecialFolder.Resources.child("EddiesElectronics.sqlite")) Then Var w As New SingleWindow Else System.Beep MessageBox("The database could not be reached due to an error.") End If Supported controls ****************** DBKit includes a several subclasses of the existing controls. These subclasses are designed to handle the work of connecting a database column to that particular type of control. Most of them you will only use to designate a control as being a DBKit-enabled control. You do this by dragging a control from the Library to your layout, then clicking the Pencil icon next to the Super property in the Inspector and choosing the DBKit control class that appears. .. important:: For all DBKit controls, if there is only one TableConnection instance on the layout, the control will act upon that instance's QueryRows RowSet. If there is more than one instance of TableConnection, providing a matching table name (and in many cases the Column as well) is required. The following controls are supported for binding to database columns: * CheckBox * ComboBox * DateTimePicker/DatePicker * ImageViewer * ImageUploader (web only) * Label * PopupMenu * QueryRowsListBox * RadioGroup * Slider * TextArea * TextField .. note:: For DesktopDateTimePicker and WebDatePicker the database column must be Text, and cannot be null. DesktopDateTimePicker TimeOnly mode is not currently supported. DBKit.CheckBox ^^^^^^^^^^^^^^ The standard Checkbox control subclassed to work with DBKit. .. csv-table:: :header: "Property", "Description" :widths: auto AutoPopulate, "Populates the CheckBox with the unique values from the database column in its Column property." AutoPopulateColumn, "If AutoPopulate is True, this column will be used to populate the InitialValue property of the control. If this property is empty, the table in the Column property is used." AutoPopulateTable, "If AutoPopulate is True, this table will be used to populate the InitialValue property of the control. If this property is empty, the table in the Table property is used. Column, The database column from which to exchange data." Table, The database table name from which to get the columns. Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that the CheckBox be checked to save the row. DBKit.ComboBox ^^^^^^^^^^^^^^ The standard ComboBox control subclassed to work with DBKit. .. csv-table:: :header: "Property", "Description" :widths: auto AutoPopulate, "Populates the ComboBox with the unique values from the database column in its Column property." AutoPopulateColumn, "If AutoPopulate is True, this column will be used to populate the InitialValue property of the control. If this property is empty, the table in the Column property is used." AutoPopulateTable, "If AutoPopulate is True, this table will be used to populate the InitialValue property of the control. If this property is empty, the table in the Table property is used. Column, The database column from which to exchange data." Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that a value be entered or selected to save the row. Table, The database table name from which to get the columns. DBKit.DateTimePicker/DatePicker ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The standard DesktopDateTimePicker or WebDatePicker control subclassed to work with DBKit. .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that a value be entered or selected to save the row. Table, The database table name from which to get the columns. DBKit.DeleteButton ^^^^^^^^^^^^^^^^^^ A Button control that will automatically delete the current row. It will also automatically enable and disable when appropriate. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table whose matching TableConnection holds the RowSet from which row will be deleted. This is required when there's more than one TableConnection instance on the layout. DBKit.EditButton ^^^^^^^^^^^^^^^^ A Button control that when pressed will make editable all DBKit controls on the layout that are bound to the same table as the button. It will then become a Done button that when pressed will commit the user changes. It will also automatically enable and disable when appropriate. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table whose matching TableConnection holds current row and DBKit controls will be made ready for editing. This is required when there's more than one TableConnection instance on the layout. DBKit.ImageViewer ^^^^^^^^^^^^^^^^^ An Imageviewer subclass that can display a picture stored in the database. .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. CurrentImage, The image displayed in the ImageViewer. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that an image be assigned to the control to save the row. Table, The database table name from which to get the columns. DBKit.ImageUploader ^^^^^^^^^^^^^^^^^^^ Used in web projects to upload images to be displayed in a DBKit.ImageViewer. .. csv-table:: :header: "Property", "Description" :widths: auto TargetImageViewer, The DBKit.ImageViewer control an uploaded image should be sent to once the upload is complete. DBKit.Label ^^^^^^^^^^^ A Label subclass that can be bound to a column of a table. .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that an image be assigned to the control to save the row. Table, The database table name from which to get the columns. .. csv-table:: :header: "Method", "Description" :widths: auto UpdateText, Used to change the Text property so that DBKit will be aware and thus the Undo button (should you have one) will be able to undo the change and the Edit button (should you have one) will be able to save the change. DBKit.MoveToFirstRowButton ^^^^^^^^^^^^^^^^^^^^^^^^^^ A button that will move the current row to the first row and updates the DBKit controls on the layout to display that row. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table whose matching TableConnection holds the RowSet whose first row will become the current row. This is required when there's more than one TableConnection instance on the layout. DBKit.MoveToLastRowButton ^^^^^^^^^^^^^^^^^^^^^^^^^^ A button that will move the current row to the last row and updates the DBKit controls on the layout to display that row. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table whose matching TableConnection holds the RowSet whose last row will become the current row. This is required when there's more than one TableConnection instance on the layout. DBKit.MoveToNextRowButton ^^^^^^^^^^^^^^^^^^^^^^^^^^ A button that will move the current row to the next row and updates the DBKit controls on the layout to display that row. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table whose matching TableConnection holds the RowSet whose next row will become the current row. This is required when there's more than one TableConnection instance on the layout. DBKit.MoveToPreviousRowButton ^^^^^^^^^^^^^^^^^^^^^^^^^^ A button that will move the current row to the previous row and updates the DBKit controls on the layout to display that row. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table whose matching TableConnection holds the RowSet whose previous row will become the current row. This is required when there's more than one TableConnection instance on the layout. DBKit.NavigationButton ^^^^^^^^^^^^^^^^^^^^^^ A SegmentedButton control that can be configured to include First, Last, Next and Previous buttons. The buttons will navigate automatically and will enable/disable automatically. While you can add your own segments to the control in the Layout Editor or at runtime, the built-in buttons you enable must be the first buttons in the control. In other words, if you enable Next and Previous for example, the first two buttons will act as the Next and Previous buttons. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. FirstRow, Set this to True in Inspector or in the Opening event to include a First button in the control. LastRow, Set this to True in Inspector or in the Opening event to include a Last button in the control. NextRow, Set this to True in Inspector or in the Opening event to include a Next button in the control. PreviousRow, Set this to True in Inspector or in the Opening event to include a Previous button in the control. Table, "If the layout includes more than one TableConnection instance, designate the name of a table that matches the Table property of the TableConnection instance whose RowSet you wish the user to navigate." DBKit.NewButton ^^^^^^^^^^^^^^^ A Button control that when pressed will make editable all DBKit controls on the layout that are bound to the same table as the button. It will then become a Done button that when pressed will commit the user changes. It will also automatically enable and disable when appropriate. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table into which the row will be inserted. This is required when there's more than one TableConnection instance on the layout. DBKit.NumberField ^^^^^^^^^^^^^^^^^ A DBKit.TextField subclass that can be bound to a column of a table. .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DecimalPlaces, The number of decimal places allowed during data entry by the user. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that the NumberField contains a value to save the row. MaximumValue, The lowest acceptable value the NumberField can contain for the user to be able to save the row. MinimumValue, The highest acceptable value the NumberField can contain for the user to be able to save the row. Table, The database table name from which to get the columns. ThousandsSeparator, If True, a the locale-savvy thousands separator will appear when appropriate. Type, Controls both the formatting and entry of the numeric value. See NumberField.Types for details. NumberField.types ################# .. csv-table:: :header: "Type", "Description" :widths: auto Number, Any unformatted value. Currency, Formats the value using appropriate currency symbol based on the locale of the device. Custom, Uses the format assigned to the Format property. Percentage, Formats the value as a percentage. When loading the value, 1 is assumed to be 100%. Thus for example the user can enter 45.5 which will be stored in the database column as .455. .. csv-table:: :header: "Methods", "Description" :widths: auto Number, The numeric value of the NumberField returned as a Double. NumericTypeFormat, Returns as a String the format of NumberField based upon its Type property. DBKit.PopupMenu ^^^^^^^^^^^^^^^ A PopupMenu subclass that can be bound to a column of a table. It's contents can be created manually in the Layout Editor or can be created at runtime by being automatically populated via a column from a table in a database. .. csv-table:: :header: "Property", "Description" :widths: auto AutoPopulate, "Populates the PopupMenu with the unique values from the database column in its Column property." AutoPopulateColumn, "If AutoPopulate is True, this column will be used to populate the InitialValue property of the control. If this property is empty, the table in the Column property is used." AutoPopulateTable, "If AutoPopulate is True, this table will be used to populate the InitialValue property of the control. If this property is empty, the table in the Table property is used. Column, The database column from which to exchange data." Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires a value be selected to save the row. Table, The database table name from which to get the columns. DBKit.QueryRowsListBox ^^^^^^^^^^^^^^^^^^^^^^ A ListBox subclass that can display columns from a table in a database. .. csv-table:: :header: "Property", "Description" :widths: auto Columns, "The database columns (comma-separated) from which to exchange data. To skip a ListBox column, add the comma without entering a column name. For example, `FirstName,,LastName`." Connector, The TableConnection instance to which the control is connected. QueryRows, The RowSet whose data will be displayed in the bound controls. This property is write-only. .. csv-table:: :header: "Method", "Description" :widths: auto BindDatabaseColumns, "Used to connect database columns to columns of the ListBox if the Columns property is not populated." DBKit.RadioGroup ^^^^^^^^^^^^^^^^ A RadioGroup subclass that can be bound to a column of a table. .. note:: Unlike PopupMenu, the AutoPopulate option will only populate the control from a table if the RadioGroup initially has a single row in it in the Layout Editor. Otherwise, the control in the Layout Editor would have to be empty which would make it invisible in the Layout Editor. .. csv-table:: :header: "Property", "Description" :widths: auto AutoPopulate, "Populates the RadioGroup with the unique values from the database column in its Column property." AutoPopulateColumn, "If AutoPopulate is True, this column will be used to populate the InitialValue property of the control. If this property is empty, the table in the Column property is used." AutoPopulateTable, "If AutoPopulate is True, this table will be used to populate the InitialValue property of the control. If this property is empty, the table in the Table property is used. Column, The database column from which to exchange data." Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that a value be selected to save the row. Table, The database table name from which to get the columns. DBKit.SearchField ^^^^^^^^^^^^^^^^^ A SearchField subclass that automatically handles searching and displaying the results in a DBKit.QueryRowsListBox. The QueryRowsListBox is uses is the one bound to the same TableConnection as the SearchField. DBKit.SearchField will bind itself to the TableConnection on the layout whose Table property matches its Table property. If the Table property is left blank, SearchField will bind to the first TableConnection in the layout's tray. In other words, if you have a TableConnection and a QueryRowsListBox on the layout, adding a SearchField will search the table defined by the .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayAllRowsByDefault, If True, when the SearchField has no search value, all rows will be displayed. The default for this property is True. Table, The database table name that must match the Table property of a TableConnection instance on the layout in order to bind to it. If this is left blank, the SearchField will bind to the first TableConnection in the layout's tray. DBKit.Slider ^^^^^^^^^^^^ A Slider subclass that can be bound to a column of a table. .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that a value greater than the minimum value be selected to save the row. Table, The database table name from which to get the columns. DBKit.TextArea ^^^^^^^^^^^^^^ A TextArea subclass that can be bound to a column of a table. .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that the TextArea contains at least a single character to save the row. Table, The database table name from which to get the columns. DBKit.TextField ^^^^^^^^^^^^^^^ A TextField subclass that can be bound to a column of a table. .. csv-table:: :header: "Property", "Description" :widths: auto Column, The database column from which to get the value. Connector, The TableConnection instance to which the control is connected. DisplayName, The name you wish displayed when DBKit needs to display it in a dialog box. Mandatory, When checked requires that the TextField contains at least a single character to save the row. Table, The database table name from which to get the columns. DBKit.Toolbar ^^^^^^^^^^^^^ A Toolbar subclass that can provide and manage buttons to create a new row, delete the selected row, edit the selected row and undo changes to the selected row. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. DeleteButton, If True, a Delete button will be included in the Toolbar. This can be set in the Inspector or via code. Clicking the button will delete the selected row. EditButton, If True, an Edit/Done button will be included in the Toolbar. This can be set in the Inspector or via code. Clicking the button will toggle the layout between read-only and read-write mode to allow editing by the user. NewButton, If True, a New button will be included in the Toolbar. This can be set in the Inspector or via code. Clicking this button will empty the layout controls so that a new row can be entered. Table, The database table name that must match the Table property of a TableConnection instance on the layout in order to bind to it. If this is left blank, the Toolbar will bind to the first TableConnection in the layout's tray. UndoButton, If True, an Undo button will be included in the Toolbar. This can be set in the Inspector or via code. Clicking this button will reload the previously saved version of the row. DBKit.UndoButton ^^^^^^^^^^^^^^^^ A Button control that when pressed will reload the current row into any DBKit controls on the layout that are bound to the same table as the button. This has the effect of undoing any changes the user made. It will also automatically enable and disable when appropriate. .. csv-table:: :header: "Property", "Description" :widths: auto Connector, The TableConnection instance to which the control is connected. Table, The name of the table whose matching TableConnection holds the RowSet whose current row will be reloaded undoing any changes the user has made. This is required when there's more than one TableConnection instance on the layout. Binding data entry controls to database columns *********************************************** Binding is, for the most part, automatic. You can either name your controls to match the names of the columns from the database table or put the column name for the control in its Column property in the Inspector. DBKit controls have a Table property as well. This is only required in cases where you are using two DBKit.TableConnections for two different tables on the same layout. In that case, all controls should designate both their Column and their Table names. However, if you find a situation in which you need to bind controls manually, use BindAllControls to bind them all at once. To bind a single control to a database column, use DBKit.TableConnection's BindEntryControl method. If you name your controls to match the names of the columns, you only need pass the control itself as the first parameter. If the control names do not match the column names, pass the column name as the second parameter: For example: .. code:: xojo Me.BindEntryControl(LastName) or .. code:: xojo Me.BindEntryControl(LastName, "SurName") Loading the selected row into the bound entry controls ****************************************************** When the user clicks on a row in your bound QueryRowsListBox, DBKit.TableConnection will first determine if the row already displayed is in the middle of being edited. If it is, DBKit.TableConnection will display a confirmation dialog box allowing the user to cancel the row change in order to continue editing the current row or lose those changes and switch to the new row. If the user decides to switch to the new row, it will load that row automatically. While this is done automatically, if you need to load the row manually, in the ListBox.SelectionChanged event, call DBKit.TableConnection's LoadSelectedRow method: .. code:: xojo TableConnection1.LoadSelectedRow Intercepting the loading and saving of rows ******************************************* DBKit.TableConnection has a LoadRowing event that is called before the row is loaded into bound entry controls. This is a good place to put code that needs to modify or view the row before it's loaded into the user interface. You can also stop the process of loading the row by returning False from this event. Make sure you return True to allow the row to be loaded. DBKit.TableConnection also has a SavingRow event that is called just before the row is saved to the database. This event gives you an opportunity to change values and to prevent the row from being saved by return False. Loading related rows ******************** If you have the need to show related rows in another table: 1. Add a second ``TableConnection`` object to the layout set its Table property to the table with the related rows. 2. Add a ``QueryRowsListBox`` to the layout, assigning its Table property the same table. 3. In the LoadingRow event of the primary TableConnection instance, you will get the primary key from the row being loaded (which is passed to the event), query for the related rows in the other table and assign the resulting RowSet to the QueryRowsListBox you just added: For example, on a Layout displaying a customer and their invoices, the CustomersConnection.LoadingRow event would look like this: .. code:: xojo 'Load the invoices for this customer Var ID As Integer = row.Column(Me.PrimaryKeyColumn).IntegerValue Var rs As RowSet = Me.Connection.SelectSQL("SELECT * FROM " + InvoicesConnection.Table + " WHERE CustomerID = ?", ID.ToString) Invoices.QueryRows = rs Return True Adding Edit, Undo, Delete and New buttons ***************************************** You may wish to have buttons to allow the user to edit, undo changes, delete the record and/or make new records. You can use a DesktopButton or WebButton. To designate a button as being a Delete, New, Undo or Edit button: 1. Drag a button to the layout. 2. In the Inspector, click on the Pencil icon next to the Super property then select the appropriate button type: .. csv-table:: :header: "Name", "Description" :widths: auto DBKit.DeleteButton, Deletes the selected row. It will ask the user to confirm before deleting. DBKit.EditButton, Makes all DBKit data entry controls on the layout ready for editing by the user. DBKit.NewButton, Resets all DBKit data entry controls on the layout to their default state so they are ready for the user to make a new database entry. DBKit.UndoButton, "Reloads the entire row as it was originally into the data entry controls, undoing all changes the user has made." While DBKit can bind these buttons automatically for you using the steps above, if you need to do it in code, you can do so. In all cases, pass the control as the only parameter: .. code:: xojo 'Indicate the buttons that handle Delete, New, Undo and Edit Me.BindDeleteButton(DeleteButton) Me.BindNewButton(NewButton) Me.BindUndoButton(UndoButton) Me.BindEditButton(EditButton) Creating your own Edit button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you set a button's Super property to the DBKit.EditButton class, the edit button function will be handled automatically. If you need to handle it manually, call the DBKit.TableConnection's EditRow method: .. code:: xojo Try TableConnection1.EditRow Catch error As DatabaseException System.Beep MessageBox("An error occurred while attempting to save the record.") End Try Creating your own Undo button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you set a button's Super property to the DBKit.UndoButton class, the undo button function will be handled automatically. If you need to handle it manually, call the DBKit.TableConnection's UndoRowChanges method: .. code:: xojo Try TableConnection1.UndoRowChanges Catch error As DatabaseException System.Beep MessageBox("An error occurred while attempting undo the changes you made.") End Try Creating your own New button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you set a button's Super property to the DBKit.NewButton class, the new button function will be handled automatically. If you need to handle it manually, call the DBKit.TableConnection's NewRow method: .. code:: xojo Try TableConnection1.NewRow FirstName.SetFocus 'Set focus back to whichever control is first in the tab order Catch error As DatabaseException System.Beep MessageBox("An error occurred while attempting create the new record.") End Try Creating your own Delete button ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you set a button's Super property to the DBKit.DeleteButton class, the delete button function will be handled automatically. If you need to handle it manually, call the DBKit.TableConnection's ConfirmDeleteRow method: .. code:: xojo Try TableConnection1.ConfirmDeleteRow Catch error As DatabaseException System.Beep MessageBox("An error occurred while attempting delete the record.") End Try Adding navigation buttons ************************* You may wish to have buttons to allow the user to move to the first, previous, next and last rows in the RowSet. You can add a single navigation button that handles moving between rows or separate buttons. If you wish to have a single navigation button: 1. Drag a SegmentedButton contorl to the layout. 2. In the Inspector, click on the Pencil icon next to the Super property then select DBKit.NavigationButton. For separate buttons, you can use a DesktopButton or WebButton. To designate a button as being a Delete, New, Undo or Edit button: 1. Drag a button to the layout. 2. In the Inspector, click on the Pencil icon next to the Super property then select the appropriate button type: .. csv-table:: :header: "Name", "Description" :widths: auto DBKit.MoveToFirstRow, Displays the first row in the RowSet. DBKit.MoveToLastRow, Displays the last row in the RowSet. DBKit.MoveToNextRow, Displays the next row in the RowSet. DBKit.MoveToLastRow, Displays the last row in the RowSet. In all cases, if the row has been edited but not saved, clicking one of these buttons will display the save confirmation dialog box. These buttons will all auto-enable and auto-disable appropriately based upon which row is being displayed. For example, if the first row is being displayed the MoveToFirstRow and MoveToPreviousRow buttons will automatically disable. While DBKit can bind these buttons automatically for you using the steps above, if you need to do it in code, you can do so. In all cases, pass the control as the only parameter: .. code:: xojo 'Indicate the buttons that handle First, Last, Next and Previous Me.BindMoveToFirstRow(FirstButton) Me.BindMoveToLastRow(LastButton) Me.BindMoveToNextRow(NextButton) Me.BindMoveToPreviousRow(PreviousButton) Binding in this case will only handle automatically enabling/disabling the controls. Make sure in their Pressed events you are calling the appropriate DBKit.TableConnection method (MoveToNextRow for example) to make the button perform the desired action. Creating your own navigation buttons ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you set a button's Super property to one of the DBKit navigation button classes (MoveToFirstButton, MoveToLastButton, MoveToNextButton, MoveToPreviousButton, or NavigationButton), the button's function will be handled automatically. If you need to handle it manually, call the appropriate DBKit.TableConnection method: For example, to create your own button to move the current row to the next row, put the following code in its Pressed event: .. code:: xojo Try TableConnection1.MoveToNextRow FirstName.SetFocus 'Set focus back to whichever control is first in the tab order Catch error As DatabaseException System.Beep MessageBox("An error occurred while attempting move to the next row.") End Try Using an ImageViewer control for adding/changing images ******************************************************* For desktop projects, the DBKit.ImageViewer will accept JPEG and PNG files dropped on to it. For web projects, use the DBKit.FileUploader control. The example projects demonstrate these. .. _dbkit.connecting_to_multiple_databases: Connecting to multiple database targets simultaneously ------------------------------------------------------ DBKit can be used in situations that require connecting to more than one database at a time. For example, your application might need to connect to multiple database servers. It might need to connect to more than one local SQLite database. Any combination of these where the application needs to be connected to more than one database at a time can be done with DBKit. What you need to do to accomplish this depends on whether you will have single or multiple instances of a layout open at the same time each using a unique database connections. Single instances **************** An example of this would be where one layout displays data from a local SQLite database while another displays data from a MySQL database (or even another SQLite database). If you only ever have a single instance of a particular layout open at any given time: 1. When you make your initial connection to the database, set the DBKit.TableConnection.ConnectionName property to a unique string that identifies the connection. 2. On your layouts, set the TableConnection.ConnectionName property to the same unique string above for those that should use that connection. Multiple instances ****************** An example of this would be where you open multiple instances of the same layout each accessing a different database connection. For example, if you were opening multiple SQLite database files each with an instance of the same layout. When you have multiple instances of a particular layout open at any given time: 1. When you make your initial connection to the database, set the DBKit.TableConnection.ConnectionName property to a unique string that identifies the connection. 2. When you create instances of your layout in code, set the TableConnection.ConnectionName property to the same unique string above for those that should use that connection. 3. When you are ready to connect, call the Connect method of the TableConnection instance on the layout. 4. On these layouts, make sure to turn AutoConnect off so that it doesn't attempt to connect before your code sets the ConnectionName property. Testing Your Project ******************** Testing a database application often involves making changes to the data. To facilitate this, if you are using SQLite, you can add a Copy Files Build Step to your project to put a copy of your database in the debug build when running from the IDE. If you've never done this before, checkout either DBKit Tutorial as they will have to do this as one of the steps. Localizing DBKit **************** Any text displayed by DBKit that the end user is likely to see is localizable. You will find most of the localizable strings in DBKit.TableConnection > Localizable Strings. Others can be found in the controls themselves. .. note:: Keep copies of your localized strings so that you can reinstall them when new versions of DBKit are released. Eventually we will provide an update that has these strings localized for you. Save Changes confirmation dialog box ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The localizable parts of the confirmation dialog that appears when the user attempts to exit a row they have edited before saving changes are: .. csv-table:: :header: "Name", "Description" :widths: auto ConfirmCancel, The Cancel button caption. ConfirmContinue, The Continue button caption. ConfirmExplanation, The explanatory message. ConfirmMessage, The primary message. ConfirmTitle, The dialog title. Delete confirmation dialog box ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The localizable parts of the confirmation dialog that appears when the user attempts to delete the current row are: .. csv-table:: :header: "Name", "Description" :widths: auto DeleteConfirmCancel, The Cancel button caption. DeleteConfirmContinue, The Continue button caption. DeleteConfirmExplanation, The explanatory message. DeleteConfirmMessage, The primary message. DeleteConfirmTitle, The dialog title. Edit button caption ^^^^^^^^^^^^^^^^^^^ The Edit button's Edit and Done captions can be localized: .. csv-table:: :header: "Name", "Description" :widths: auto EditButtonDoneCaption, The caption that appears when the user clicks the Edit button. The default is *Done*. EditButtonEditCaption, The initial caption that appears when the layout is not in editing mode. The default is *Edit*. Mandatory field required message ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To localize the message displayed when the user attempts to save the row but has left a mandatory control without a value, edit the TableConnection.ErrorMandatoryValueMissing constant. NumberField out of range message ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To localize the message displayed when the user attempts to leave a NumberField with a value that doesn't meet the minimum or maximum values, edit the NumberField.ErrorMinValue and ErrorMaxValue constants. NumberField no decimals allowed message ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Because the TextField control in a web app does not have a KeyDown event, numeric entry can't be checked as the user types. It is therefore checked by DBKit when the user attempts to exit the field. If they have entered a decimal value when the DecimalPlaces property is 0, a message will be displayed. To localize this message, update the NumberField/ErrorDecimalsNotAllowed constant. NumberField message dialog box (web) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To localize the OK button that appears in the dialog box when any of the above NumberField messages are displayed, edit the NumberField.MessageDialogButtonCaption constant. Bug reports and feature requests ******************************** If you find a bug in DBKit or have a feature request, use our `Issues `_ reporting system to give us the details. Make sure you prefix the title of your `issue` with ``DBKit``. Supporting more controls and platforms ************************************** To support controls that are not currently supported, the following DBKit.TableConnection methods must be updated: * CurrentRowChanged * EditRow * SaveRow * SetControlValue Control subclasses must implement the DBKit.Control class interface and include TableName As String, ColumnName As String and Connector as DBKit.TableConnection properties. Examine the existing control subclasses in DBKit for details. To support entirely new platforms, the following DBKit.TableConnection methods must be updated: * Constructor * DesktopControlForColumn/WebControlForColumn * EditRow * LoadSelectedRow * NewRow * NoRowSelected * SaveRow * SetButtonEnabledState .. _dbkit.release_notes: .. rst-class:: nosearch DBKit `Release notes` --------------------- Known `Issues` ************** * A regular ListBox control separate from the QueryRowsListBox is needed. * DBKit is not yet aware of _rowid in MySQL tables. * Some DBKit properties available in the Inspector do not appear in the DBKit section of the Inspector. * In web projects, if a QueryRowsListBox has bound controls on the same webpage and those controls are populated when the QueryRowsListBox has no selection (such as after doing a search that results in now rows), the controls continue to display the data from the previously selected row. This is due to a bug in the Web framework (#78279). Beta 18 (May 27th, 2025) ************************* * Added TableConnection.CurrentRow which will return a RowSet containing the currentRow. * Fixed a bug that caused a NilObjectException in the Desktop version when deleting a row when no QueryRowsListBox was present. * Fixed a bug that would cause the New button in a DBKit.Toolbar to be initially disabled. * Fixed a bug that would cause the Undo button to continue to be enabled after the user edits a row and then uses a navigation button to move to another row, losing any changes they had made. * When adding a new row when a QueryRowsListBox is present, the new row appears in the list and becomes the selected row. Beta 17 (May 14th, 2025) ************************* * Fixed a bug that caused the New button (when in a toolbar) to not enable/disable properly. (79120) * Fixed a bug that caused a DatabaseException should the user click the Undo button while creating a new record. (79120) * Added a New toolbar button to the Row Only example in the DBKit example project so adding rows from a toolbar can be tested. Beta 16 (April 7th, 2025) ************************* Connecting to multiple simultaneous databases is now supported. To do so, when you create your connection, set the DBKit.TableConnection.ConnectionName property to a unique value that represents that database connection. On layouts that will connect to that connection, set the TableConnection's ConnectionName property to match one you set when connecting. If you are not making multiple simultaneous database connections, there are no changes you will need to make. The new TableConnection.AutoConnect property mentioned below will be on by default. Note to those using DBKit with WebProjects: after you replace DBKit in your project, save it then reopen it so that the new AutoConnect property will appear in the Inspector. * New TableConnection.Constructor(connName as String = "") this new method is used to set the (new) ConnectionName property when you are making multiple simultaneous database connections. * New TableConnection.Connection Name As String property: used to allow connections to multiple simultaneous databases. * New TableConnection.AutoConnect As Boolean: this tells the TableConnection on the layout to automatically connect. This is true by default. * New TableConnection.Connect method: used to connect to the database when you have AutoConnect turned off. * A :ref:`New section` of the documentation on how to set up multiple, simultaneous database connections has been added. Beta 15 (February 4th, 2025) **************************** * Fixed a bug that caused that the transaction not to complete if the user closed the window/webpage while editing a row. Beta 14 (January 21th, 2025) **************************** * Fixed another bug involving nil encodings when rows are being loaded into the QueryRowsListBox. Beta 13 (January 20th, 2025) **************************** * Fixed a bug that caused an IllegalCastException when a container was on a window with a DBKit.TableConnection. * Fixed a bug that caused some characters not to appear correctly in DBKit.TextField and DBKit.TextArea when the encoding of the characters coming from the database was nil. * Fixed a bug that caused an `issue` when using DBKit with PostgreSQL where the primary key of a table was a TEXT type. * The EditingButton is now available for the web version of DBKit. * Icons that were missing in the web version are now again included. Beta 12 (October 4th, 2024) *************************** * Fixed a bug in the desktop version of DBKit with DBKit.SearchField that only manifested itself on Windows. It's the result of a different in event order that caused the search to occur when the window opens but the SearchField's Connector was not yet initialized. Beta 11 (October 3rd, 2024) *************************** * Removing the Version attribute apparently was not enough for web projects. This version completely resolves the `issue` the IDE has with copying a class with an attribute. Beta 10 (October 2nd, 2024) *************************** * Removed the Version attribute on the TableConnection class as this could cause a crash when copying the class between projects. This bug has been resolved for 2024r4 but the attribute is unnecessary anyway since DBKit has a Version constant that is used to indicate the version number. Beta 9 (May 7th, 2024) ********************** Changes ------- * DBKit now supports using any column type for the primary key column (not just numbers as had been the case). Resolved `Issues` ----------------- * Fixed a bug when using DBKit with PostgreSQL that caused the PrimaryKeyColumn function to fail if the table name included the name of the schema. * DBKit.DatePicker (web) now stores null in the database column when there is no date selected. * WebDialogs and WebContainers are now supported in addition to WebPages for web projects. Note that any setup you are going to do for a TableConnection on a WebDialog must be done in the Shown event rather than the Opening event. * Fixed a bug that caused a NOE when uploading a picture to a DBKit.ImageViewer. New Features ------------ * DBKit.SearchField - Provides a Search UI that automatically works with your QueryRowsListBox without any additional code. If you leave the Table property blank, it will find the first TableConnection then bind with its QueryRowsListBox. Otherwise, it will look for a TableConnection on the layout whose Table property matches its own. If you provide columns for it to search it will use them. If you don't, it will use the columns of the QueryRowsListBox to which it is bound. String and Number type columns are supported. For String values, a begins with search is performed. For numeric values, the search is for an exact match. You can use >, < or ... for a range. For example, >10 finds rows use searched numeric column has a value greater than 10. Entering 10...20 will find rows whose searched numeric column has a value between 10 and 20. The Show All Rows By Default property if true will display all rows when the SearchField is blank. * DBKit.Toolbar - New control that provides a toolbar with options for New, Delete, Edit and Undo buttons. No code needed. The buttons you wish displayed can be indicated via properties in the Inspector. (For web projects, you'll will need to add the new backicon.png file to your icons folder) * DBKit controls now have a Mandatory property that can be set via code or in the Inspector. Mandatory controls must have a value for the record to be able to be saved. For Slider, the value must be greater than the minimum value. For CheckBox, the checkbox must be checked. * DBKit.NumberField - Handles numeric entry. Choose the type (Currency, Number, Percentage and Custom) and it will handle the formatting and entry masking for you. It includes properties to indicate if you want a thousands separator and the number of decimals you wish to show. It's also locale-aware. Choose Custom if you plan to assign a format to the Format property. It also has MinimumValue and MaximumValue properties. It will only allow entry of values between these two numbers provided that they are not the same number. In other words, if MinimumValue and MaximumValue are both 0 then no range checking will be done. * All controls that display database data now have a DisplayName property. If this property has a value, it will be used in any messages (except those displayed as a result of database errors) to the user such as when they have not filled in a mandatory field. If it does not have a value, the name of the control will be used. Beta 8 (April 9th, 2024) ************************ Changes ------- * DBKit.Connector has been renamed TableConnection (to be consistent with SerialConnection, URLConnection). * DBKit.SearchResultsListBox no longer has an AddRowsFromRowSet method. Use the new QueryRows As RowSet property instead. This is for consistency with the new DBKit.TableConnection.QueryRows property. * DBKit.SearchResultsListBox has been renamed QueryRowsListBox for consistency with other parts of the API. You may need to make a few name changes in your code to accommodate this. * DBKit.TableConnection.PrimaryKeyColumn now caches the column name rather than querying the database every time. Resolved `Issues` ----------------- * DeleteButton, EditButton, NewButton and UndoButton all now fire their Pressed events when pressed. (74844) * Fixed a bug that caused the controls to not be bound to the correct table when the user had entered a schema name as part of the table name in the Table property of a DBKit control. * Fixed a bug that caused rows to not save properly when using DBKit with PostgreSQL. New Features ------------ * New TableConnection.QueryRows As RowSet property that can be used to create layouts that show the current row of a RowSet without a QueryRowsListBox as part of the user interface. This property is write-only. Once you assign your RowSet to it, do NOT change your RowSet in any way as the TableConnection is using that RowSet to display rows in your user interface. * New TableConnection.MoveToFirstRow, MoveToNextRow, MoveToPreviousRow, MoveToLastRow methods that can be used to move the current row of the QueryRows property for use when you're displaying rows directly from the TableConnection rather than via a QueryRowsListBox. * New TableConnection.HasBoundListBox function that will tell you if the TableConnection has a QueryRowsListBox bound to it or not. * New MoveToFirstRowButton, MoveToLastRowButton, MoveToPreviousRowButton, MoveToNextRowButton which can be dragged to the layout to automatically move the current row. These buttons enable and disable automatically. * New NavigationButton As DesktopSegmentedButton/WebSegmentedButton. This provides a single control that can include navigation buttons (First, Previous, Next, Last). * QueryRowsListBox now has a Table As String property. The purpose of this is to allow for multiple QueryRowsListBoxes to exist on the same layout each driven by its own TableConnection instance. In a case like this, the Table property is mandatory as it will bind to the TableConnection whose table matches its Table. This allows for displaying rows from related tables. See the EntryWindow/EntryPage layouts for an example of using two TableConnections (one to show the customer, the other two show the customer's invoices). The invoices are loaded in the CustomersConnection.LoadingRow event. Beta 7 (February 27th, 2024) **************************** * Calling BindControls in a DBKit.Connector Opening event is no longer necessary. If you used previous betas, you'll want to update your Connector instance opening events to remove the now unnecessary call to BindControls. * Fixed the bug where a bound WebFileUploader button was always enabled even when a row was not being edited. * When using DBKit with PostgreSQL, tables in schemas other than public are now supported. To designate the schema, prefix the table name assigned to the DBKit.Connector.Table property with the schema name. For example, if the schema name is "utilities" and the table name is "resources", then the Table property of the instance of the DBKit.Connector control on the layout should be assigned "utilities.resources". * The DBKit.DeleteButton controls are now disabled when a new row is being added by clicking a DBKit.NewButton. (`75002 `_) Beta 6 (August 24th, 2023) ************************** * **IMPORTANT**: It turns out there are too many `issues` with trying to have a single DBKit module that includes all the DBKit controls for all project types. As a result, the version of DBKit in the Desktop example is only for desktop projects. The version in the web example is only for web projects. The core code in DBKit itself is still the same in both projects. This also means that you will have to reset the super property of any DBKit controls on your layouts. This is a one-time only change. * **IMPORTANT**: DBKitConnector is now Public (instead of Global) and is now simply Connector. This means that when you replace a previous version of DBKit with this one, you will need to reset the Super of any DBKitConnector class instances to DBKit.Connector. This is a one time-only change. * **IMPORTANT**: DBKit control subclasses are no longer in their own project type-specific modules (Desktop or Web). As a result, they are no longer namespaced as Desktop or Web. Thus you will need to reset the super on any DBKit controls you're using on your layouts. This is a one time-only change. * Versions for the desktop and web are now separate because the Xojo IDE isn't really designed to have classes from different platforms in the same project file. Make sure to copy from the appropriate example project. * Fixed a few controls whose DBKit properties didn't appear correctly in the Inspector. * Edit/Done button now enables when making a new row for a table with no rows. * If you attempt to use a DBKit.Connector class without first assigning a table name to the Table property, an exception is now raised. Beta 5 (August 15th, 2023) ************************** * When editing a row or adding a new row, the focus is now automatically set to the first TextField or TextArea in the tab order. * Fixed a bug that caused the Edit button to be disabled after pressing the Undo button. * DBKitSearchResultsListBox columns can now be bound via the Columns property in the DBKit section of the Inspector instead of having to do it in code with the BindDatabaseColumns method though that too still works. * It is no longer necessary to set up the connection for a DBKitConnector on a layout if one has already been established and stored in an App class (desktop) or the Session (web) classes. DBKitConnector looks for the connection in those objects and will use it if it finds it. You can still create the connection in code if needed. Beta 4 (July 20th, 2023) ************************ * TableConnector has been renamed DBKitConnector. * All new control subclasses that nearly eliminate all the code needed to set up database controls. It's highly recommended that you go through the tutorial again to get a good idea of how different this version is. The tutorial now only takes about 20 minutes compared to 30 with the previous beta as there's a LOT less code to enter. * DBComboBox, DBPopupMenu and DBRadioGroup now all have an AutoPopulate As Boolean property that appears in the DBKit section of the Inspector. Setting this to true will auto populate the control with the unique values from the column. If you prefer the values to come from another column or another table and column, provide those in the AutoPopulateTable and AutoPopulateColumn properties. * BindListBoxColumns now allows you to bind two columns (example: FirstName and LastName) to the same single column of a listbox. To do so, pass two columns names separated by a comma "firstname,lastname" as a column parameter. Beta 3 (June 26th, 2023) ************************ * Removed the transaction used while testing in the IDE since, at least for SQLite, we are copying the database each time your run from the IDE. Beta 2 (June 26th, 2023) ************************ * Fixed a bug with the locking of some of the TextFields in the example projects. * Fixed a bug that resulted in an exception when a row had been modified and saved more than once. * Replaced icons in example projects with nicer ones from Jérémie Leroy (thanks Jérémie!). * Added TableConnector.ControlsStateChanged event that fires when the entry controls are enabled or disabled by TableController allowing the user to enable/disable other controls that are not bound to the database table. * Fixed a bug that caused TableConnector running in a web project to think stored images had changed when they had not changed. * Changed the behavior of returning False from the SavingRow event. It no longer reloads the original row. * Fixed a bug that prevented the saved record from being unlocked. * Changed the behavior of editing rows. Entry controls are now disabled or read-only (in the case of TextFields and TextAreas) by default. The user clicks the Edit button to begin editing. The Edit button's caption then changes to Done. This enables the controls or in the case of TextFields and TextAreas makes them read-write. When the user is done editing, they click the Done button. This avoids accidental edits and is in preparation for a future version of DBKit that will provide the option to allow editing of any specific record by one user at a time. In the example projects, the button is now named EditButton and has Edit as its default caption. The two possible captions (Edit and Done) are localizable strings. * Because of the change to how rows are edited, use EditRow now instead of SaveRow (which is now private). * The BindSaveButton method is now BindEditButton. When you update your projects to use DBKit 1b2, you'll get some compiler errors as a result in your TableConnector opening events. Changing BindSaveButton to BindEditButton will fix that. * To improve clarity, the TableConnector.BindListControl method has been renamed BindListBoxControl. When you update to this version, you'll need to update any places where you call BindListControl. Thankfully, the compiler will point these out to you when you run your project. Beta 1 (June 21st, 2023) ************************ * Initial pre-release .. seealso:: :doc:`DBKit Desktop Tutorial`, :doc:`DBKit Web Tutorial` Debugging ========= .. toctree:: :maxdepth: 1 :name: sec-debugging Android iOS Remote Debugging How Xojo manages memory What to do when your app is consistently crashing Android Debugging ================= .. toctree:: :maxdepth: 1 :name: sec-android_debugging Debugging via the Android emulator Debugging on-device ================================== Debugging via an Android emulator ================================== Android Studio creates includes an emulator by default. However, you can create emulators for other devices. To create an Android emulator: 1. Launch Android Studio. If you haven't installed it, :doc:`do that first`. 2. Click the New Project button in the Welcome window. 3. In the New Project window, click **Next** then click **Finish**. 4. Android Studio will prepare the new new project. When it's done, click **Finish**. 5. From the menubar, choose Tools > Device Manager. 6. Click **Create device**. The Virtual Device Configuration window appears. 7. For the best debugging experience, in the device listbox, choose one of the devices that has an icon in the Play Store column then click **Next**. 8. On the System Image page, the appropriate Android version should already be selected. If the **Next** button is not enabled, click the **Download** button to download the system image. 9. Click the **Next** button. 10. On the Verify Configuration page, click **Finish**. 11. The emulator you created appears in the Device Manager pane in Android Studio. Click the Play button to start your new emulator for the first time so that it can be ready to go when you use it in Xojo. 12. You may now quit Android Studio and use the emulator in Xojo. The first time you launch a Xojo app in an Android emulator you just created, it will take a minute or so to configure itself. After that, it launches and runs quite quickly. .. note:: On macOS, the first time you run an Android emulator, macOS will display a dialog asking for permission to access system events. This happens because Xojo uses AppleScript behind the scenes to launch Android emulators. .. tip:: If you're going to be building several different apps for Android, the default amount of storage the emulator has might not be enough. When creating your emulator, on the Verify Configuration page, click **Show Advanced Settings** then scroll the configuration list until you see the *Internal Storage* field. It defaults to 2048 MB. Increasing this will make the emulator run faster. .. note:: As Google changes Android Studio, these steps may be not be 100% correct. .. seealso:: :doc:`Installing Android Studio`, :doc:`Running/Debugging your Android app on a device`, :doc:`Android QuickStart`, :doc:`Android Tutorial` ============================================== Running/debugging your Android app on a device ============================================== You can run, test and debug your Android projects on an actual device either via USB or Wi-Fi. Enabling developer options on your Android device ------------------------------------------------- Before you can debug on your device, you need to enabled the developer options on your device: 1. Plug your **Android device** into your computer with a USB cable. 2. If your computer asks for permission to access the device, grant it. 3. On your Android device, go to the **Settings app**. 4. Find the **About screen**. It's often found by tapping on System and then About (or About Phone). 5. On the About screen, look for the **Build number**. 6. Tap the **Build number** 7 times. You may see a message as you tap indicating that more taps will enable developer mode. 7. A confirmation dialog may appear asking if you want to allow developer settings. Tap **OK**. 8. You may need to now authenticate with whatever method you use (PIN, password, etc.) USB debugging ------------- To debug via USB: 1. Make sure you have enabled **Developer options** on your Android device. 2. On your Android device, go to **Settings > Developer options**. 3. Scroll down to the **USB debugging** option and turn it on. 4. In Xojo, go to the Navigator, click on **Build Settings** then click on **Android**. 5. The device should now appear in the **Debug Device menu**. Select it and run your project on your device. .. tip:: If you already had the Android Build Settings displayed in the Inspector during this process, the Debug Device menu may not refresh to show your device. Should this happen, click on another item in your project and then back to Android under Build Settings to refresh the Debug Device menu. Wi-Fi debugging --------------- To debug via Wi-Fi: 1. Make sure your computer and your Android device are **both on the same Wi-Fi network**. 2. Make sure your Android device is running **Android 11 or greater**. 3. If you don't have Android Studio installed, do so now. 4. Launch **Android Studio**. 5. On the upper-right side of the Android Studio toolbar, you will see a popup menu. If you have not yet created any Android emulators, it will display "No devices". If you have, then the name of your first emulator will appear there. .. image:: https://documentation.xojo.com/topics/debugging/android/images/run_configuration_menu.png :scale: 50% :align: center 6. Click on this menu and choose **Pair Devices Using Wi-Fi**. The Pair Devices over Wi-Fi dialog box appears. 7. If you haven't enabled **Developer options** on your Android device, do that now. 8. On our Android device, go to **Settings > System > Developer options**. 9. Scroll down until you find the **Wireless Debugging** option and turn it on. 10. Tap on the **Wireless Debugging** label. 11. Tap **Pair device with QR code**. 12. Scan the QR code with your Android device. The dialog on Android Studio confirms that pairing is complete. .. image:: https://documentation.xojo.com/topics/debugging/android/images/pairing_complete.png :scale: 30% :align: center 13. In Xojo, go to the Navigator, click on **Build Settings** then click on **Android**. 14. The device should now appear in the **Debug Device menu**. Select it and run your project on your device. .. tip:: If you already had the Android Build Settings displayed in the Inspector during this process, the Debug Device menu may not refresh to show your device. Should this happen, click on another item in your project and then back to Android under Build Settings to refresh the Debug Device menu. Unpairing your device ********************* To unpair your device from your computer: 1. On your device go to **Settings > System > Developer options**. 2. Scroll down to **Wireless Debugging** and tap on it (on the item itself, not the switch). 3. Tap on your **computer** in the **Paired Devices** section. 4. Tap **Forget**. 5. Launch **Android Studio** and open a project or create a new one. 6. If the Device Manager pane is not displayed, choose **Tools > Device Manager**. 7. In that pane, click on **Physical**. 8. Click on the **Trash can icon** next to your device name to remove it. A confirmation dialog appears. 9. Click the **Remove button** to remove the device and completely unpair your device from your computer. 10. You may now quit Android Studio. Troubleshooting --------------- If on-device debugging has stopped working, there are three possibilities: * The device has gone to sleep. It must be awake from the time you press the Run button to the time the app appears on your device. If it's gone to sleep, Xojo will tell you that the device isn't responding. Wake up your device and try again. * The device isn't responding to the connection from Xojo. Should this happen, try toggling the USB debugging or wireless debugging option in the Developer Options on your device off and back on. If that does not work, you will need to turn off Developer Options by going to your device, tapping on System > Developer Options and then toggling Use developer options off. Once you do that, you'll need to restart the process of setting up on-device debugging. Once done, you should be able to connect from Xojo to your device again. You may need to relaunch Xojo after going through the process. * You are trying to debug a project whose application identifier is the same as one that is already installed on your device. Delete the app on your device and then try debugging again. .. seealso:: :doc:`Installing Android Studio`, :doc:`Running/Debugging via the Android emulator`, :doc:`Android QuickStart`, :doc:`Android Tutorial` iOS Debugging ============= .. toctree:: :maxdepth: 1 :name: sec-ios_debugging Debugging your iOS apps =================================== Testing and debugging your iOS apps =================================== You can test/debug your iOS apps in the iOS Simulator or directly on the the device itself. The iOS Simulator is part of the Xcode download, so you'll need to make sure it is installed before trying to run your Xojo iOS projects. Refer to the :doc:`Installing Xcode and Apple Certificates` topic for information on how to install Xcode. See the :ref:`system requirements for the current release` information on the supported version of the iOS Simulator. .. _/topics/debugging/testing_and_debugging_your_ios_apps/running_your_app_in_the_ios_simulator: Running your app in the iOS Simulator ------------------------------------- To run your iOS app in the iOS Simulator, click the Run button on the Xojo toolbar. Your project is compiled and the app starts the iOS Simulator. .. image:: https://documentation.xojo.com/topics/debugging/ios/images/ios_simulator_eddies_electronics_running_in_ios_simulator.png :scale: 50 % .. tip:: By default, Xojo runs your app on the smallest simulator screen available. It's best to test your app on a small screen to make sure your user interface design works well on small screens. When you run the app in the Simulator, you can use the Xojo debugger where you can set breakpoints to stop at the line of code, and then step through the code and look at variable values. Running in the Simulator is fast and convenient, but you should also always test your apps on actual devices as the Simulator does not work exactly like a device. Some differences when running in the Simulator include: * Apps built for the Simulator use the 32-bit x86 compiler, not the 64-bit ARM compiler. This can result in different app behavior. * Not all device features are available in the Simulator. * Keyboard may work differently in the Simulator. You may have noticed that the iOS entry in Build Settings has a build step called "Sign". This build step is used to code-sign your iOS app when you build it for deployment. You cannot change its properties and you should not remove it. .. _/topics/debugging/testing_and_debugging_your_ios_apps/running_your_app_on-device: Running your app on-device -------------------------- To test on-device, first go to Settings > Personal Hotspot on your device and turn that on. Next, you will need to complete four steps in Apple's Developer Portal: 1. Create a developer certificate, download it then double-click it to install it. 2. Create an app identifier that matches the one in your project. 3. Register the device you intend to use for on-device debugging. 4. Create a Development Profile and add your device or devices to it. Download the profile and double-click it to install it. Next you need to set the Team and Build For properties in your project: 1. **Team** should be set to the Team to whom the app belongs. 2. **Build For** must be set to Development. .. warning:: The first time you run an app on-device (or after you delete the app from your device should you do so), you'll get a warning about the app wanting to connect to the WIFI network. This is **required** for debugging. At the moment, this causes the first run to fail. Press Run again and the app will run to test on the device. Once you've set up all of that, in Xojo choose Project > Run On then choose a device from the menu. If you have a device plugged into your Mac, you can run directly on it. .. image:: https://documentation.xojo.com/topics/debugging/ios/images/run_on_menu.png :scale: 50 % .. _/topics/debugging/testing_and_debugging_your_ios_apps/tips: .. note:: When testing on-device, make sure not to let your device go to sleep or you'll have to reconnect it. Tips ---- When you use Copy File Steps to copy files to your iOS app on the Simulator, the files are copied to special locations on the Mac itself. These files are not removed between runs of your app. For complete information about the iOS Simulator, refer to the official Apple iOS Simulator User Guide. The tips below are from the section `Interacting with the iOS Simulator `_: .. csv-table:: :header: "Gesture", "Description" :widths: auto "Two Finger Drag","1. Place the pointer where you want the two-finger drag to occur. 1. Hold down the Option key. 2. Move the circles that represent finger touches to the start position. 3. Move the center of the pinch target by holding down the Shift key, moving the circles to the desired center position, and releasing the Shift key. 4. Hold down the Shift key and the mouse button, move the circles in the direction you want to drag, and release both the Shift key and the mouse button." "Pinch","1. Place the pointer where you want the pinch to occur. 1. Hold down the Option key. 2. Move the circles that represent finger touches to the start position. 3. Move the center of the pinch target by holding down the Shift key, moving the circles to the desired center position, and releasing the Shift key. 4. Hold down the mouse button, move the circles in and out to the end position, and release the Option key." "Rotate","1. Place the pointer where you want the rotation to occur. 1. Hold down the Option key. 2. Move the circles that represent finger touches to the start position. 3. Move the center of the pinch target by holding down the Shift key, moving the circles to the desired center position, and releasing the Shift key. 4. Hold down the mouse button, rotate the circles to the end position, and release the Option key." .. _/topics/debugging/testing_and_debugging_your_ios_apps/removing_unused_devices_from_the_ios_simulator: Removing unused devices from the iOS Simulator ---------------------------------------------- You may find that the list of iOS devices and iOS versions for the iOS Simulator contains many more information than you need. For example you may not really need to have iPhone 5, 5s and SE listed in the iOS Simulator. To remove older devices that you no longer need from the iOS Simulator, you 'll need to go to Xcode and open the "Devices and Simulators" window (in the Window menu). Select the "Simulators" tab to see all the iOS Simulator devices that are installed. If you have multiple iOS versions installed then you'll see devices listed more than once since each device is listed for each version of iOS. For example you may see iPhone 8 listed for both iOS 11.0 and iOS 10.3.1. To remove a device, such as iPhone 7 running iOS 10.3.1, select it from the list, right-click on it and from the menu select "Delete". Choose "Delete" again from the "Are you sure?" prompt. The device will no longer appear in this list and the next time you open Xojo the device will no longer appear in the iOS Build Settings Simulator Device pop-up menu. This Terminal command can also removed all unavailable devices that are left over from older versions of Xcode: .. code:: xojo xcrun simctl delete unavailable .. _/topics/debugging/testing_and_debugging_your_ios_apps/troubleshooting: Troubleshooting --------------- * If your app launches in the Simulator but has a blank screen, be sure to verify that all your image paths are correct. * If your app is immediately crashing on launch, add the :ref:`UnhandledException` event and put code there (or a :doc:`Break` command) to see if that provides additional information. .. _/topics/debugging/testing_and_debugging_your_ios_apps/ios_system_logs: iOS System logs *************** If you app crashes while running in the iOS Simulator, you can view the system logs for the crash by selecing Debug > Open System Log from the menu. You can also open the Console app to look at the All Messages view, which will also have additional information, including a link to the crash log itself. If you find a crash, please create a :doc:`bug report` and attach the crash log. .. _/topics/debugging/testing_and_debugging_your_ios_apps/see_also: .. seealso:: :doc:`Installing Xcode and Apple Certificates` topic ======================= How Xojo manages memory ======================= With object-oriented programming, each new object you create takes up space in memory. Xojo uses a technique called **Automatic Reference Counting** (ARC) to manage the memory used by objects. ARC is faster and more efficient than other memory management techniques such as garbage collection. Here is how it works: When you create a new instance of a class (an object), an internal reference counter for the class is increased. When the class goes out of scope, the reference is decreased. When the reference reaches 0, the class instance is immediately removed from memory. This means that instances of classes are removed from memory automatically when they are no longer used. Suppose you create a class based on a ListBox. You then create an instance of that class in a window. When the window is opened, the instance of the class is created in memory automatically. When the window is closed, the instance of the class is automatically removed from memory. If you store the reference to a class in a local variable, when the method or event handler is finished executing, the instance of the class is removed from memory. If you store a reference to an instance of a class in a property, the instance will be removed from memory when the object owning the property is removed from memory. ARC really is a great way to handle object memory management and is also used by languages such a Swift and Objective-C. With ARC you generally don't have to worry about memory management except for the special case of circular references. In those cases, you'll need to manually release an object (by setting it to Nil) so that its reference count decreases to allow it to eventually reach 0, thus allowing it to get removed from memory. Otherwise, your app will create an object that is never released from memory, causing what is called a **memory leak** in your app. This special case does not occur often, but when it does you can make use of :doc:`Weak References` to help mitigate it. .. _/topics/debugging/how_xojo_manages_memory/more_about_memory_leaks: More about memory leaks ----------------------- Memory leaks occur when objects are created but never destroyed. As more and more objects are created and not destroyed, the amount of memory used increases. Eventually, the app will crash because the computer runs out of available memory. In a desktop app this may not be a big deal because desktop and laptop computers typically have lots of memory and the user will eventually quit the app, clearing its memory. Mobile apps also should pay attention to memory leaks because mobiles devices do not typically have as much available memory as desktop computers and can run out of memory sooner. Memory leaks are more serious in web apps because the web app may be running for days, months or even longer. If your web app has :ref:`WebApplication.AutoQuit` set to :doc:`True`, once the number of users (sessions) accessing the app gets to zero, the app will quit and this will release any memory that app is using. However, your app may never reach the point where there are no users so you need to be careful about not leaking memory. And standalone apps generally do not quit. The first thing to do is to look for circular references in your code. That is, **object a** has a reference to **object b** and **object b** has a reference to **object a**. When it comes time to call destructors to release memory, neither one can be called because their reference counts cannot reach 0. Refer to :doc:`Web App Optimization` to learn more about how to effectively manage memory in your web apps. ================================================= What to do when your app is consistently crashing ================================================= .. _/topics/debugging/what_to_do_when_your_app_is_consistently_crashing/windows: Windows ------- If you experience application crashes you may be asked by support to create a crash dump file. Crash dumps are created automatically by Windows if the following registry key is present: HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps In order to help support troubleshoot your problem please do the following: 1. Create the registry key LocalDumps if it is not present already. Recent versions of Xojo automatically add this key for you. #. Reproduce the problem (i.e. make the application crash). #. Locate the crash dump file in %LOCALAPPDATA%\\CrashDumps. Note that if the crashing application runs under the System account, that resolves to C:\\Windows\\System32\\config\\systemprofile\\AppData\\Local\\CrashDumps. #. Attach the crash dump file to a :doc:`bug report`. This works on all versions of Windows starting with Vista and Server 2008. The `DebugView `_ utility might also be helpful to capture and view app crash information. .. _/topics/debugging/what_to_do_when_your_app_is_consistently_crashing/windows/see_also: See also ******** * `Helge-Klein `_, `Microsoft Dev Center `_ * :ref:`Creating an OS Sample File` .. _/topics/debugging/what_to_do_when_your_app_is_consistently_crashing/linux: Linux ----- On Linux, whenever an app crashes (most commonly by a segmentation fault, i.e. when accessing a bad pointer/memory), it has the option of generating a core dump. In most cases though, this core dump generation is disabled by default. A core dump is a file which contains the program's running state at the point of the crash. It also contains a snapshot of all the virtual memory areas accessed by your app, so there can be confidential information stored in a core dump -- be careful who you give it to. To enable core dumps on Linux varies by distro as is often the case. These steps assume a Linux distro that has a running Terminal using bash (for example, Ubuntu distros running Gnome desktop manager). To start, you need to tell the system how big of a core dump to allow. To do this, type the following line in your Terminal: .. code:: plain ulimit -c unlimited By default, this value is 0, which is why the core dump is never generated. Executing the above line in a bash Terminal will allow the core dump to be generated for that Terminal session. The “unlimited” parameter tells the system not to restrict the size of the core dump file. Now assume you have an app that does crash, run that application from this Terminal session (type it's name at the prompt) and wait for it to crash. After the apps crashes, you should see a “core” file generated in the current directory (however, depending on your configuration this may be located elsewhere). Attach the core dump file to a :doc:`bug report`. .. _/topics/debugging/what_to_do_when_your_app_is_consistently_crashing/linux/see_also: .. seealso:: `How to Take a Core Dump `_ blog post Declares ======== .. toctree:: :maxdepth: 1 :name: sec-declares Calling native Android APIs Calling native iOS APIs Calling native Linux APIs Calling native macOS APIs Calling native Windows APIs Examples for iOS Examples for macOS Examples for Windows =========================== Calling native Android APIs =========================== Calling into the Android OS to utilize an API can be done in one of two ways. If you simply need to call an API that does not directly involve any parts of the Xojo framework, you can make a direct API call. If instead your API call will be interacting with a Xojo framework object, then it's best to create the :doc:`declare` as part of an extension method for the Xojo framework class with which you intend to use it. Direct API call --------------- Calling an Android OS API directly via a :doc:`declare` is no different than it would be for other operating systems. For example, if to get the version number of Android upon which the app is running, the Android Developer documentation for the `Build object `_ indicates that it is located in the ``android.os`` library. The ``Release`` property holds the user-visible string and is accessed via the Alias section. Because ``Release`` is a string, the return value must be defined in the :doc:`declare` as a :doc:`CString`. .. code:: xojo Declare Function AndroidVersion Lib "android.os.Build.VERSION" Alias "RELEASE" As CString An example project that uses this :doc:`declare` is available at Platform > Android > Declare > Declare project. Using Xojo framework classes ---------------------------- For Android, Xojo supports a special new :doc:`declare` format that allows making calls directly against certain objects and controls. This is useful when creating a :doc:`Declare` to change the property of a control. As an example, let's set the background color of a :doc:`MobileButton`. The typical way to use this new type of Object :doc:`Declare` is by adding an :doc:`Extends` method to a Xojo Module in your project. To extend the :doc:`MobileButton` class with the ability to change its color, add a method called ``SetBackgroundColor`` with the following parameters: ``Extends ctrl As MobileButton, c As Color`` to a Xojo Module in your project. In the `Android developer documentation `_ you will see that a Button is considered a View and thus has a `setBackgroundColor `_ method. An Alias is not needed in this case since the extension method is what will be called in Xojo code. What you will do instead is make the Declare like this: .. code:: xojo Declare Sub setBackgroundColor Lib "Object:ctrl:MobileButton" (myColor As Integer) Breaking this down, it is calling the setBackgroundColor function. The Lib denotes this new type of Object :doc:`declare`: an Object, separated by a colon, the Xojo name of the object (in this case, the ``ctrl`` parameter from the extension method signature), followed by another colon, followed by the parameter's Xojo framework class name. The next and final line of the extension method will call the declared function: .. code:: xojo setBackgroundColor(c.ToInteger) You can see this :doc:`declare` and its helper method in action from the Examples section of Xojo in the Platforms > Android > Declare > ButtonBackgroundColor project. Declare integer types --------------------- The size-specific Integer types are now precise when used with Declares. The mappings are as follows: .. csv-table:: :header: "Xojo Type", "Kotlin Type" :widths: auto "Int8, Byte", "Byte" "UInt8", "UByte" "Int16", "Short" "UInt16", "UShort" "Int32", "Int" "UInt32", "UInt" "Integer, Int64", "Long" "UInteger, UInt64", "ULong" When mapping to Android API functions or Library functions, be sure to use the correct Xojo type that matches what is expected on Android. By far you will use Int32 and Integer. .. note:: If you previously created Declares, you will likely need to change usage of Integer to Int32 as the mapping for that have changed compared to prior versions. Nullable types -------------- Xojo object variables (also properties, parameters and return values) can be :doc:`Nil`. With Kotlin, the default is that these things cannot be null (the equivalent of :doc:`Nil`). To ensure that you do not get compile errors with your parameters, declare them as nullable in Kotlin by appending the ``?`` to the end of the type name. Application context ------------------- Many Android APIs require the application context. This is not available in a Library so it has to be supplied by the app itself. There is a property for this: .. code:: Xojo MobileApplication.AndroidContextHandle As Ptr You can pass this value to a function on the Library that then either saves it or uses it directly. Using Ptr --------- Xojo uses the :doc:`Ptr` type to interface with Declares, however Kotlin does not have a :doc:`Ptr` type. Instead the Any type is used with Kotlin. This means that if you are passing something that is a :doc:`Ptr` to Kotlin (such as the app context above), your library function declaration should use ``Any?`` as the type of the parameter. In your function, cast the parameter to the actual type you want. The same rule applies to return values. If the return value in the Declare is a :doc:`Ptr`, then in Kotlin the function declaration should be ``Any?``. You can also use :doc:`Ptr` with API calls to save object references as described below. Library permissions ------------------- Depending on the API you are using, you may need to add permissions to the app manifest.xml for the Library. These permissions also need to be applied to the main app. This can be done using the Advanced tab of the Android Build Settings. There you will find the Permissions property in the Inspector where you can add one Permission per line. .. note:: Previous versions let you separate permissions by spaces or commas. Going forward, each permission must be on its own line. Empty lines are ignored. There you can add the permission constants that are required by your Library. In addition, you can follow the constant with additional attributes and they will also be applied to the app's manifest file. For example, to include the android:maxSdkVersion attribute with the BLUETOOTH permission: .. code:: plain android.permission.BLUETOOTH android:maxSdkVersion="30" Library dependencies -------------------- Similarly to permissions, your Library may make use of additional dependencies, which could even be other Libraries. These are added to the build.gradle file for the Library, but also need to be included in the main app. You can add these dependencies using the Dependencies property on the Advanced Android Build Settings. Creating objects ---------------- Previous versions of Xojo only let you call Companion (essentially shared) methods on Library classes. Now you can also work with class instances and call instance methods. This gives you even better control and ability to interface with Android APIs. To create an object, your Library class should have a factory method on the Companion that returns a new instance. It is clearest to use create() as this method name. In Xojo you create a Declare to this create() method and call it, saving the result in a :doc:`Ptr`. .. code:: xojo Declare Function create Lib "com.example.utility.counter" () As Ptr Var counter As Ptr = create Instead of using a shared factory method, you can also call the Class constructor using this syntax: .. code:: xojo Declare Function counter Lib "com.example.utility.counter" () As Ptr Because the function name has the same name as the class itself, this will call the constructor of the class. When you want to call a method on the instance, you pass the instance as a :doc:`Ptr`. First, Declare to the method adding “.instance” to the library location to indicate you are calling a class instance. .. code:: xojo Declare Function increment Lib "com.example.utility.counter.instance" (ref As Ptr) As Integer Then you can call the method and save its value. .. code:: xojo Var count As Integer = increment(counter) The reference as the :doc:`Ptr` must be the first parameter passed in the Declare. Follow it with other parameters as usual. You can also call instance methods directly in the Android API in this manner. Method callbacks ---------------- There are Android APIs that use callbacks to methods that you provide. In Xojo these methods are Delegates and are methods that you supply using :doc:`AddressOf`. It is very important that the signature of this method exactly matches what the callback expects which means you will be limited to just the :doc:`Boolean` and :doc:`Ptr` types. If you want to use an API that uses its own callbacks, you will be better served by creating a Library and having the API do the callback to your own Library methods, which you can then call back to your Xojo methods. You can pass the reference to the Xojo method using :doc:`AddressOf` like this: .. code:: xojo Var cb As Ptr = AddressOf TestCallback Declare Sub xojocallback Lib "com.example.utility.callback" (cb As Ptr) xojocallback(cb) The Xojo method can only use parameter types of :doc:`Boolean` and :doc:`Ptr`. It looks like this: .. code:: xojo Public Sub TestCallback(b As Boolean, i As Ptr, s As Ptr) In the Kotlin library you have to cast the incoming parameter to a function reference. This is a two-step process where you verify the function has the correct number of parameters and then you cast it to the specific parameter types. Sample code: .. code:: kotlin if (cb is Function3<*, *, *, *>) { try { (cb as Function3).invoke(true, 42, "Hello") println("Sent callback to Xojo") } catch (e: ClassCastException) { println("Casting failed: ${e.message}") } } The *Function3* indicates a function with 3 parameters (the last ``*`` indicates the return type), but you can change that as needed using Function1, Function2, etc. You can pass anything back through a :doc:`Ptr` type, but the Xojo code has to convert them using :doc:`Ptr` methods so you should not change the types of the values you send back to be different than what your Xojo code expects. Object declares --------------- Object Declares are not new, but for completeness are described here. Essentially an Object Declare uses special syntax to Declare to a UI control. The typical way to use an Object Declare is by adding an :doc:`Extends` method to a :doc:`Module`. For example, an extension method to allow :doc:`MobileButton` to change its colors could look like this: .. code:: xojo SetBackColor(Extends ctrl As MobileButton, c As Color) The Object Declare looks like this: .. code:: xojo Declare Sub setBackgroundColor Lib "Object:ctrl:MobileButton" (myColor As Int32) setBackgroundColor(c.ToInteger) Breaking this down, we are calling the setBackgroundColor function. The Lib denotes this new type of Object declare: an Object, separated by a colon, the Xojo name of the object (in this case, our ``ctrl`` parameter), followed by another colon, and the Xojo type of the object. Kotlin declares --------------- You can use the Android-specific Kotlin Declare designation to indicates that what is specified in the Alias section of the Declare should be used as is. This is particularly helpful when you want to cast :doc:`Ptr` values to a specific type for use with OS APIs. This code gets a ColorStateList object for a Xojo Color value: .. code:: xojo Var c As Color = Color.Blue Declare Function valueOf Lib "android.content.res.ColorStateList:Kotlin" Alias _ "android.content.res.ColorStateList.valueOf(android.graphics.Color.argb(alpha.toInt(), r.toInt(), g.toInt(), b.toInt()))" _ (alpha As Int32, r As Int32, g As Int32, b As Int32) As Ptr Var csl As Ptr = valueOf(255 - c.Alpha, c.Red, c.Green, c.Blue) This code can then be used to call setStrokeColor on a :doc:`MobileButton`: .. code:: xojo Declare Sub import Lib "android.content.res.ColorStateList:Import" ' Imports the ColorStateList class Declare Sub setStrokeColor Lib "Object:ctrl:MobileDateTimePicker:Kotlin" Alias _ "setStrokeColor(strokecolor as ColorStateList)" (strokeColor As Ptr) setStrokeColor(csl) As you can see, in the Alias is the Kotlin method call that casts the incoming :doc:`Ptr` value to a ColorStateList. Import ------ Note the *import* declare in the code snippet above. This is also specific to Android. This Declare indicates that a specific class should be imported so that it can be used for casting purposes. If you left off that import, then the cast to ColorStateList would cause a compile error because ColorStateList would not be known. You only need to import a specific class once. After you have done so it can be referenced by any other Declare, even ones in different methods. The use of an import Declare is optional and is meant to simply the Kotlin method call code in the Alias. Instead of using import you can specify the full class path in the cast like this: .. code:: xojo Declare Sub setStrokeColor Lib "Object:ctrl:MobileDateTimePicker:Kotlin" Alias _ "setStrokeColor(strokecolor as android.content.res.ColorStateList)" (strokeColor As Ptr) setStrokeColor(csl) Handles ------- Many classes in the framework now have Handle properties (or methods) which you can use with various Declare techniques described above. These include: :doc:`FolderItem`, :doc:`Font`, :doc:`GraphicsPath`, :doc:`Locale`, :doc:`TimeZone`, :doc:`Graphics`, :doc:`Picture`. In addition, the mobile controls have Handle properties that can serve as an alternative for an Object Declare. Example project --------------- You can :download:`download an example project ` that demonstrates a library and some of the concepts described above. You can use this as a template or starting point for creating your own libraries or Declares. You might also want to take a look at the `Android Design Extensions `_ open source project, which has hundreds of Declares that can enhance your Android apps. .. seealso:: * :doc:`Declare` keyword * :doc:`Extends` keyword * `Android developer documentation `_ * `Android details video `_ * `Android design extensions blog post `_ * `Android libraries blog post `_ * `Android declares blog post `_ ======================= Calling native iOS APIs ======================= You can call into Cocoa Touch APIs to use methods and properties that are not built into the framework by using the :doc:`Declare` command. To create a Declare statement you first need to track down the API you want to use using Apple's documentation: `Apple Developer Documentation `_. Most of the time you will reference the `Foundation `_ and `UIKit `_ libraries, but there are many other libraries as well. Xojo Declares use the Objective-C names so be sure to refer to those in the documentation rather than the Swift naming. When you call Cocoa methods you supply the method name using the Selector part of the Declare command. The selector name has to end in a ":" if there are any parameters that are passed, an oddity of how Objective-C works. Unlike with Xojo methods, the case of the name also has to match exactly. For the Xojo Declare, the first parameter is always required and must be a reference to the class containing the method you are calling. .. _/topics/declares/calling_native_ios_apis/give_focus_to_a_text_field: Give focus to a text field -------------------------- To start with a simple example, consider that you may want to set focus to a text control such as an :doc:`MobileTextField`. Xojo does not provide a method for this. Looking at the Summary on the iOSTextField page you'll see that the actual UIKit control is called "UITextField". Click the link to open the Apple doc page. On the doc page you'll find information about calling becomeFirstResponder to show the keyboard, which is what happens when a text field gets focus. When you click on `becomeFirstResponder `_ you'll get to its doc page with this declaration: .. code:: xojo (BOOL)becomeFirstResponder; The above is Objective-C code that indicates that this is a function that returns a Boolean value. You should also note on the page that this is part of the UIKit library. With this information you can now create a Xojo Declare command to call it: .. code:: xojo Declare Function becomeFirstResponder Lib "UIKit" Selector "becomeFirstResponder" (controlHandle As Ptr) As Boolean The important thing to note here is that you have to always have a parameter (the first parameter) that is a reference to an instance of the class (or control in this case). You can call this method just as you would any other Xojo method, but remember you have to pass it a reference to the control. Assuming you have a control called TextField1 on the view you can call the method like this: .. code:: xojo Call becomeFirstResponder(TextField1.Handle) The Call command is used to ignore the return value, which is not relevant in this situation. If you want to use this function often you can wrap it in an Extension method so that it is available everywhere. To do this, create a module and add a global method to it with this declaration: .. code:: xojo SetFocus(Extends c As MobileTextField) As Boolean To the method, add the Declare code as follows: .. code:: xojo Declare Function becomeFirstResponder Lib "UIKit" Selector "becomeFirstResponder" (controlHandle As Ptr) As Boolean Return becomeFirstResponder(c.Handle) Now you can call the method like this: .. code:: xojo Call TextField1.SetFocus .. _/topics/declares/calling_native_ios_apis/use_the_clipboard: Use the Clipboard ----------------- As a more advanced example, this is how you can add some text to the system clipboard. On iOS the clipboard is actually called the pasteboard and is managed by the UIPasteboard class. Looking a Apple's docs for `UIPasteboard `_ you'll see two methods that are useful. First is the `generalPasteboard `_ shared method that gets the instance of the system pasteboard. The other method is `setValue `_ which is used to put content, such as text, onto the pasteboard. To get started, in Xojo create a public method on the App object and call it AddTextToPasteboard with this declaration: .. code:: xojo AddTextToPasteboard(value As String) The first bit of code you need has to get a reference to the UIPasteboard class and then call the generalPasteboard method to get an instance of the system UIPasteboard. In order to do this, you'll need to use a common utility function, NSClassFromString, that gets a reference to an iOS class. So this is the first line of code: .. code:: xojo Declare Function NSClassFromString Lib "Foundation" (className As CFStringRef) As Ptr You can now use this function to get a reference to the UIPasteboard class: .. code:: xojo Var uiPasteboardClass As Ptr = NSClassFromString("UIPasteboard") Now you can call the generalPasteboard method by passing in the reference to the UIPasteboard class (UIPasteboard is part of the UIKit library): .. code:: xojo Declare Function sharedApplication Lib "UIKit" Selector "sharedApplication" (classRef As Ptr) As Ptr Var sharedApp As Ptr = sharedApplication(uiAppClass) For reference, here is the declaration for the setValue method from the Apple docs: .. code:: xojo - (void)setValue:(id)value forPasteboardType:(NSString *)pasteboardType; This declaration tells you the method is a Sub (the void at the beginning indicates it does not return a value) and that it takes two parameters: a value and the pasteboardType (as Text). Since this example is for putting text in the pasteboard the setValue method can be mapped to a Xojo Declare command like this: .. code:: xojo Declare Sub setValue Lib "UIKit" Selector "setValue:forPasteboardType:" (pasteboardReference As Ptr, _ value As CFStringRef, _ pasteboardType As CFStringRef) Remember, the first parameter is always a reference to the class, which is the pasteboard in this case. Another thing to note is that the name for the selector is not just the method name of "setValue". Objective-C uses names that include the parameter so the selector is actually called "setValue:forPasteboardType" (the proper case is important). This is the full name you'll see on the Apple doc page for the method. Lastly, when you see NSString as an Objective-C type you can instead substitute the Xojo type of :doc:`CFStringRef` which will automatically convert a Text value to the correct type needed for the Declare. With this Declare in place you can now call the method to put the text onto the pasteboard: .. code:: xojo setValue(generalPasteboard(NSClassFromString("UIPasteboard")), value, "public.text") The pasteboardType takes a UTI which for text is "public.text". Combining all this code together results in this method: .. code:: xojo Public Sub AddTextToPasteboard(value As String) ' Get a reference to the system pasteboard Declare Function NSClassFromString Lib "Foundation" (className As CFStringRef) As Ptr Var uiPasteboardClass As Ptr = NSClassFromString("UIPasteboard") Declare Function generalPasteboard Lib "UIKit" Selector "generalPasteboard" (classRef As Ptr) As Ptr Var pasteboard As Ptr = generalPasteboard(uiPasteboardClass) ' Put the value onto the pasteboard Declare Sub setValue Lib "UIKit" Selector "setValue:forPasteboardType:" (pasteboardReference As Ptr, _ value As CFStringRef, _ pasteboardType As CFStringRef) setValue(generalPasteboard(NSClassFromString("UIPasteboard")), value, "public.text") End Sub You can now use your new method to put text into the pasteboard with this code: .. code:: xojo App.AddTextToPasteboard(TextField1.Text) .. _/topics/declares/calling_native_ios_apis/other_tips: Other tips ---------- .. _/topics/declares/calling_native_ios_apis/utility_functions: Utility functions ***************** For reference, these two utility functions are commonly used with Declares. You've already seen how to use NSClassFromString. The alloc method can be used to create a new instance of a class when there is otherwise no factory method that returns an instance. It is equivalent to using "New" with a Xojo class. .. code:: xojo ' Gets a reference to an iOS class Declare Function NSClassFromString Lib "Foundation" (classname As CFStringRef) As Ptr ' Usage: Var NSClass As Ptr = NSClassFromString("NSClassName") ' Gets an instance of an iOS class Declare Function alloc Lib "Foundation" Selector "alloc" (classRef As Ptr) As Ptr ' Usage: Var instance As Ptr = alloc(NSClass) .. _/topics/declares/calling_native_ios_apis/properties: Properties ********** When dealing with properties in the Apple frameworks you have to alter the property name to include "set" at the beginning (for the setter). Apparently this is because the computed property is really a method behind the scenes. So for a property called "borderStyle", the actual name would be "setBorderStyle" (note the slight change to the case as well because it matters). All properties are accessed with just the property name as the selector unless they have a special getter (generally this is only for boolean properties and the name of the getter will be provided in the docs). .. _/topics/declares/calling_native_ios_apis/see_also: .. seealso:: * `Hiding the Border in iOS Text Fields `_ * `Set iOS App Badge Icon Number `_ * `iOSKit `_ open-source library * `iOS Design Extensions `_ open-source library * `Xojo-AppleLib `_ open-source library * :doc:`iOS Declare Examples` * `Video: iOS Declares `_ ========================= Calling native Linux APIs ========================= You can call into Linux APIs to use methods and properties that are not built into the framework by using the :doc:`Declare` command. To create a Declare statement you first need to track down the API you want to use using Linux documentation. The Linux API is largely based on the C/C++ programming language and makes heavy use of structures. .. _/topics/declares/calling_native_linux_apis/window_opacity: Window opacity -------------- As a simple example, the `gtk_widget_set_opacity `_ method in libgtk-3 can be used to change the window opacity so that it appears more transparent. This is what the method declaration looks like in the Gnome docs: .. code:: xojo void gtk_widget_set_opacity (GtkWidget *widget, double opacity); This tells you it is a method (sub) because the "void" at the beginning indicates it does not return a value. The first parameter is a pointer to a GtkWidget, which in this case is the handle to the Xojo window. The opacity is a Double in the range of 0 to 1. So the above method call translates to a Declare that looks like this: .. code:: xojo Declare Sub gtk_widget_set_opacity Lib "libgtk-3" (handle As Ptr, opacity As Double) To set the window to 75% opacity you can call it like this: .. code:: xojo gtk_widget_set_opacity(Self.Handle, 0.75) Because this method is called for a window, you can put it in a Xojo Extension Method to make it easier to call. To do this, create a global method on a module like this: .. code:: xojo Public Sub Opacity(Extends w As DesktopWindow, value As Double) #If TargetLinux Then Declare Sub gtk_widget_set_opacity Lib "libgtk-3" (handle As Ptr, opacity As Double) gtk_widget_set_opacity(w.Handle, value) #EndIf End Sub Note the use of the "#If TargetLinux" line. This prevents this code from being compiled into Windows or macOS builds of your app where the code could possible crash your app. This now allows you to have code like this on a window's Opening event to center the window: .. code:: xojo Self.Opacity(0.75) .. _/topics/declares/calling_native_linux_apis/default_control_sizes: Default control sizes --------------------- With the many different Window Managers and themes on Linux, and personal preferences, you can be assured that your UI will look different from one Linux user to the next. The main challenge of being a native Linux app is trying to normalize the UI experience across different platforms (yes, even different Linux distros). As each platform has their own native default control sizes and such, your app will most likely need to be adjusted accordingly. On Windows and macOS this almost never changes, so thankfully a 22 pixel high Button would look just fine on macOS, as it does on Windows. The problem on Linux is that you have different themes that dictate how much padding should go into a control, and the much larger default font size. .. _/topics/declares/calling_native_linux_apis/obtaining_the_default_control_size: Obtaining the default control size ********************************** A solution to this problem is to use Linux APs to get the default control size. This information is provided by GTK+ in conjunction with the Window Manager. To start you first have to ask the Window Manager to let you know when the control has been "realized" (made available) so that you can request its default size. You will typically do that on the Opening event of the control. Here is the code, which you can put on the Opening event of a :doc:`Button` that has been added to a Window: .. code:: xojo #If TargetLinux Then Declare Sub g_signal_connect_data Lib "libgobject-2.0" (instance As Ptr, signal As CString, callback As Ptr, data As Ptr, destroy_data As Ptr, connectFlags As Integer) g_signal_connect_data(Button1.Handle, "realize", AddressOf RealizeCallback, Nil, Nil, 0) #Endif The actual Declare calls `g_signal_connect_data `_ in the libgobject library which takes a reference to the control, the text "realize" to indicate we want to know when the control is available and then the address of a method to call (the callback method) with the information. So now you need to create the callback method, which is just a shared method on the Window. Here is its code: .. code:: xojo Private Shared Sub RealizeCallback(widget As Ptr, data As Ptr) #If TargetLinux Then Declare Sub gtk_widget_get_preferred_size Lib "libgtk-3" _ (widget As Ptr, ByRef minSize As GtkRequisition, ByRef naturalSize As GtkRequisition) Var minSize, naturalSize As GtkRequisition gtk_widget_get_preferred_size(widget, minSize, naturalSize) For Each aControl As DesktopControl in Window1.Controls If aControl IsA DesktopUIControl Then Var dc As DesktopUIControl = DesktopUIControl(aControl) If dc.Handle = widget Then dc.Width = minSize.Width dc.Height = minSize.Height End If End If Next #Endif End Sub This method has a Declare that calls `gtk_widget_get_preferred_size `_ in the libgtk-3 library. This method takes a reference to the control and then has two parameters that contain size information. These parameters use the `GtkRequisition `_ structure which you will create in a moment. Looking at the code you can see that it is looping through all the controls on the Window and updating any that are :doc:`DesktopUIControls` to use the size reported back from the ``gtk_widget_get_preferred_size`` Declare call. The last thing you need to add is the GtkRequisition structure so add a Structure to the Window and call it GtkRequisition, with two properties: Width As Int32 and Height As Int32. .. code:: xojo Structure GtkRequisition Width As Int32 Height As Int32 End Structure One thing to keep in mind with this technique is that since it is adjusting the size of controls it will alter the look of your layout. You may want to make sure your layout has enough room for control sizes to change (probably they will increase) without causing them to overlap. .. _/topics/declares/calling_native_linux_apis/related_information: Related information ------------------- * `Gnome Developer API Reference `_ * `GLib Reference Manual `_ * `GObject Reference Manual `_ * `GTK+ 3 Reference Manual `_ ========================= Calling native macOS APIs ========================= You can call into Cocoa APIs to use methods and properties that are not built into the framework by using the :doc:`Declare` command. To create a Declare statement you first need to track down the API you want to use using Apple's documentation: `Apple Developer Documentation `_. Most of the time you will reference the `Foundation `_ and `AppKit `_ libraries, but there are many other libraries as well. Xojo Declares use the Objective-C names so be sure to refer to those in the documentation rather than the Swift naming. When you call Cocoa methods you supply the method name using the Selector part of the Declare command. The selector name has to end in a ":" if there are any parameters that are passed, an oddity of how Objective-C works. Unlike with Xojo methods, the case of the name also has to match exactly. For the Xojo Declare, the **first parameter is always required and must be a reference to the class containing the method you are calling**. .. _/topics/declares/calling_native_macos_apis/center_a_window: Center a window --------------- To start with a simple example, consider that you may to center a window on the screen. You can obviously do this by manually adjusting the window's Top and Left properties by taking into account the window size and the screen size, but Cocoa has a simple center function that can be used on a window. On macOS, a Xojo window is actually a native NSWindow. When you view Apple's `docs for NSWindow `_ you'll see there is a `center `_ method. This method is very simple as it does not take any parameters. Looking at the doc page you should note that this function is in the AppKit library. Now you can create a Declare command to map to the center method: .. code:: xojo Declare Sub centerWindow Lib "AppKit" Selector "center" (windowHandle As Ptr) Remember, even though the center method does not take any parameters, you still have to add a parameter to the Declare so you can pass in the reference to the calling class, which in this case is the window to center. You can call this method like this (such as from a button's Pressed event handler): .. code:: xojo centerWindow(Self.Handle) Because this method is called for a window, you can put it in a Xojo Extension Method to make it easier to call. To do this, create a global method on a module like this: .. code:: xojo Public Sub Center(Extends w As DesktopWindow) #If TargetMacOS Then Declare Sub centerWindow Lib "AppKit" Selector "center" (windowHandle As Ptr) centerWindow(w.Handle) #EndIf End Sub Note the use of the "#If TargetMacOS" line. This prevents this code from being compiled into Windows or Linux builds of your app where the code could possible crash your app. This now allows you to have code like this on a window's Opening event to center the window: .. code:: xojo Self.Center .. _/topics/declares/calling_native_macos_apis/display_the_standard_about_window: Display the standard About window --------------------------------- Instead of manually creating a modal window to display the About window for your app, you can instead display the standard macOS About Window. This window has a fixed size, gets the app name directly from the built app name, the icon from the app icon and the version from the version values in Shared Build Settings. On the `NSApplication `_ class there is a method called `orderFrontStandardAboutPanel `_ that displays this standard about window. The first thing you need to do is to get a reference to the shared application. Xojo does not provide this for you so you'll have to use some Declares to get at it. To get started, in Xojo create a public method on the App object and call it ShowAbout. The first bit of code you need has to get a reference to the NSApplication class and then call the sharedApplication method to get an instance of the app. In order to do this, you'll need to use a common utility function, NSClassFromString, that gets a reference to a Cocoa class. So this is the first line of code: .. code:: xojo Declare Function NSClassFromString Lib "AppKit" (className As CFStringRef) As Ptr You can now use this function to get a reference to the NSApplication class: .. code:: xojo Var nsApp As Ptr = NSClassFromString("NSApplication") Next you can use this to call the sharedApplication method on NSApplication with this Declare: .. code:: xojo Declare Function SharedApplication Lib "AppKit" Selector "sharedApplication" (receiver As Ptr) As Ptr Var sharedApp As Ptr = SharedApplication(nsApp) You now have the reference to the app in the sharedApp variable. The last thing to do is to call the orderFrontStandardAboutPanel method, which has this Objective-C declaration in the Apple docs: .. code:: xojo (void)orderFrontStandardAboutPanel:(id)sender; This tells you it is a Sub (the void at the beginning means it does not return a value) and that it takes one parameter. So the Declare looks like this: .. code:: xojo Declare Sub OrderFrontStandardAboutPanel Lib "AppKit" Selector "orderFrontStandardAboutPanel:" (sharedApp As Ptr, ID As Ptr) And lastly you can call this method like this (a value for the ID parameter is not actually needed): .. code:: xojo OrderFrontStandardAboutPanel(sharedApp, Nil) Combining all this code together results in this: .. code:: xojo Public Sub ShowAbout() #If TargetMacOS Then Declare Function NSClassFromString Lib "AppKit" (className As CFStringRef) As Ptr Var nsApp As Ptr = NSClassFromString("NSApplication") Declare Function SharedApplication Lib "AppKit" Selector "sharedApplication" (receiver As Ptr) As Ptr Var sharedApp As Ptr = SharedApplication(nsApp) Declare Sub OrderFrontStandardAboutPanel Lib "AppKit" Selector "orderFrontStandardAboutPanel:" (sharedApp As Ptr, ID As Ptr) OrderFrontStandardAboutPanel(sharedApp, Nil) #EndIf End Sub As above, note the use of the "#If TargetMacOS" line which prevents this code from being compiled into Windows or Linux builds of your app where the code could possibly crash your app. You can now display the about window with this code: .. code:: xojo App.ShowAbout .. _/topics/declares/calling_native_macos_apis/64-bit_information: 64-bit information ------------------ Some API calls need to adapt when used in a 64-bit app. For example, some API calls expect an Int32 in an 32-bit app but want an Int64 in a 64-bit app. If you have hard-coded usage of Int32 in your Declare API calls then you'll have to fix them so that they work for 64-bit. For Int32/Int64 you should just use the :doc:`Integer` type which will automatically use the correct type for the type of the app. You may run into a similar issue with floating point where Single is used in 32-bit apps, but Double is used for 64-bit apps. In these cases use the :doc:`CGFloat` type to have it automatically choose the correct type. .. _/topics/declares/calling_native_macos_apis/see_also: .. seealso:: * :doc:`Declare` keyword * :doc:`macOS Declare Samples` * `Using Declares in Your Desktop Apps `_ * `macoslib `_ open-source project =========================== Calling native Windows APIs =========================== You can call into Win32 APIs (aka WinAPI) to use methods and properties that are not built into the framework by using the :doc:`Declare` command. To create a Declare statement you first need to track down the API you want to use using Microsoft's documentation: `Microsoft Developer Documentation `_. The Win32 API is largely based on the C/C++ programming language and makes heavy use of structures. .. _/topics/declares/calling_native_windows_apis/flash_the_window: Flash the window ---------------- As a simple example, you can call a function to flash the window to get the user's attention. Refer to the `FlashWindow doc page `_ in the Microsoft Win32 docs, where you can see the declaration for this method. It looks like this: .. code:: xojo BOOL WINAPI FlashWindow( _In_ HWND hWnd, _In_ BOOL bInvert ); This tells you that this method is a function (the BOOL at the beginning indicates it returns a Boolean) and that it takes two parameters. The HWND parameter is the handle to the window, which you get from Xojo as Window.Handle (it is an Integer). The BOOL parameter is a Boolean. At the bottom of the doc page there is a section that tells you the Windows OS library that contains this function, which is "User32.lib". With this information you can create the :doc:`Declare` statement to this function, which looks like this: .. code:: xojo Declare Function FlashWindow Lib "User32" (handle As Ptr, invert As Boolean) As Boolean You can call this function by passing in a window handle, so if you had the above Declare on a button's Pressed event handler you could flash the window with this code: .. code:: xojo Call FlashWindow(MyWindow.Handle, True) Since the return value is not needed, the Call statement is used to avoid having to declare a variable to store the result. If you wanted to make this more easily accessible you could use the Extends method feature to make this available for any window. To do this, create a global method on a module with this code: .. code:: xojo Public Sub Flash(Extends w As DesktopWindow) #If TargetWindows Then Declare Function FlashWindow Lib "User32" (handle As Ptr, invert As Boolean) As Boolean Call FlashWindow(w.Handle, True) #EndIf End Sub You could then call this method from code on a Window like this: .. code:: xojo Self.Flash .. _/topics/declares/calling_native_windows_apis/related_information: Related information ------------------- * `WinAPILib open-source library `_ * :doc:`Windows Declare Examples` * `Windows API Data Conversions for Xojo `_ * `I Wish I Knew How To… Implement Declares with Xojo on Windows `_ book * `Windows Functionality Suite open-source library `_ * Examples/Platform-Specific/Windows ================ Examples for iOS ================ .. _/topics/declares/examples_for_ios/commonly_used_declares: Commonly used declares ---------------------- .. code:: xojo ' Gets a reference to a class Declare Function NSClassFromString Lib "Foundation" (classname As CFStringRef) As Ptr ' Usage: Var NSClass As Ptr = NSClassFromString("NSClassName") ' Gets an instance of a class Declare Function alloc Lib "Foundation" Selector "alloc" (classRef As Ptr) As Ptr ' Usage: Var instance As Ptr = alloc(NSClass) .. _/topics/declares/examples_for_ios/set_focus_on_an_ios_control: Set focus on an iOS control --------------------------- .. code:: xojo Declare Sub becomeFirstResponder Lib "Foundation" Selector "becomeFirstResponder" (ref As Ptr) becomeFirstResponder(TextField1.Handle) .. _/topics/declares/examples_for_ios/clears_focus_on_an_ios_control: Clears focus on an iOS control ------------------------------ .. code:: xojo Sub ClearFocus(Extends c As MobileControl) Declare Sub resignFirstResponder Lib "Foundation" Selector "resignFirstResponder" (ref As Ptr) resignFirstResponder(c.Handle) End Sub .. _/topics/declares/examples_for_ios/open_a_url_in_the_default_app_that_can_handle_it_(showurl: Open a URL in the default app that can handle it (ShowURL) ---------------------------------------------------------- The openURL command can be called with a series of Declares, the last of which requires a Block. Here are the Declares: .. code:: xojo Public Function ShowURL(url As String) as Boolean ' NSString* launchUrl = @"http://www.xojo.com/"; ' [[UIApplication sharedApplication] openURL:[NSURL URLWithString: launchUrl]]; Declare Function NSClassFromString Lib "Foundation" (name As CFStringRef) As Ptr Declare Function sharedApplication Lib "UIKit" Selector "sharedApplication" (obj As Ptr) As Ptr Var sharedApp As Ptr = sharedApplication(NSClassFromString("UIApplication")) ' https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/#//apple_ref/occ/clm/NSURL/URLWithString: Declare Function URLWithString Lib "Foundation" Selector "URLWithString:" (id As Ptr, URLString As CFStringRef) As Ptr Var nsURL As Ptr = URLWithString(NSClassFromString("NSURL"), url) ' https://developer.apple.com/Library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html#//apple_ref/occ/instm/UIApplication/openURL: Declare Function openURL Lib "UIKit" Selector "openURL:options:completionHandler:" (id As Ptr, nsurl As Ptr, options As Ptr, handler As Ptr) As Boolean Var b As New ObjCBlock(AddressOf URLResult) Return openURL(sharedApp, nsURL, Nil, b.Handle) End Function URLResult is a global method with this Declaration: .. code:: xojo Public Sub URLResult(success As Boolean) ' Your code here, although nothing is required End Sub .. _/topics/declares/examples_for_ios/check_the_scale_for_the_main_ios_screen: Check the scale for the main iOS screen --------------------------------------- .. code:: xojo Function MainScreenScale() As Double Declare Function NSClassFromString Lib "Foundation" (aClassName As CFStringRef) As Ptr Declare Function scale Lib "Foundation" Selector "scale" (classRef As Ptr) As CGFloat Declare Function mainScreen Lib "Foundation" Selector "mainScreen" (classRef As Ptr) As Ptr Return scale(mainScreen(NSClassFromString("UIScreen"))) End Function .. _/topics/declares/examples_for_ios/disable_the_idle_timer_to_prevent_the_device_from_sleeping: Disable the Idle Timer to prevent the device from sleeping ---------------------------------------------------------- .. code:: xojo Function SetIdleTimerDisabled(disabled As Boolean) Declare Sub setIdleTimerDisabled Lib "UIKit" Selector "setIdleTimerDisabled:" (obj_id As Ptr, disabled As Boolean) Declare function sharedApplication Lib "UIKit" Selector "sharedApplication" (clsRef As Ptr) As Ptr Declare function NSClassFromString Lib "Foundation" (clsName As CFStringRef) As Ptr setIdleTimerDisabled(sharedApplication(NSClassFromString("UIApplication")), disabled) End Function .. _/topics/declares/examples_for_ios/vibrate_device: Vibrate device -------------- .. code:: xojo Public Sub Vibrate() Const kSystemSoundID_Vibrate = 4095 Declare Sub AudioServicesPlaySystemSound Lib "AudioToolbox.framework" (snd As Integer) AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) End Sub .. _/topics/declares/examples_for_ios/change_text_field_border: Change text field border ------------------------ This is how you can create an extension method to change the border style of an :doc:`MobileTextField`. Create a module and add this Enumeration to it: .. code:: xojo Public Enum BorderStyles None = 0 Line = 1 Bezel = 2 RoundedRect = 3 End Enum Now add this method: .. code:: xojo Public Sub BorderStyle(Extends tf As MobileTextField, Assigns style As BorderStyles) Declare Sub setBorderStyle Lib "UIKit.Framework" _ Selector "setBorderStyle:" (obj As Ptr, value As Integer) setBorderStyle(tf.Handle, Integer(style)) End Sub You can call this in the Opening event for an MobileTextField like this: .. code:: xojo Me.BorderStyle = BorderStyles.None .. _/topics/declares/examples_for_ios/change_control_background_color: Change control background color ------------------------------- This is how you can create an extension method to change the background color of most iOS controls. Create a module and add this method to it: .. code:: xojo Public Sub BackgroundColor(Extends c As MobileUIControl, Assigns col As Color) Declare Function NSClassFromString Lib "Foundation" (className As CFStringRef) As Ptr Declare Function colorWithRGBA Lib "UIKit" Selector "colorWithRed:green:blue:alpha:" (UIColorClassRef As Ptr, red As CGFloat, green As CGFloat, blue As CGFloat, alpha As CGFloat) As Ptr Declare Function view Lib "UIKit" Selector "view" (UIViewController As Ptr) As Ptr Declare Sub setBackgroundColor Lib "UIKit" Selector "setBackgroundColor:" (UIView As Ptr, UIColor As Ptr) Var UIColorClassPtr As Ptr = NSClassFromString("UIColor") Var red As CGFloat = col.Red / 255 Var green As CGFloat = col.Green / 255 Var blue As CGFloat = col.Blue / 255 Var alpha As CGFloat = 1.0 - col.Alpha / 255 Var colorPtr As Ptr = colorWithRGBA(UIColorClassPtr, red, green, blue, alpha) Var viewPtr As Ptr = c.Handle setBackgroundColor(viewPtr, colorPtr) End Sub You can call this method in the Opening event of an MobileTextField to change its background color to red: .. code:: xojo Me.BackgroundColor = Color.RGB(255, 0, 0) .. _/topics/declares/examples_for_ios/see_also: .. seealso:: :doc:`Declare` keyword ================== Examples for macOS ================== .. _/topics/declares/examples_for_macos/current_logged-in_user: Current logged-in user ---------------------- .. code:: xojo Declare Function NSFullUserName Lib "Foundation" As CFStringRef .. _/topics/declares/examples_for_macos/center_a_window: Center a window --------------- .. code:: xojo Declare Sub center Lib "AppKit" Selector "center" (windowRef As Ptr) center(Self.Handle) ' The Handle property of the Window .. _/topics/declares/examples_for_macos/miniaturize_a_window_to_the_dock: Miniaturize a window to the dock -------------------------------- .. code:: xojo Declare Sub miniaturize Lib "AppKit" Selector "miniaturize:" (windowRef As Ptr, id As Ptr) miniaturize(Self.Handle, Nil) ' The Handle property of the Window .. _/topics/declares/examples_for_macos/move_a_window_with_animation: Move a window with animation ---------------------------- .. code:: xojo Var newRect As NSRect ' note that coordinates are upside down from what you are used to with Carbon ' 0,0 is at the bottom left newRect.x = 10 newRect.y = Screen(0).Height - 30 newrect.width = Self.Width newrect.height = Self.Height + 22 Declare Sub setFrameDisplayAnimate Lib "AppKit" Selector "setFrame:display:animate:" _ (windowRef As Ptr, rect As NSRect, display As Integer, animate As Integer) setFrameDisplayAnimate(Self.Handle, newRect, 1, 1) .. _/topics/declares/examples_for_macos/display_standard_cocoa_about_window: Display standard Cocoa About window ----------------------------------- .. code:: xojo Declare Function NSClassFromString Lib "AppKit" (aClassName As CFStringRef) As Ptr Declare Function SharedApplication Lib "AppKit" Selector "sharedApplication" (receiver As Ptr) As Ptr Var sA As Ptr = NSClassFromString("NSApplication") sA = SharedApplication(sA) Declare Sub OrderFrontStandardAboutPanel Lib "AppKit" Selector "orderFrontStandardAboutPanel:" _ (receiver As Ptr, ID As Ptr) OrderFrontStandardAboutPanel(sA, Nil) .. _/topics/declares/examples_for_macos/display_a_textfield_with_rounded_corners: Display a TextField with rounded corners ---------------------------------------- .. code:: xojo Declare Sub setBezelStyle Lib "AppKit" Selector "setBezelStyle:" (handle As Ptr, value As Integer) setBezelStyle(Me.Handle, 1) .. _/topics/declares/examples_for_macos/play_a_cocoa_notification_sound: Play a Cocoa notification sound ------------------------------- .. code:: xojo Const kAppKit = "AppKit" Soft Declare Function NSClassFromString Lib kAppKit (aClassName As CFStringRef) As Ptr Soft Declare Function SoundNamed Lib kAppKit Selector "soundNamed:" (ClsPtr As Ptr, name As CFStringRef) As Ptr Soft Declare Function Play Lib kAppKit Selector "play" (instPtr As Ptr) As Boolean Var NSSoundClassPtr As Ptr = NSClassFromString("NSSound") Var notificationSound As Ptr = SoundNamed(NSSoundClassPtr, "Frog") Call Play(notificationSound) .. _/topics/declares/examples_for_macos/open_a_document_in_a_specific_app: Open a document in a specific app --------------------------------- .. code:: xojo ' Launch help with selected preview browser Declare Function NSClassFromString Lib "AppKit" (name As CFStringRef) As Ptr Declare Function sharedApplication Lib "AppKit" Selector "sharedWorkspace" (obj As Ptr) As Ptr Var sharedWorkspace As Ptr = sharedApplication(NSClassFromString("NSWorkspace")) Declare Function openFile Lib "AppKit" Selector "openFile:withApplication:" (id As Ptr, urlString As CFStringRef, appName As CFStringRef) As Boolean ' fDocument is a FolderItem that points to the document Call openFile(sharedWorkspace, fDocument.NativePath, "AppName.app") .. _/topics/declares/examples_for_macos/see_also: .. seealso:: :doc:`Declare` keyword ==================== Examples for Windows ==================== .. _/topics/declares/examples_for_windows/flash_app_indicator_in_task_bar: Flash app indicator in Task Bar ------------------------------- .. code:: xojo Declare Function FlashWindow Lib "User32" (HWND As Ptr, invert As Boolean) As Boolean Call FlashWindow(MyWindow.Handle, True) ' flash once .. _/topics/declares/examples_for_windows/enable_window_compositing: Enable window compositing ------------------------- .. code:: xojo Sub EnableComposite(w As DesktopWindow) #If TargetWindows Const GWL_EXSTYLE = -20 Const WS_EX_COMPOSITED = &h2000000 Declare Function GetWindowLongW Lib "user32" (hwnd As Ptr, nIndex As Int32) As Integer Var style As Integer = GetWindowLongW(w.Handle, GWL_EXSTYLE) style = BitwiseOr(style, WS_EX_COMPOSITED) Declare Sub SetWindowLongW Lib "user32" (hwnd As Ptr, nIndex As Int32, dwNewLong As Integer) SetWindowLongW(w.Handle, GWL_EXSTYLE, style) #EndIf End Sub .. _/topics/declares/examples_for_windows/declare_example_projects: Declare example projects ------------------------ These can be found in Examples/Topics/Declares. .. _/topics/declares/examples_for_windows/other_resources: Other resources --------------- These external sources of information might also be helpful when working with Declares on Windows: * `WinAPILib library on GitHub `_ * `Calling Native Windows APIs `_ * `Windows Functionality Suite on GitHub `_ * `Windows APIs to Xojo data type conversion `_ * `Implement Declares with Xojo on Windows book from xDevLibrary `_ .. _/topics/declares/examples_for_windows/see_also: .. seealso:: :doc:`Declare` File management ============== .. toctree:: :maxdepth: 1 :name: sec-file_management Accessing files Accessing binary files Accessing files in a web application Accessing the file system via the FolderItem class Accessing text files Reading and writing data in JSON format Reading and writing data in XML format Understanding File Types Understanding Uniform Type Identifiers =============== Accessing files =============== These questions are related to file handling, which make use of the :doc:`FolderItem` class. .. _/topics/file_management/accessing_files/how_do_i_load_and_save_data_to_a_file?: How do I load and save data to a file? -------------------------------------- There are several classes to help you load and save your data. If you are working with text files, you use the :doc:`TextInputStream` class to load data and the :doc:`TextOutputStream` class to save data. If you are working with binary data (or data with a fixed-size file format) you can use the :doc:`BinaryStream` class. This code prompts the user to choose a file and then its contents are read and displayed in a text area: .. code:: xojo Var f As FolderItem = FolderItem.ShowOpenFileDialog("") If f <> Nil Then If f.Exists Then ' Be aware that TextInputStream.Open could raise an exception Var t As TextInputStream Try t = TextInputStream.Open(f) t.Encoding = Encodings.UTF8 TextArea1.Text = t.ReadAll Catch e As IOException MessageBox("Error accessing file.") End Try t.Close End If End If This code saves text in a TextArea to a text file in the Documents folder: .. code:: xojo Var Documents As FolderItem = SpecialFolder.Documents If Documents <> Nil Then Var f As FolderItem = Documents.Child("Sample.txt") If f <> Nil Then Try ' TextOutputStream.Create raises an IOException if it can't open the file for some reason. Var t As TextOutputStream = TextOutputStream.Create(f) t.Write(TextField1.Text.ConvertEncoding(Encodings.UTF8)) t.Close Catch e As IOException ' handle End Try End If End If .. _/topics/file_management/accessing_files/how_do_i_list_files_in_a_folder?: How do I list files in a folder? -------------------------------- The FolderItem class is used to access the file system. You can use its :ref:`Children` property to iterate through files contained in a folder. This code puts the name of all files on the Desktop into a ListBox: .. code:: xojo For Each item As FolderItem In SpecialFolder.Desktop.Children ListBox1.AddRow(item.Name) Next .. _/topics/file_management/accessing_files/how_do_i_upload_and_download_files_in_a_web_app?: How do I upload and download files in a web app? ------------------------------------------------ To allow users to upload files to you web app, you use the :doc:`WebFileUploader` control. This control provides a user interface for the user to select and add files to a list to be uploaded. You then add a button to the page that calls the :ref:`WebFileUploader.StartUpload` method on the control to actually upload the files to your web app on the server. When the upload has finished, the :ref:UploadFinished`` event handler is called where you can get a list of the files that were uploaded so that you can process them as necessary. To make a file available for download, you first have to create a :doc:`WebFile` that refers to the file on the drive. This WebFile should be a Property of the WebPage so that it does not go out of scope while the file is downloading, which would cause an incomplete download. You then want to set the :ref:`WebFile.ForceDownload` property to True so that the file is downloaded rather than displayed in the browser. To start the download, you call ShowURL and pass in the URL of the WebFile that you created. For more information: * Examine the WebFileUploader example project (Examples/API/User Interface/Web) that shows you how to upload files to a web app. * Examine the Downloading example project (Examples/Topics/Web/Downloading Files) that shows you how to download files from a web app. * Watch the `Uploading and Downloading Files with a Web App `_ video. .. _/topics/file_management/accessing_files/how_do_i_create_a_folder?: How do I create a folder? ------------------------- To create a folder, you first create a FolderItem that points to where you want the folder. Then you call the :ref:`FolderItem.CreateFolder` method. This code creates a folder called "MyData" in the Documents folder: .. code:: xojo Var myData As FolderItem = SpecialFolder.Documents.Child("MyData") myData.CreateFolder ====================== Accessing binary files ====================== Binary files are files that store values in their binary format rather than as text. For example, the number 30000 stored as text requires 5 characters of text to store in a text file, which can be 5 or more bytes depending on the encoding. In a binary file, this number can be written as a short integer (or just “short”), which requires only 2 bytes. Binary files also have the added benefit that you can read and write to a file without having to close the file in-between, as is required with text files. For example, you can open a binary file, read some data, then write some data, and close it. You can also read and write anywhere in the file without having to read through all the data preceding the data you want. Although text-based file formats are increasingly common, many apps also store data in a binary format. The format is the arrangement of data within the file. In order to read a binary file, you must know how the data is arranged. If your own app created the file, you will know this, but if the file was created by an app you didn't write, you may not know it. Some formats are made public. For example, the PNG format is public. Other formats are not. Many software vendors do not publish the binary formats that their apps use to create documents. .. _/topics/file_management/accessing_binary_files/binary_streams: Binary streams -------------- Data read from or written to a binary file is accessed using the :doc:`BinaryStream` class. This class represents the flow of binary data between the :doc:`FolderItem` and the file it represents. Unlike the TextInputStream class (which can only be used to read from a text file) and the TextOutputStream class (which can only be used to write data to a text file), the BinaryStream class can be used for both reading data and writing data. You can even indicate to the BinaryStream that you will only be reading data from the file so that the file can continue to be available to other apps for reading or writing. You can use a BinaryStream to read and write specific types of data, such as strings, short integers, long integers, currency, and single bytes. They can also be used to read and write raw unformatted binary data as a block of bytes. .. _/topics/file_management/accessing_binary_files/reading_from_a_binary_file: Reading from a binary file -------------------------- Once you have a FolderItem that represents the file you wish to open, you open the file using the Open method of the BinaryStream class. It returns a BinaryStream object which you then use to read data from the stream. The BinaryStream class includes separate methods for reading each of the built-in data types. The BinaryStream keeps track of the last byte position in the file you read from in its *BytePosition* property. However, you can change this property's value to move the byte position to any location in the file. This desktop code displays the Open File Selector, reads a file made up of strings, and displays those strings in a TextArea. Notice that since the code is only reading data and not writing, False is passed to the Open method to indicate the file should be opened in “read-only” mode. Also, reading continues in a loop until the stream's *EndOfFile* is :doc:`True`. The EndOfFile is becomes True once the end of the file is reached. .. code:: xojo Var f As FolderItem Var stream As BinaryStream f = FolderItem.ShowOpenFileDialog(FileTypes1.Text) If f <> Nil Then stream = BinaryStream.Open(f, False) Do TextArea1.AddText(stream.Read(255)) Loop Until stream.EndOfFile stream.Close End If When you read a binary file that contains text, you need to take the encoding of the characters into account. To do so, you can pass an optional parameter to the Read method that specifies the encoding. Use the :doc:`Encodings` module to get any encoding and pass it to Read. For example, the following code specifies the UTF8 encoding: .. code:: xojo TextArea1.AddText(stream.Read(255, Encodings.UTF8)) .. _/topics/file_management/accessing_binary_files/writing_to_a_binary_file: Writing to a binary file ------------------------ Once you have a FolderItem that represents the file you wish to open and write to, you can open the file using the Open method of the BinaryStream class. If you are creating a new file, use the Create method of the BinaryStream class. This method returns a BinaryStream. You then use the appropriate method for writing data to the stream. The BinaryStream class includes separate methods for each of the built-in data types. The BinaryStream keeps track of the last position in the file you wrote to in its Position property. However, you can change this property's value to move the position to any location in the file. When you are finished writing data to the file, call the BinaryStream's Close method to close the stream to the file making the file available to be opened again. This desktop code displays the Save As dialog box and writes the contents of the TextArea1 to a text file: .. code:: xojo Var f As FolderItem Var stream As BinaryStream f = FolderItem.OpenSaveFileDialog(FileTypes1.Text, "Untitled.txt") If f <> Nil Then stream = BinaryStream.Create(f, True) stream.Write(TextArea1.Text) stream.Close End If You can also append data to the end of a binary file if you open it in read/write mode. This code is a modified version of the code in the "Reading from a Binary File" section above. It opens the file in read/write mode (by specifying True for the 2nd parameter) and then after the EOF is reached, it writes some additional data (in this case text) to the end. .. code:: xojo Var f As FolderItem Var stream As BinaryStream f = FolderItem.ShowOpenFileDialog("") If f <> Nil Then stream = BinaryStream.Open(f, True) Do TextArea1.AddText(stream.Read(255)) Loop Until stream.EndOfFile stream.Write(EndOfLine + "This text is added to the end of the file.") stream.Close End If .. _/topics/file_management/accessing_binary_files/see_also: .. seealso:: :doc:`BinaryStream`, :doc:`FolderItem` classes; :doc:`Accessing Text Files` topic ==================================== Accessing files in a web application ==================================== When working with web apps you can access files on the server as described in these topics: * :doc:`Accessing the file system via the FolderItem Class` * :doc:`Accessing Text Files` * :doc:`Accessing Binary Files` You may also need to make files available for upload or download. The :doc:`WebFile` class is used to make files available for download or to process files that were uploaded using the :doc:`File Uploader` control. .. _/topics/file_management/accessing_files_in_a_web_application/downloading_files: Downloading files ----------------- To start a download on a WebFile, you use the :ref:`System.GotoURL` method to show its URL: .. code:: xojo System.GotoURL(MyWebFile.URL) Keep in mind that MyWebFile must remain in scope in order for the download to complete. In general, the easiest way to create a WebFile is to use a FolderItem as the starting point: .. code:: xojo Var localFile As FolderItem localFile = New FolderItem("localfile.txt") ' mTextFile is a property of the web page ' so that it does not go out of scope ' while the file is downloading. mTextFile = WebFile.Open(localFile) System.GotoURL(mTextFile.URL) The above code ends up loading the file from disk into memory for each session that initiates the file download. This could quickly use up a lot of memory on your web server. A better approach is to store a single reference to the WebFile on the global App class. This single reference can be used by all sessions so that multiple copies are not created in memory. .. code:: xojo Var localFile As FolderItem localFile = New FolderItem("localfile.txt") ' App.TextFile is a property of App ' so that it does not go out of scope ' while the file is downloading and so that ' it can be reused by multiple sessions. App.TextFile = WebFile.Open(localFile) System.GotoURL(App.TextFile.URL) The URL property gets a URL that is specific to the Session while using the same instance of the WebFile, saving RAM on the server. Both of the above code samples are using the default method of the Opening event to load the entire file into memory. To save even more memory on your web server, you can instead have the file read directly from disk (in 64K chunks) by specifying False as the second parameter: .. code:: xojo Var localFile As FolderItem localFile = New FolderItem("localfile.txt") ' App.TextFile is a property of App ' so that it does not go out of scope ' while the file is downloading. ' The False parameter loads the file from ' disk in 64K chunks instead of loading it all ' into memory at once. mTextFile = WebFile.Open(localFile, False) System.GotoURL(App.TextFile.URL) But an in-memory WebFile can also be useful, especially if you are creating a file to directly download. You create a WebFile in memory and supply it with data by using the Data property: .. code:: xojo ' mTextFile is a property of the web page mTextFile = New WebFile mTextFile.MimeType = "text/plain" ' Ensure the browser downloads the file rather ' than trying to display it. mTextFile.ForceDownload = True mTextFile.FileName = "TextFile.txt" mTextFile.Data = "Hello, world!" ' This causes the file to be downloaded System.GotoURL(mTextFile.URL) The above code also demonstrates the usage of the MimeType to specify the type of data contained in the WebFile and the ForceDownload property to ensure the browser always downloads the file. Some browsers may always try to display certain file types (such as text, PDF, etc), however. .. _/topics/file_management/accessing_files_in_a_web_application/uploading_files: Uploading files --------------- To upload files to the server using a web app, you can use the :doc:`WebFileUploader` control described in :doc:`File Uploader`. This control allows the user to add one or more files to be uploaded. When the upload starts (by calling the Upload method), all the files are sent to the web app on the server where you can process them in the UploadFinished event handler. Uploaded files larger than 256K are written directly to the temporary folder if it is writeable. The file is kept in memory if the temporary folder is not writeable or the file is 256K or smaller. For files kept in memory, be aware of the maximum available RAM you have on your web server. Once the file are uploaded, if the temporary folder is writeable the files in memory are also copied to disk and the UploadCompleted event handler is called. If the temporary folder was not writeable then all files are kept in memory. Regardless, you must process the files in the UploadCompleted event handler in order to save them. Files in the temporary folder are deleted and ones in memory are released when the UploadCompleted event handler returns. The code below processes each uploaded file (which is a WebUploadedFile) in memory and saves its data to an actual file on disk by using a FolderItem and BinaryStream. .. code:: xojo Var bs As BinaryOutputStream Var f As FolderItem For Each file As WebUploadedFile In Files f = New FolderItem(file.Name) Try bs = BinaryStream.Create(f, True) bs.Write(file.Data) bs.Close Catch e As IOException ' Error, skip file End Try Next However, it is simpler and uses less memory to just call the Save method to save the file to a permanent location: .. code:: xojo Var saveFile As FolderItem For Each file As WebUploadedFile In Files saveFile = New FolderItem(file.Name) Try file.Save(saveFile) Catch e As IOException ' File Error, skip file Continue End Try Next .. _/topics/file_management/accessing_files_in_a_web_application/see_also: .. seealso:: :doc:`FolderItem`, :doc:`WebFile` classes; :doc:`File Uploader` topic ================================================== Accessing the file system via the FolderItem class ================================================== All file access use done using a class called :doc:`FolderItem`. A FolderItem is anything that can be stored on a drive such as volumes, folders, files, applications, and documents. Using the FolderItem class, you can get a reference to any such items on your drives. To read from a file, you need a FolderItem for it. To write to a file, you need a FolderItem. When you ask users to select a file using one of the file selectors, you get a FolderItem referring to the file they selected. Once you have a FolderItem, you can refer to its properties (such as Name or path) and perform actions on it such as deleting or copying it. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/shortcuts_and_aliases: Shortcuts and aliases --------------------- Shortcuts (aliases on Mac) are files that actually represent a volume, app, folder, or file stored in another location and possibly under another name. The FolderItem class contains properties and methods that allow you to either resolve the shortcut and work with the actual object or work with the object directly. The FolderItem constructor, Child and ChildAt methods automatically resolves a shortcut when it encounters it. However, each also provides an option to work with the shortcut itself. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/file_locations: File locations -------------- When you create your own files, you should avoid creating them beside the app itself. Modern operating systems restrict permissions on apps in the Application (or Program Files) folders so you would get an error in most cases. In addition, when running your app from Xojo on Windows, the build folder is recreated each time you run so any files you place alongside the app will be deleted. You should instead use more appropriate locations for files such as the user's Documents folder or system folders such as Application Support or AppData. You can get access to these locations using :doc:`SpecialFolder`.Documents or SpecialFolder.ApplicationData. Refer to the next section for more information. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/accessing_a_file_from_a_specific_location: Accessing a file from a specific location ----------------------------------------- If you know the full path to a file and you wish to access the file, you can do so by specifying the path to the file. For example, suppose you have a document called “Schedule” stored in the same folder as your app. The relative path starts with the folder your app is in. The FolderItem.Constructor function can be used to quickly get a reference to a file as seen in the following code: .. code:: xojo Var f As FolderItem f = New FolderItem("Schedule") To get the folder where your app resides: The full path (sometimes called the native path) to a volume, folder, app, or document starts with the volume name followed by the path delimiter character (a backslash on Windows and a forward slash on Mac and Linux), the names of any folders in the path (each separated by the path delimiter) and ending with the name of the item. To create a native path to a file or folder, you should use the FolderItem.DriveAt shared method to build a full path to the item, starting with the drive it is on. You then use the Child method of the FolderItem class to navigate to the item. DriveAt returns a FolderItem for one of your mounted volumes. You specify the volume by passing an integer, indicating the volume. Volume 0 is the volume that contains the operating system — the “boot” volume. .. code:: xojo Var f As FolderItem f = FolderItem.DriveAt(0) ' the boot volume The Parent property returns the FolderItem for the next item up in the path for the current FolderItem. It returns :doc:`Nil` if you try to get the parent of a volume. The Child and ChildAt methods let you access any items one level below the current FolderItem. You can build a full path starting from a volume with the Child method. For example, if you want to get a FolderItem for the file “Schedule” in the folder “Stuff” on the boot volume, the code would be: .. code:: xojo Var f As FolderItem f = FolderItem.DriveAt(0).Child("Stuff").Child("Schedule") The following code works with a relative path. It uses the Parent property to get the FolderItem for the folder that contains the folder in which the app is located. Passing the empty string to the FolderItem.Constructor gets the current folder, so the parent of that folder is one level up in the hierarchy. .. code:: xojo Var f As New FolderItem("") f = f.Parent Once you have a FolderItem, you can (depending on what type of item it is) copy it, delete it, rename it, read from it or write to it, etc. You will learn how to read and write to files using FolderItems later in this topic. The FolderItem.Constructor method has an optional parameter that allows you to pass a native path, a shell path, or a URL path. It uses the :ref:`PathModes` enumeration from the FolderItem class. You specify the type of path by passing one of the class constants as the second parameter in a call to FolderItem.Constructor. For example, the following uses a shell path on Linux. It returns a FolderItem for the “Documents” folder in the home folder for the user “Joe.” .. code:: xojo Var f As FolderItem f = New FolderItem("/home/Joe/Documents", FolderItem.PathModes.Shell) If f.Exists Then TextField1.Text = f.NativePath Else MessageBox("The FolderItem does not exist.") End If A URL path must begin with “file:///” The following example uses the URL path to the user's “Documents” folder on Windows: .. code:: xojo Var f As FolderItem f = New FolderItem("file:///C:/Documents%20and%20Settings/" _ + "Joe%20User/My%20Documents/", FolderItem.PathModes.URL) If f.Exists Then MessageBox(f.NativePath) Else MessageBox("The FolderItem does not exist.") End If The FolderItem class's properties NativePath, URLPath, and ShellPath contain the types of paths. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/accessing_system_folders: Accessing system folders ************************ Operating systems have specific locations for various folders that contain information, such as the Documents folder for the user. Use the :doc:`SpecialFolder` module to get FolderItems representing these special system folders. The benefit of using this module rather than attempting to recreate the path manually, is that SpecialFolder always works and is correct across platforms (in most cases) and languages. You obtain the desired FolderItem using the syntax: .. code:: xojo result = SpecialFolder.FolderName where result is the FolderItem you want to obtain and FolderName is the name of the SpecialFolder function that returns that FolderItem. For example, the following gets a FolderItem for the "Application Support" folder on macOS and the "Application Data" directory on Windows: .. code:: xojo Var f As FolderItem f = SpecialFolder.ApplicationData Refer to :doc:`SpecialFolder` for the complete list of supported functions and the FolderItems that are available for macOS, Windows and Linux. Note that not all functions return FolderItems on all platforms. If a FolderItem is not defined on all platforms, you should use an alternative function that returns a FolderItem on every platform. Check that the result is not :doc:`Nil` before using the FolderItem. For example, SpecialFolder.Documents returns the current user's Documents folder on macOS and Windows but returns :doc:`Nil` on Linux. On Linux, you should call SpecialFolder.Home instead. For example: .. code:: xojo Var f As FolderItem #If Not TargetLinux f = SpecialFolder.Documents #Else f = SpecialFolder.Home #EndIf If f <> Nil Then If f.Exists Then ' use the FolderItem End If Else MessageBox("FolderItem is Nil!") End If .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/verifying_the_folderitem: Verifying the FolderItem ************************ When you try to get a FolderItem, either of two things can go wrong. First, the path may be invalid. An invalid path contains a volume reference and/or a folder name that doesn't even exist. For example, if you use FolderItem.DriveAt and pass it 3 when the user has only one drive, the FolderItem.DriveAt function returns a :doc:`Nil` value in the FolderItem instance, f. If you try to use any of the FolderItem class's properties or methods on a :doc:`Nil` FolderItem, a NilObjectException error will occur. If the exception is not handled in some way, the app will quit. Second, the path may be valid, but the file you are trying to access may not exist. The following shell code checks for these two situations: .. code:: xojo Var f As FolderItem f = SpecialFolder.Documents.Child("Schedule") If f <> Nil Then If f.Exists Then MessageBox(f.NativePath) Else MessageBox("File does not exist!") End If Else MessageBox("Invalid path!") End If If the path is valid, the code checks the Exists property of the FolderItem to be sure that the file already exists; if the file doesn't exist or the path is invalid, a warning message is displayed. You can also handle an invalid path using an Exception Block. They are discussed in the Exception Handling section in the Debugging chapter. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/creating_new_folderitems: Creating new FolderItems ************************ You can create a FolderItem for an existing item by passing it the pathname. When you create a FolderItem with the New command, you can pass the path to the new FolderItem as an optional parameter. For example: .. code:: xojo Var f As FolderItem f = New FolderItem("myDoc.txt") specifies the name of the new FolderItem and it is located in the same folder as your application (if you 're running in the IDE) or the same folder as the built app. If you pass a FolderItem instead of a path, New will create a copy of the passed FolderItem. In this example, the FolderItem “f2” refers to a copy of the original FolderItem, not a reference to it. .. code:: xojo Var f, f2 As FolderItem f = SpecialFolder.Documents.Child("Schedule") If f <> Nil Then f2 = New FolderItem(f) End If .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/creating_files_on_web_servers: Creating files on web servers ***************************** If your web app creates files (or folders) on web servers, including Xojo Cloud, you have to ensure you set the appropriate permissions to that you can later write to (or delete) them. To do so, use the Permissions property of the FolderItem class. By default, files created on the web server get the permissions of the parent folder. This setting gives a file read/write access for all users: .. code:: xojo Var f As New FolderItem("myFile.txt") f.Permissions = 438 'Octal 666 Refer to the :ref:`FolderItem.Permissions` property for specifics on how you can set permissions. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/deleting_folderitems: Deleting FolderItems ******************** Once you have a FolderItem that represents an item that can be deleted, you can call the Delete method. The following example deletes the file represented by the FolderItem: .. code:: xojo myFile.Remove If the FolderItem is locked, an error will occur. You can check to see if the FolderItem is locked by checking the FolderItem's Locked property. Deleting a FolderItem does not move the FolderItem to the trash -- it is deleted permanently from the volume. .. code:: xojo Var f As FolderItem f = SpecialFolder.Documents.Child("Schedule") If f <> Nil Then If f.Exists Then f.Remove End If End If .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/finding_the_default_folder: Finding the default folder -------------------------- Passing an empty string (two quotes with no characters in between them) to the FolderItem.Constructor function returns a FolderItem representing the folder your app is in. You can then use the FolderItem's Item method to access all the items in the folder your app is in. The Item method returns an array of FolderItems in the folder. The array is one-based. You get a FolderItem for an item by passing the Item method the index of the item. For example, the following method gets a FolderItem for the folder and populates a ListBox with the paths to each item in the Folder. It uses an iterator to loop through the FolderItems and the Child method to get a FolderItem for each item. .. code:: xojo Var f As New FolderItem("") For Each file As FolderItem In f.Children If file <> Nil Then ListBox1.AddRow(file.NativePath) End If Next The following code returns a FolderItem that represents a file called “My Template” in a folder called “Templates” that is located in the same folder as the app: .. code:: xojo Var f As FolderItem f = New FolderItem("Templates").Child("My Template") .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/iterating_through_folder_contents: Iterating through folder contents --------------------------------- You may find that you need to iterate through a folderitem (that is a folder) in order to process all the files in it. Here is an example that deletes all the files in a folder: .. code:: xojo ' TargetFolder is a FolderItem that points to a folder. Var itemsToRemove() As FolderItem Var n As Integer = TargetFolder.Count If n > 0 Then For i As Integer = 1 To n For Each file As FolderItem In TargetFolder.Children If file.Exists And Not file.IsFolder Then itemsToRemove.AddRow(file) End If Next Next End If For i As Integer = 0 To itemsToRemove.LastIndex itemsToRemove(i).Remove Next This code saves the files that are to be deleted in an array so that they can be deleted after all the files have been processed in order to improve performance. A more complete example is available in the :ref:`FolderItem.Remove` section of the :doc:`FolderItem` page. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/prompting_the_user_for_files_and_folders: Prompting the user for files and folders ---------------------------------------- There are several methods available to allow the user to select files while using your apps. You may want to allow your user to specify a file to open, to specify the name of a file to save or you may want to let them choose a folder. These commands can only be used in desktop apps. .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/opening_files: Opening files ************* The simplest method for prompting the user to select a file to open is to use the :ref:`FolderItem.ShowOpenFileDialog` function as follows: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog("") MessageBox(f.ModificationDateTime.ToString) The FolderItem.ShowOpenFileDialog function displays the Open File selector and returns a FolderItem object that represents the file the user selected. One or more file types (that have been defined in the File Type Group Editor or with the FileType class via the language.) must be passed to the FolderItem.ShowOpenFileDialog function. It presents only those file types to the user in its browser. In this way, the user can only open files of the appropriate type. To pass more than one file type, separate them with semicolons. If the user clicks the Cancel button rather than the Open button in the Open File selector, FolderItem.ShowOpenFileDialog returns :doc:`Nil`. You will need to make sure the value returned is not :doc:`Nil` before using it. If you don't, your app will crash with a NilObjectException. The following code shows how the code from the previous example should be written to check for a :doc:`Nil` object: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog(FileTypes1.jpeg) If f <> Nil Then MessageBox(f.ModificationDateTime.ToString) End If For more precise control, you can use the :doc:`OpenFileDialog` class to create an Open File selector. The class allows you to create a customizable open-file dialog box in which you can specify the following aspects of the dialog: * Position (Left and Top properties) * Default folder (InitialFolder property) * Valid file types to show (Filter property) * Text of Validate and Cancel buttons (ActionButtonCaption and CancelButtonCaption properties) * Text that appears above the file browser (Title property) * Text that appears below the file browser (PromptText property) When you use the OpenFileDialog class, you create a new object based on this class and assign values to its properties to customize its appearance. The following example uses a custom prompt and displays only one file type: .. code:: xojo Var dlg As New OpenFileDialog dlg.InitialFolder = SpecialFolder.Documents dlg.Title = "Select a MIF file" dlg.Filter = FileTypes1.pdf Var f As FolderItem f = dlg.ShowModal If f <> Nil Then ' Proceed normally Else ' User Cancelled End If .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/saving_files: Saving files ************ The Save As selector is used to let the user choose a location in which to save a file and give the file to be saved a name. The :ref:`FolderItem.ShowSaveFileDialog` function presents the Save As dialog box. The :doc:`SaveFileDialog` class allows you to create a customized version of this dialog. Both objects return a FolderItem that represents the file the user wishes to save. This is an important distinction because the file doesn't exist yet. You must provide additional code that will create the file and write the data to the file. You will learn about creating files and writing data later in the :doc:`Accessing Text Files` and :doc:`Accessing Binary Files` topics. When you call the FolderItem.ShowSaveFileDialog function, you define the type of file and the default name for the file (that appears in the Name field in the Save As selector). The file type (which is the first parameter of the function) is the name of any file type defined for the project in the File Types dialog box. Like the other functions that return FolderItems, you should make sure the FolderItem returned by FolderItem.ShowSaveFileDialog is not :doc:`Nil` before using it (which can happen if the user clicked Cancel). This code displays a Save As selector with a default filename of “Untitled”: .. code:: xojo Var f As FolderItem f = FolderItem.ShowSaveFileDialog(FileTypes1.jpeg, "Untitled") If f <> Nil And f.Exists Then MessageBox(f.Name) End If When you use the SaveFileDialog class, you create a new object based on this class and customize the dialog by assigning values to its properties. You can customize the following aspects of the dialog: * Position (Left and Top properties) * Default directory (InitialFolder property) * Valid file types to show (Filter property) * Default filename (SuggestedFileName property) * Text of the Validate and Cancel buttons (ActionButtonCaption and CancelButtonCaption properties) * Text that appears above the file browser (Title property) * Text that appears below the file browser (PromptText property) The following code opens a customized save-file dialog box and displays the contents of the Documents directory in the browser area. .. code:: xojo Var dlg As New SaveFileDialog dlg.InitialFolder = SpecialFolder.Documents dlg.Title = "Select a pdf file" dlg.Filter = FileTypes1.Pdf Var f As FolderItem f = dlg.ShowModal If f <> Nil Then ' Proceed normally Else ' User Cancelled End If .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/selecting_folders: Selecting folders ***************** Sometimes you need to have the user select a Folder rather than a file using the Folder selector. You can do this using the :ref:`FolderItem.ShowSelectFolderDialog` function: .. code:: xojo Var f As FolderItem f = FolderItem.ShowSelectFolderDialog If f <> Nil Then MessageBox(f.Name) End If If you need more control over this selector, you can use the :doc:`SelectFolderDialog` class instead. The class has properties to modify the: * Action button caption * Cancel button caption * Initial Folder * Position on screen * Prompt text * Title * Default folder name This code displays a customized Folder selector: .. code:: xojo Var dlg As New SelectFolderDialog dlg.ActionButtonCaption = "Select" dlg.Title = "Title Property" dlg.PromptText = "Prompt Text" dlg.InitialFolder = SpecialFolder.Documents Var f As FolderItem f = dlg.ShowModal If f <> Nil Then ' Use the FolderItem here Else ' User cancelled End If .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/file_formats: File formats ------------ When saving your own files, you have to decide on the file format. You can use a text-based format such as JSON or XML or choose a binary format where you can include whatever types of data you want. If you need to save a combination of data such as both text and graphics, here are some other options: * You could use a text file and include the path to the related graphics files. This doesn't work great for transferring files around, though. * Although a text file cannot contain graphics you could still use a text format using JSON or XML and then use the :doc:`EncodeBase64` and :doc:`DecodeBase64` functions to convert binary information such as pictures to text, inserting them into JSON/XML as appropriate, but this will make the files pretty large. * You could save the file as a binary file and embed everything in the file at specific locations, but this is less common these days. * You could use a SQLite database as the file format and create one or more tables to contain what you want to save. This is pretty useful and fully cross platform. * On MacOS there is a concept called a “package” that is essentially a folder that is treated as a file. This is not cross platform but is a way to combine different types of data. More information from Apple: `Document Packages `_ .. _/topics/file_management/accessing_the_file_system_via_the_folderitem_class/see_also: .. seealso:: :doc:`FolderItem` class; :doc:`SpecialFolder` module; :doc:`FolderItem`, :ref:`FolderItem.ShowOpenFileDialog`, :ref:`FolderItem.ShowSaveFileDialog`, :ref:`FolderItem.ShowSelectFolderDialog`, :ref:`FolderItem.DriveAt`, :ref:`FolderItem.DriveCount` functions ==================== Accessing text files ==================== Text files can be read by text editors (like TextEdit, NotePad, gedit, and BBEdit) and word processors (like Microsoft Word and Pages). Text files can easily be created, read from, or written to by your apps. Text files are convenient since they can also be read by many other apps. Whether you are going to read from a text file or write to a text file, you must first have a :doc:`FolderItem` that represents the file you are going to read from or write to. .. _/topics/file_management/accessing_text_files/text_streams: Text streams ------------ Text streams are used to read or write data to text files. There are two text stream classes: :doc:`TextInputStream` and :doc:`TextOutputStream`. As you might expect, use TextInputStream to read data from text files. Use TextOutputStream to write data to text files. .. _/topics/file_management/accessing_text_files/reading_from_a_text_file: Reading from a text file ------------------------ Once you have a FolderItem that represents an existing text file you wish to open, you open the file using the Open shared method of the TextInputStream class. This method is a function that returns a “stream” that carries the text from the text file to your application. The stream is called a TextInputStream. This is a special class of object designed specifically for reading text from text files. You then use ReadAll or ReadLine methods of the TextInputStream to get the text from the text file. The TextInputStream keeps track of the last position in the file you read from. The TextInputStream.ReadAll method returns all the text from the file (via the TextInputStream) as a String. The ReadLine method returns the next line of text (the text after the last character read but before the next end of line character). As you read text, you can determine if you have reached the end of the file by checking the TextInputStream's EndOfFile property. This property will be True when the end of the file has been reached. When you are finished reading text from the file, call the TextInputStream's Close method to close the stream to the file, making the file available to be opened again. This code lets the user choose a text file using the Open-file dialog box and displays the text in a TextArea. It assumes that the valid text file types have been defined in a File Type Set called TextTypes: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog(TextTypes.All) ' All the file types in this set If f <> Nil Then Var input As TextInputStream input = TextInputStream.Open(f) TextArea1.Text = input.ReadAll input.Close End If Because ReadAll reads all of the text in the file, the resulting string will be as large as the file. Keep this in mind because reading a large file could require a significant amount of memory and could slow down the app (or cause an error in 32-bit apps with limited memory). This code reads the lines of text from a file stored in the Preferences folder in the System folder into a ListBox: .. code:: xojo Var f As FolderItem f = SpecialFolder.Preferences.Child("My Apps Prefs") If f <> Nil And f.Exists Then Var input As TextInputStream input = TextInputStream.Open(f) While Not input.EndOfFile ListBox1.AddRow(input.ReadLine) Wend input.Close End If .. _/topics/file_management/accessing_text_files/reading_from_a_text_file/dealing_with_encodings: Dealing with Encodings ********************** If you are reading and writing text files with only your app, this code will work. However, if the files are coming from other apps or platforms, in other languages or a mixture of languages, then you need to specify the encoding of the text. This is because the character codes above ACSII 127 may differ from what your app expects. When you read text, you can set the Encoding property of the TextInputStream to the encoding of the text file. Here is the above code, amended to specify the text encoding of the incoming text stream to UTF8, which is the most common text encoding. The string containing the text will use the specified encoding until it is specifically changed to something else. The code assumes that there is a File Type Set in the project named TextTypes: .. code:: xojo Var text As String Var f As FolderItem f = FolderItem.ShowOpenFileDialog(TextTypes.All) If f <> Nil Then Var input As TextInputStream input = TextInputStream.Open(f) input.Encoding = Encodings.UTF8 text = input.ReadAll input.Close End If The :doc:`Encodings` module provides access to all encodings. Use it whenever you need to specify an encoding. You can also specify the text encoding by passing the encoding as an optional parameter to Read or ReadAll. .. _/topics/file_management/accessing_text_files/writing_to_a_text_file: Writing to a text file ---------------------- Once you have a FolderItem that represents the text file you wish to open and write to, you open the file using the Append shared method of the :doc:`TextOutputStream` class. If you are creating a new text file or overwriting an existing text file, use the Create shared method of the TextOutputStream class. These methods are functions that return a "stream" that carries the text from your app to the text file. You then use the WriteLine method of the TextOutputStream class to write the text to the text file. The WriteLine method, by default, adds a carriage return to the end of each line. This is controlled by the TextOutputstream's Delimiter property which can be changed to any other character. When you are finished writing text to the file, call the TextOutputStream's Close method to close the stream to the file making the file available to be opened again. This desktop code prompts the user for a filename and then creates a file with that name and saves text from TextFields into the file: .. code:: xojo Var file As FolderItem file = FolderItem.ShowSaveFileDialog(FileTypes1.Text, "CreateExample.txt") If file <> Nil Then Var output As TextOutputStream output = TextOutputStream.Create(file) output.WriteLine(NameField.Text) output.WriteLine(AddressField.Text) output.WriteLine(PhoneField.Text) output.Close End If This desktop code prompts the user to select an existing text file and then appends the contents of three TextFields to it. It assumes that there is a file type called “Text” in the TextTypes File Type Set, which is one of the common file types that are built into the File Type Sets Editor. .. code:: xojo Var file As FolderItem file = FolderItem.ShowOpenFileDialog(TextTypes.Text) If file <> Nil Then Var output As TextOutputStream output = TextOutputStream.Open(file) output.WriteLine(NameField.Text) output.WriteLine(AddressField.Text) output.WriteLine(PhoneField.Text) output.Close End If .. _/topics/file_management/accessing_text_files/writing_to_a_text_file/dealing_with_encodings: Dealing with Encodings ********************** As is the case with reading text files, you need to specify an encoding when you write out a text file. If the app that will read the file is expecting that the text is in a specific encoding, you should convert the text to that encoding before exporting it. Before writing out a line or the entire block of text (with the Write method) use the :doc:`ConvertEncoding` function to convert the encoding of the text. Here is a revised code that converts the text (from UTF8, the Xojo default for Strings) to the MacRoman encoding: .. code:: xojo Var file As FolderItem Var output As TextOutputStream file = FolderItem.ShowSaveFileDialog(TextTypes.Text, "My Info") output = TextOutputStream.Create(file) output.Write(NameField.Text.ConvertEncoding(Encodings.MacRoman)) output.Close .. _/topics/file_management/accessing_text_files/random_access_of_text_files: Random access of text files --------------------------- Text files are usually accessed sequentially. This means that to read some text that is in the middle of the file, you usually read all of the text that comes before it. However, you can use the PositionB property to position the read position by a specific byte, which is not necessarily a character position, in the text file. This also means that to write some text to the middle of a text file, you have to write all of the text that comes before the text you wish to insert, then write the text you wish to insert, then the text that follows the text you wish to insert. You can not read text from a text file and write to the same text file at the same time. If these limitations are going to be a problem for your project, consider using a :doc:`binary file` instead. .. _/topics/file_management/accessing_text_files/see_also: .. seealso:: :doc:`FolderItem`, :doc:`TextInputStream`, :doc:`TextOutputStream` classes; :doc:`Encodings` module; :doc:`ConvertEncoding` function; :doc:`Accessing Binary Files` topic ======================================= Reading and writing data in JSON Format ======================================= JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. JSON is not as verbose as XML and is often used for Internet and web data communication because its data files are smaller. Xojo has two ways to deal with JSON. The first is to use :doc:`ParseJSON` to parse JSON text into Dictionaries and Arrays. The second is to use :doc:`GenerateJSON` to create JSON from Dictionaries and Arrays. Alternatively you can use the :doc:`JSONItem` class which you can manage data similarly to Dictionaries or Arrays. Use :doc:`ParseJSON` and :doc:`GenerateJSON` for iOS apps. Here is an example of a JSON document that describes three fictional baseball teams (Seagulls, Penguins and Crows): .. code:: json [ { "players": [ { "Name":"Bob", "position":"1B" }, { "Name":"Tom", "position":"2B" } ], "Team":"Seagulls" }, { "players": [ { "Name":"Bill", "position":"1B" }, { "Name":"Tim", "position":"2B" } ], "Team":"Pigeons" }, { "players": [ { "Name":"Betty", "position":"1B" }, { "Name":"Tina", "position":"2B" } ], "Team":"Crows" } ] This JSON example has a lot of white space to make it easier to read. Without all this white space, the JSON is much smaller: .. code:: json [{"players":[{"Name":"Bob","position":"1B"},{"Name":"Tom","position":"2B"}],"Team":"Seagulls"}, {"players":[{"Name":"Bill","position":"1B"},{"Name":"Tim","position":"2B"}],"Team":"Pigeons"}, {"players":[{"Name":"Betty","position":"1B"},{"Name":"Tina","position":"2B"}],"Team":"Crows"}] This JSON is organized as an array (indicated by the "[" that starts the JSON. There are 3 JSON objects in the array. Each JSON object consists of the key "Team" with the value being the name of the team and the key "players" with the value being another array consisting of JSON objects that contain players and their positions. .. _/topics/file_management/reading_and_writing_data_in_json_format/using_jsonitem: Using JSONItem -------------- .. _/topics/file_management/reading_and_writing_data_in_json_format/using_jsonitem/creating_json_data: Creating JSON data ****************** To create the JSON data shown above you first create JSONItem objects to hold everything: .. code:: xojo Var teams As New JSONItem Var team, player As JSONItem Now you can populate the data for each team. This is how you populate the first team: .. code:: xojo ' Add Seagulls team team = New JSONItem team.Value("Team") = "Seagulls" teams.AddRow(team) ' Add players Var seagullPlayers As New JSONItem ' Bob player = New JSONItem player.Value("Name") = "Bob" player.Value("position") = "1B" seagullPlayers.Add(player) ' Tom player = New JSONItem player.Value("Name") = "Tom" player.Value("position") = "2B" seagullPlayers.Add(player) ' Add players to the team team.Value("players") = seagullPlayers ' Do the same thing for the next two teams: ' Add Pigeons team team = New JSONItem team.Value("Team") = "Pigeons" teams.Add(team) ' Add Players Var penguinPlayers As New JSONItem ' Bill player = New JSONItem player.Value("Name") = "Bill" player.Value("position") = "1B" penguinPlayers.Add(player) ' Tim player = New JSONItem player.Value("Name") = "Tim" player.Value("position") = "2B" penguinPlayers.Add(player) ' Add players to the team team.Value("players") = penguinPlayers ' Add Crows team team = New JSONItem team.Value("Team") = "Crows" teams.Add(team) ' Add Players Var crowPlayers As New JSONItem ' Betty player = New JSONItem player.Value("Name") = "Betty" player.Value("position") = "1B" crowPlayers.Add(player) ' Tina player = New JSONItem player.Value("Name") = "Tina" player.Value("position") = "2B" crowPlayers.Add(player) ' Add players to the team team.Value("players") = crowPlayers Now all the JSON data is created. You can convert this to a string to save (using a TextOutputStream) or to display. This code displays the JSON data in a Text Area: .. code:: xojo JSONArea.Text = league.ToString .. _/topics/file_management/reading_and_writing_data_in_json_format/loading_json_data_with_the_load_method: Loading JSON data with the Load method ************************************** JSON data can also be parsed and loaded using the JSONItem class. Presuming you have the above JSON text assigned to a String constant in your project (called kBaseballJSON) you can parse it with this code: .. code:: xojo Var teams As New JSONItem teams.Load(kBaseballJSON) With the data in a JSONItem, you can now parse it for display. You loop through all the teams, displaying them and then for each team display its players. .. code:: xojo For t As Integer = 0 To teams.Count - 1 Var team As JSONItem = teams.Value(t) TextArea1.AddText(team.Value("Team") + EndOfLine) Var players As JSONItem = team.Value("players") For p As Integer = 0 To players.Count - 1 Var player As JSONItem = players.Value(p) TextArea1.AddText("--->" + player.Value("Name") + _ " (" + player.Value("position") + ")" + EndOfLine) Next Next This code loops through the JSON array, displaying each team name. For each team, it then gets the array of players and displays their names and positions. .. _/topics/file_management/reading_and_writing_data_in_json_format/using_xojo.data: Using JSON Data --------------- .. _/topics/file_management/reading_and_writing_data_in_json_format/using_xojo.data/creating_json_data: Creating JSON data ****************** To create the JSON data shown above you first create Dictionary objects to hold everything: .. code:: xojo Var teams(), team, player As Dictionary Now you can populate the data for each team. This is how you populate the first team, Seagulls: .. code:: xojo ' Add Seagulls team team = New Dictionary team.Value("Team") = "Seagulls" teams.Add(team) ' Add players Var seagullPlayers() As Dictionary ' Bob player = New Dictionary player.Value("Name") = "Bob" player.Value("position") = "1B" seagullPlayers.Add(player) ' Tom player = New Dictionary player.Value("Name") = "Tom" player.Value("position") = "2B" seagullPlayers.Add(player) ' Add players to the team team.Value("players") = seagullPlayers Do the same thing for the next two teams: .. code:: xojo ' Add Pigeons team team = New Dictionary team.Value("Team") = "Pigeons" teams.Add(team) ' Add Players Var penguinPlayers() As Dictionary ' Bill player = New Dictionary player.Value("Name") = "Bill" player.Value("position") = "1B" penguinPlayers.Add(player) ' Tim player = New Dictionary player.Value("Name") = "Tim" player.Value("position") = "2B" penguinPlayers.Add(player) ' Add players to the team team.Value("players") = penguinPlayers ' Add Crows team team = New Dictionary team.Value("Team") = "Crows" teams.Add(team) ' Add Players Var crowPlayers() As Dictionary ' Betty player = New Dictionary player.Value("Name") = "Betty" player.Value("position") = "1B" crowPlayers.Add(player) ' Tina player = New Dictionary player.Value("Name") = "Tina" player.Value("position") = "2B" crowPlayers.Add(player) ' Add players to the team team.Value("players") = crowPlayers Now all the JSON data is created. You can convert this to Text to save (using a TextOutputStream) or to display by using the GenerateJSON method. This code displays the JSON data in a Text Area: .. code:: xojo TextArea1.Text = GenerateJSON(teams) .. _/topics/file_management/reading_and_writing_data_in_json_format/loading_json_data_with_the_parsejson_method: Loading JSON data with the ParseJSON method ******************************************* To load JSON data you use the ParseJSON method. Presuming you have the above JSON text assigned to a String constant in your project (called kBaseballJSON) you can parse it with this code: .. code:: xojo Var teams() As Variant = ParseJSON(kBaseballJSON) With the data now loaded you can loop through it for display. .. code:: xojo For Each team As Dictionary In teams TextArea1.AddText(team.Value("Team") + EndOfLine) Var players() As Variant = team.Value("players") For Each player As Dictionary In players TextArea1.AddText("--->" + player.Value("Name") + _ " (" + player.Value("position") + ")" + EndOfLine) Next Next This code loops through the JSON array, displaying each team name. For each team, it then gets the array of players and displays their names and positions. .. _/topics/file_management/reading_and_writing_data_in_json_format/extending_the_file_format: Extending the file format ------------------------- Much like XML, you can extend your JSON format fairly easily. For example, if you wanted to update the format to add a “coach” for each team, you can easily do so when the file is saved and you can modify any loading code to only process the “coach” if it exists. In the saving code, add a new value after each team JSONItem is created, such as this: .. code:: xojo team.Value("Coach") = "Coach Mark" With this change, create new JSON data and display it using the existing loading code. The JSON displays properly, but the new coach value is ignored. You have just extended your JSON data format without breaking its ability to be loaded. Of course, you 'll want to update the loading code so that it can display the coach. To do that, in the loading code above add this code after the team name is displayed to check if there is a Coach value in the JSON and display it if it is there: .. code:: xojo If team.HasName("Coach") Then TextArea1.AddText(team.Value("Coach") + EndOfLine) End If .. _/topics/file_management/reading_and_writing_data_in_json_format/sample_projects: Sample projects --------------- * Examples/Communication/Internet/JSON Example * Examples/Text/FormatJSON * Examples/Text/JSONTree .. _/topics/file_management/reading_and_writing_data_in_json_format/see_also: .. seealso:: :doc:`JSONItem`, :doc:`Dictionary` classes; :doc:`GenerateJSON`, :doc:`ParseJSON` methods ====================================== Reading and writing data in XML format ====================================== XML (Extensible Markup Language) is a human-readable common text format that can be used for many purposes. Along with JSON, it can be used as a file format for your apps and is also often used to return data from web services. You can create, open, modify and manage XML using :doc:`XMLDocument` and related classes. The XML document format uses tags similar to what you may see in HTML. These tags create XML nodes that contain your data. The tags are case-sensitive. Here is example XML that describes three teams in a fictional baseball league: .. code:: xml A tag is anything that is between the brackets, such as . Within a tag you may have attributes, which is what name is within the Team tag. So in this snippet of XML: .. code:: xml Team is a tag and name is an attribute. The XML structure is very specific. Every tag must have a closing tag, which is the tag name prefixed with a slash. The closing tag for is . It is possible to have a single line tag that has its closing tag embedded in it. You can see this here: .. code:: xml Because this tag ends in “/>” it is considered to close itself. Tag names can be anything you want. Because this is your file format, you define the tags and other specifics of its format. There are many classes available to help you read, process and create XML files, including: :doc:`XMLDocument`, :doc:`XMLNode`, :doc:`XMLElement` and :doc:`XMLNodeList`. XMLDocument is the primary class you use to work with XML documents. XMLDocument is used to create new XML documents, modify XML documents and load XML documents. To create XML documents, you create a new instance of XMLDocument and then add the Nodes for the tags you have defined. .. _/topics/file_management/reading_and_writing_data_in_xml_format/creating_an_xml_document: Creating an XML document ------------------------ To create the XML shown at the beginning of this topic, you first create your XMLDocument instance: .. code:: xojo Var xml As New XMLDocument Now you can add the topmost node (called the root) to the XML document: .. code:: xojo Var root As XMLNode root = xml.AppendChild(xml.CreateElement("League")) And now you can add the first team to the root: .. code:: xojo Var team As XMLNode team = root.AppendChild(xml.CreateElement("Team")) The team has an attribute containing its name: .. code:: xojo team.SetAttribute("name", "Seagulls") Now you can add the players to the team: .. code:: xojo Var player As XMLNode player = team.AppendChild(xml.CreateElement("Player")) player.SetAttribute("name", "Bob") player.SetAttribute("position", "1B") player = team.AppendChild(xml.CreateElement("Player")) player.SetAttribute("name", "Tom") player.SetAttribute("position", "2B") Now you are done with the first team and its players. Repeat the code to do the next two teams: .. code:: xojo team = root.AppendChild(xml.CreateElement("Team")) team.SetAttribute("name", "Pigeons") player = team.AppendChild(xml.CreateElement("Player")) player.SetAttribute("name", "Bill") player.SetAttribute("position", "1B") player = team.AppendChild(xml.CreateElement("Player")) player.SetAttribute("name", "Tim") player.SetAttribute("position", "2B") team = root.AppendChild(xml.CreateElement("Team")) team.SetAttribute("name", "Crows") player = team.AppendChild(xml.CreateElement("Player")) player.SetAttribute("name", "Ben") player.SetAttribute("position", "1B") player = team.AppendChild(xml.CreateElement("Player")) player.SetAttribute("name", "Ty") player.SetAttribute("position", "2B") Lastly, add the code to save the file: .. code:: xojo Var f As FolderItem f = FolderItem.ShowSaveFileDialog("", "league.xml") If f <> Nil Then xml.SaveXml(f) End If This prompts you to save the file (with a default name of league.xml). After the file is saved, you can open it using a text editor or a web browser (Firefox displays XML nicely). .. _/topics/file_management/reading_and_writing_data_in_xml_format/loading_an_xml_document: Loading an XML document ----------------------- Speaking of loading an XML file, here is how you would load the contents of this XML file into a Text Area. First, you prompt to select the XML file: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog("") If f = Nil Then Return Now you have a valid file, so try to open it as an XML file: .. code:: xojo Var xml As New XMLDocument Try xml.LoadXML(f) Catch e As XMLException MessageBox("XML Error.") Return End Try If the selected file is not an XML file, this raises an XMLException. The exception is caught and an error message is displayed. Now you have a valid XML file which you can process using the methods and properties of the XMLDocument and XMLNode classes. This code has three parts to it. Part one verifies that the XML file has a root node called “League”. Part two is a loop that processes each team. And part three is an inner loop that processes each player on each team. The XML data is output to a Text Area. .. code:: xojo Var root As XmlNode root = xml.FirstChild If root.Name = "League" Then Var team, player As XmlNode team = root.FirstChild While team <> Nil XMLArea.AddText(team.GetAttribute("name") + EndOfLine) player = team.FirstChild While player <> Nil XMLArea.AddText("--->" + player.GetAttribute("name") + " " + player.GetAttribute("position") + EndOfLine) player = player.NextSibling Wend team = team.NextSibling Wend Else MessageBox("Not a League XML file.") End If .. _/topics/file_management/reading_and_writing_data_in_xml_format/processing_large_xml_files: Processing large XML files -------------------------- When you use XMLDocument to load XML files, the entire XML gets loaded into memory at once. This can become a problem for extremely large XML files. In these situations, you can use the XMLReader class to process the XML file in smaller pieces. XMLReader has a large set of event handlers that are called as parts of the XML file are loaded. You can use these event handlers to look at the data and process it yourself, perhaps only saving the parts you need. Refer to the Reference Guide for information about the various event handlers available with XMLReader. .. _/topics/file_management/reading_and_writing_data_in_xml_format/extending_an_xml_file_format: Extending an XML file format ---------------------------- XML is a great file format that you should consider using instead of plain text files with a custom format. A major advantage of using an XML file over a plain text file is that XML makes it much easier to update your file format. For example, if you wanted to update the format to add a “coach” attribute to the Team tag, you can easily do so when the file is saved and you can modify any loading code to only process this “coach” attribute if it exists. In the saving code, add a new attribute after each Team node is created, such as this: .. code:: xojo team.SetAttribute("coach", "Coach Mark") With this change, create a new XML file and load it using the existing loading code. The XML file loads properly, but the new coach attribute is ignored. You have just extended your XML file format without breaking its ability to be loaded. Of course, you 'll want to update the loading code so that it can display the coach. To do that, simply add another line to get the coach attribute after getting the team name attribute: .. code:: xojo XMLArea.AddText(team.GetAttribute("name") + EndOfLine) XMLArea.AddText(team.GetAttribute("coach") + EndOfLine) .. _/topics/file_management/reading_and_writing_data_in_xml_format/searching_the_xml: Searching the XML ----------------- To find things in the XML, you have these options: * Directly navigate to the tags to get the node you want. * Iterate through the tags and their values until you find what you want. * Search using XQL to find what you want. Using the Team XML shown at the beginning of this topic, suppose you want to get the first player on the Crows. You can directly access that by relying on the structure of the XML like this: .. code:: xojo Var teamXML As New XMLDocument teamXML.LoadXml(kTeamXML) ' kTeamXML is the Team XML from the top of this topic Var crowTeam As XMLNode ' First child is "League" and we want its 3rd child to get the Crows team crowTeam = teamXML.FirstChild.Child(2) ' The first child of the Crow team is the player "Ben" Var player As XMLNode = crowTeam.FirstChild Var playerName As String = player.GetAttribute("name") ' = "Ben" If you wanted to then change that player's position to shortstop, you can do so like this: .. code:: xojo player.SetAttribute("position", "SS") Var newXML As String = teamXML.ToString ' view the new XML Or suppose you want to get all the plates on the Crows. In this case you can directly go to the Crows node, but then iterate through all its children like this: .. code:: xojo Var teamXML As New XMLDocument teamXML.LoadXml(kTeamXML) Var crowTeam As XMLNode ' First child is "League" and we want its 3rd child to get the Crows team crowTeam = teamXML.FirstChild.Child(2) ' Now iterate through all the children of the Crows team to get all its players Var playerNames() As String For i As Integer = 0 To crowTeam.ChildCount - 1 playerNames.Add(crowTeam.Child(i).GetAttribute("name")) Next ' playerNames = "Ben", "Ty" Lastly, what if you want to find all the first basemen? There are first basemen on each team so you could iterate through everything to find what you want. Or you can use XQL (XML Query Language) to do this by searching for all nodes with "!B" as the value for the position attribute. XQL has its own syntax which looks like this to find all Players with a position attribute = "1B": .. code:: xojo //Player[@position=""1B""] You can use this with the XMLDocument.XQL method to get back a list of nodes that match the query: .. code:: xojo Var teamXML As New XMLDocument teamXML.LoadXml(kTeamXML) ' Use XQL (XML Query Language) to find all players with "1B" as the position attribute Var firstBasemen As XmlNodeList firstBasemen = teamXML.Xql("//Player[@position=""1B""]") Var firstBasemenNames() As String For i As Integer = 0 To firstBasemen.Length - 1 firstBasemenNames.Add(firstBasemen.Item(i).GetAttribute("name")) Next ' firstBasemenNames = "Bob", "Bill", "Ben" .. _/topics/file_management/reading_and_writing_data_in_xml_format/see_also: .. seealso:: :doc:`XMLDocument`, :doc:`XMLNode` classes; ======================== Understanding file types ======================== Your desktop apps may need to prompt users to select or save files. You use File Types to identify the types of files that can be selected or the types of files that are saved, which determines how the OS and other apps can use them. File Types are created in File Type Groups using the :doc:`File Type Group Editor`. The File Type Group Editor lets you create common file types (file such as text, PNG, JPG, etc.) or custom file types (which are owned by your app). .. _/topics/file_management/understanding_file_types/using_common_file_types: Using common file types ----------------------- A common file type is a standard file type that your app uses, but does not own. This may be file types such as png or standard text files. Your app may be able open and even create these type of files, but it is not the owner of the file. To add a common file type to a File Type Group, choose the Add Common File Type button on the command bar for the File Type Group and choose the one you want. This populates a file type with the necessary information which you should not need to change. .. image:: https://documentation.xojo.com/topics/file_management/images/understanding_file_types_file_type_set_file_type_png.png After adding the PNG common file type you can now you can refer to the file type to prompt the user to open a PNG file. This code prompts the user for a file and then assigns it to a Picture property that is displayed in a Canvas: .. code:: xojo Var pngFile As FolderItem pngFile = FolderItem.ShowOpenFileDialog(ImageFileTypeSet.Png) If pngFile <> Nil Then PNGImage = Picture.Open(pngFile) ImageCanvas.Refresh End If You can also create your own PNG files. This code saves a PNG file containing a red circle: .. code:: xojo Var redCircle As New Picture(100, 100) redCircle.Graphics.DrawingColor = &cff0000 redCircle.Graphics.FillOval(0, 0, 100, 100) Var savePNG As FolderItem savePNG = FolderItem.ShowSaveFileDialog(ImageFileTypeSet.Png, "RedCircle.png") If savePNG <> Nil Then redCircle.Save(savePNG, Picture.SaveAsPNG) End If Because this is definite as a PNG, the default system icon for PNG is used and displayed when viewing the PNG in the system file viewer. .. _/topics/file_management/understanding_file_types/using_custom_file_types: Using custom file types ----------------------- A custom file type is a file type where you specify all its properties. For standard file types, you should use a common file type rather than creating your own custom file type. Often you will create a custom file type for file types that your app owns. These are files that your app creates and ones that your app can also open and edit. Once you have specified a custom File Type, files saved by your app for that type will display the custom icon you specified. Your app will also be launched when the user opens the file (usually be double-clicking it), but the user can change that behavior in the OS itself. This sample File Type (in a File Type Group called XojoTextFileTypeGroup) is used in the code below: .. image:: https://documentation.xojo.com/topics/file_management/images/understanding_file_types_file_type_xojotext.png The Identifier, Conforms To, Mime Types and macOS Types fields are only used by macOS. .. _/topics/file_management/understanding_file_types/saving_files: Saving files ************ Now that you have the file type specified properly, you can use it to save files of that type. When you prompt the user for the file to save, include the file type from above. This code is in the Pressed event handler of a Save button on a Window: .. code:: xojo Var f As FolderItem Var output As TextOutputStream f = FolderItem.ShowSaveFileDialog(XojoTextFileTypeGroup.XojoText, "SampleFile.xojotext") If f <> Nil then output = TextOutputStream.Create(f) output.Write(EditArea.Text) output.Close End if You can also use the SaveAsDialog class for more control of the user file dialog. On Mac, the saved file will use the icon from the file type, although it's possible the icon does not appear immediately for the associated file. The Finder does not always have time to inspect the app for file types when you are running directly from the IDE. You can always try leaving the debug app running for longer or try building your app and then navigating to its folder. Refer to Troubleshooting for more information. .. youtube:: GBZF7q3Kr5Y On Windows, your app installer needs to create Registry entries to set an associated file icon and allow opening of documents to automatically launch the app. For more information on this refer to the sample Inno Setup Scripts for 32-bit and 64-bit apps. .. _/topics/file_management/understanding_file_types/opening_files: Opening files ************* When you prompt the user for a file to open, you can also include the file type so that the user can only choose your app's files. This method loads the text from the file and displays it in a Text Field: .. code:: xojo Sub LoadFile(f As FolderItem = Nil) Var file As FolderItem = f Var input As TextInputStream If file Is Nil Then ' Prompt user for file to open file = FolderItem.ShowOpenFileDialog(XojoTextFileTypeGroup.XojoText) End If If file <> Nil Then input = TextInputStream.Open(file) EditArea.Text = input.ReadAll input.Close End If End Sub It is called from the Pressed event handler of an Open button on the Window: .. code:: xojo LoadFile You can also use the OpenDialog class for more control of the user file dialog. If you need to specify multiple file types, you can just concatenate them like this: .. code:: xojo MyGroup.Type1 + MyGroup.Type2 Lastly, when the user double-clicks on one of your app's files :subscript:`1` , your app will be launched and provided a FolderItem to the file. You'll get this information in the App.OpenDocument event handler, where you can process the file. You will need to have a built app for the OS to launch in order to test this functionality. This code creates a new window and loads the text file into it by calling the method shown above: .. code:: xojo Var win As New DocWindow win.LoadFile(item) win.Show The OpenDocument method is also called when the file is dragged onto the app icon (which launches the app) or onto the Dock icon (on macOS). :subscript:`1` On Windows, your app installer needs to create Registry entries to set an associated file icon and allow opening of documents to automatically launch the app. For more information on this refer to :doc:`Creating an Installer with the Inno Setup Script (64-bit apps)` and :doc:`Creating an Installer with the Inno Setup Script (32-bit apps)`. Also watch the `Windows Installers `_ video. .. _/topics/file_management/understanding_file_types/referencing_file_types: Referencing file types ---------------------- In addition to the direct ways to refer to a specific file type as shown in the examples above, there are also ways you can also refer to multiple file types. Suppose you create a File Type Set called ImageTypes in which you specify all of the valid image types that your application can open. You can specify the entire list of image types with a line such as: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog(ImageTypes.All) If you need to add or remove image file types, your can simply modify the File Type Group and this line of code will automatically refer to the new members of the group. If you have just a single file type, you can refer to its name directly: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog(ImageTypes.PNG) If your File Type Group has multiple file types, you can concatenate them: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog(ImageTypes.JPEG + ImageTypes.PNG) .. _/topics/file_management/understanding_file_types/troubleshooting: Troubleshooting --------------- On macOS, Apple provides some tips to help troubleshoot File Type (UTI) settings: * `Troubleshooting Spotlight Importers `_ In particular, some useful commands are: .. csv-table:: :header: "Command", "Description" :widths: auto "mdimport -d1 ","Tells you what UTI it thinks this file conforms to." "mdimport -d2 ","Tells you what meta data is imported for this file. Look for kMDItemContentTypeTree to see the hierarchy it thinks this file conforms to." For anyone with many versions of Xojo installed there will be lots of entries as some will have been imported by the old Spotlight Importer and other things. To test, it is often easier to create a new user account that has never had Xojo installed before using these commands. .. _/topics/file_management/understanding_file_types/launch_services: Launch services *************** Per Apple, apps need to be registered in order for them to be known to macOS so that their file type settings work. This is normally taken care of automatically in these situations: * A built-in background tool, run whenever the system is booted or a new user logs in, automatically searches the Applications folders in the system, network, local, and user domains and registers any new applications it finds there. * The Finder automatically registers all apps as it becomes aware of them, such as when they are dragged onto the user's disk or when the user navigates to a folder containing them. * When the user attempts to open a document for which no preferred app can be found in the Launch Services database, the Finder presents a dialog asking the user to select an app with which to open the document. It then registers that app before launching it. For more information refer to the official `Apple Launch Services Concepts `_ document. .. _/topics/file_management/understanding_file_types/linux_notes: Linux notes ----------- Create the MIME type: .. code:: xojo sudo -H gedit /etc/mime.types and add the line application/x-xojotext xojotext Add the icon (which must be called application-x-xojotext.svg): .. code:: xojo sudo cp PathToIcon/application-x-xojotext.svg /usr/share/icons/gnome/scalable/mimetypes (don't forget to replace PathToIcon with the actual path to your icon) * `Adding Mime Types `_ .. _/topics/file_management/understanding_file_types/see_also: .. seealso:: :doc:`FileType` class; :doc:`File Type Group Editor`, :doc:`Creating an Installer with the Inno Setup Script (64-bit apps)`, :doc:`Creating an Installer with the Inno Setup Script (32-bit apps)` topics ====================================== Understanding Uniform Type Identifiers ====================================== Uniform Type Identifiers (UTIs) are used by the system to identify the type of files or in-memory data. For a file, the UTI is determined by the system with the help of its extension and its creator or its MIME type. As an example, Mac and iOS applications use UTIs to declare the format for data they place on a pasteboard. Mac apps use UTIs to declare the types of files that they are able to open. Related Topics: * File Type Group Editor * Using File Type Groups * Webinar: All About File Types .. _/topics/file_management/understanding_uniform_type_identifiers/usage_in_xojo: Usage in Xojo ------------- In Xojo, you specify UTIs for files using File Type Groups. The can then be used to select specific types of file or to control icons used with files owned by your apps. The Clipboard class uses UTIs to identify its contents. The SpotlightQuery class uses UTIs to search for specify files. .. _/topics/file_management/understanding_uniform_type_identifiers/inheritance: Inheritance ----------- Uniform Type Identifiers support multiple inheritance, i.e. a UTI can be a child of any number of other UTIs. It is said to conform to another UTI. For example, the UTI for an AVI movie file is public.avi and it conforms to: * public.movie: the base type for any movie * public.audiovisual-content * public.data: the base type for physical files or data * public.item: the base type for the physical hierarchy * public.content: the base type for all document content .. _/topics/file_management/understanding_uniform_type_identifiers/uti_system_tips: UTI System tips --------------- .. _/topics/file_management/understanding_uniform_type_identifiers/getting_all_the_utis_declared_on_a_system: Getting all the UTIs declared on a system ***************************************** In the Terminal application (in Applications→Utilities→Terminal), type the following command (case-sensitive) to display a list of all UTIs declared by the system and other applications: .. code:: xojo mdimport -A .. _/topics/file_management/understanding_uniform_type_identifiers/getting_uti_and_other_metadata_of_a_given_file: Getting UTI and other metadata of a given file ********************************************** In the Terminal application (in Applications→Utilities→Terminal), type the following command (case-sensitive): .. code:: xojo mdls (followed by a space) then drag & drop the file you want information on onto the Terminal window and press the return key. Among other metadata used by Spotlight, you should see the following for an AVI file: .. code:: xojo kMDItemContentType = "public.avi" kMDItemContentTypeTree = ( "public.avi", "public.movie", "public.audiovisual-content", "public.data", "public.item", "public.content" ) * kMDItemContentType contains the file's UTI * kMDItemContentTypeTree is the list of all the UTIs the file conforms to. .. _/topics/file_management/understanding_uniform_type_identifiers/to_learn_more: To learn more ------------- * `Uniform Type Identifier in the Apple iOS Developer Library `_ * `Uniform Type Identifier on Wikipedia `_ * `Uniform Type Identifier in the Apple Mac Developer Library `_ .. _/topics/file_management/understanding_uniform_type_identifiers/see_also: .. seealso:: :doc:`File Type Group Editor`, :doc:`Understanding File Types` topics Graphics ======== .. toctree:: :maxdepth: 1 :name: sec-graphics Getting started with graphics Working with color Drawing with vector graphics HiDPI support HiDPI support for web apps Supporting Dark Mode and more with Color Groups Updating code that used the Graphics property ============================= Getting started with graphics ============================= Apps can contain existing pictures or you can draw your own pictures. In some cases, you can add the pictures you want without writing any code. When you do need to write code, a variety of classes are available to you. .. _/topics/graphics/getting_started_with_graphics/adding_pictures_and_images_to_projects: Adding pictures and images to projects -------------------------------------- The easiest way to add pictures to your project is to drag them onto the Navigator. This creates an :doc:`Image Set` and puts the picture in the 1x size. Use the 2x and 3x sizes to add the same picture at larger sizes for use by HiDPI screen resolutions. This is optional but does result in better-quality pictures for people using HiDPI screens because otherwise they will see the 1x picture scaled up to fit the HiDPI screen and this can result in pixelization or fuzziness of the image. With an picture that is dragged into your project in this way, you can simply refer to it by name in your code. Refer to Displaying Pictures below for some examples. Also refer to the HiDPI topics for additional information about supporting HiDPI pictures in your apps: * :doc:`Desktop HiDPI` * :doc:`Web HiDPI` .. youtube:: auWGQWHx2PI .. _/topics/graphics/getting_started_with_graphics/understanding_drawing_coordinates: Understanding drawing coordinates --------------------------------- To draw a picture in your application, you use the Canvas (for desktop projects), the WebCanvas (for web projects), or the iOSCanvas (for iOS projects) controls. Since all drawing is done using coordinates to specify the location of things, it is important to understand how they work. Most of the graphics methods require you to indicate the location inside the control where you wish to begin drawing. This location is specified using the coordinates system. This system is a grid of invisible horizontal and vertical lines that are 1 pixel apart. If you have never done a computer drawing with a coordinates system, you might expect the origin (0,0) to be in the center of the window, but it's not. The origin is always in the upper-left corner of the area. For the entire screen, this is the upper-left corner of the screen. For multiple screens, this is the upper-leftmost corner of the leftmost screen. For a window or web page, the origin is the upper-left corner of the window or web page, and for a control, it's the upper-left corner of the control. The X axis is the horizontal axis. It increases in value moving from left to right. The Y axis is the vertical axis and it increases in value moving from top to bottom. So, a point that is at 10, 20 (within a window, for example) is 10 pixels from the left side of the window and 20 pixels from the top of the window. If you are working within a Canvas control, the point 10, 20 is 10 pixels from the left edge of the Canvas control and 20 pixels down from the top edge of the control. .. _/topics/graphics/getting_started_with_graphics/displaying_pictures: Displaying pictures ------------------- Windows, Web Pages and iOS Views have several ways to display pictures. .. _/topics/graphics/getting_started_with_graphics/using_the_entire_window: Using the entire window *********************** In desktop projects, you can use either the Backdrop property or the Paint event to draw a picture directly to the Window background. The simplest way to do this is to add a picture to your project and then assign it to the Backdrop property in the Inspector. You can also assign a background picture in code: .. code:: xojo Self.Backdrop = PictureName This is how you might prompt the user for a picture file to display: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog("") If f <> Nil Then Self.Backdrop = Picture.Open(f) End If You should only consider using the backdrop for pictures that do not change. If you find you need to change the picture being displayed, then you should use one of the next techniques. .. _/topics/graphics/getting_started_with_graphics/using_a_portion_of_the_window,_web_page_or_view: Using a portion of the window, web page or view *********************************************** If you want to display a picture in a portion of the user area (window, web page or view), you can do so using the appropriate platform-specific control, either :doc:`DesktopImageViewer`, :doc:`WebImageViewer` or :doc:`MobileImageViewer`. These controls are simple and only support displaying the picture. They do not support any drawing and size changes are limited. ImageViewer has an Image property to which you assign a Picture to display it: .. code:: xojo LogoImageViewer.Image = PictureName Of course, you can also prompt the user for a picture and assign it as shown above. WebImageViewer has a Picture property and a URL property that can be used to display a picture. The URL property displays the picture at the specified URL: .. code:: xojo LogoImageViewer.URL = "http://www.website.com/picture.jpg" You can assign a picture as well: .. code:: xojo LogoImageViewer.Picture = PictureName In order to improve performance of you web apps, you should cache your pictures locally in the browser. To do this, assign the picture to a property of the web page (or one of its controls) before you use it in your ImageViewer. This causes the picture to be sent to the browser only once (when the page with the property is loaded). If you then use it again, it will already be cached in the browser and available for immediate use. For MobileImageViewer, you also assign the Image property: .. code:: xojo LogoImageViewer.Image = MyCompanyLogo MobileImageViewer also has a DisplayMode property that gives you some control over how the picture is scaled and positioned within the control. This code in the Opening event of an MobileImageViewer scales the image so it fits within the controlling while maintaining its aspect ratio: .. code:: xojo Me.DisplayMode = MobileImageViewer.DisplayModes.ScaleAspectFit .. _/topics/graphics/getting_started_with_graphics/creating_pictures: Creating pictures ----------------- In addition to adding pre-existing pictures to your projects, you can also create pictures dynamically. To do so, you need to have an instance of a :doc:`Graphics` or :doc:`WebGraphics` class. But you cannot instantiate these classes yourself. One way to get an instance of Graphics is to create an instance of a Picture: .. code:: xojo Var p As New Picture(100, 100, 32) This creates a picture of the size 100x100 pixels. Now that you have a new picture object, you can use its Graphics to draw. To draw an existing picture that has been added to the project: .. code:: xojo Var p As New Picture(100, 100) p.Graphics.DrawPicture(PictureName, 0, 0) You can then assign your picture object to anything that accepts a picture, such as: .. code:: xojo LogoImageViewer.Image = p ' Desktop LogoImageViewer.Picture = p ' Web You can assign this to an MobileImageViewer like this: .. code:: xojo MyImageViewer.Image = pic.Image .. _/topics/graphics/getting_started_with_graphics/graphics_versus_webgraphics: `Graphics` versus WebGraphics ------------------------------ All drawing done in code uses the Graphics and WebGraphics classes. Graphics can be used in all types of projects, including desktop, web and mobile projects. As mentioned above, you can always access Graphics by creating a new Picture object. Additionally, in desktop projects any control with a Paint event provides an instance of a Graphics object (as a parameter called g) that you can use for drawing. This is most commonly done using the :doc:`DesktopCanvas` control. In web applications, the :doc:`WebCanvas` control provides you with a WebGraphics instance in its Paint event that you can use for drawing. The code sections below show various ways you can manipulate pictures in platform Canvas controls. .. _/topics/graphics/getting_started_with_graphics/scaling_pictures: Scaling pictures **************** Often the picture you have is not the right size for displaying. Using the DrawPicture method on `Graphics` and WebGraphics, you can scale your picture to any size. This code, in a Paint event, scales a picture from its original size down to whatever size the Canvas is: .. code:: xojo g.DrawPicture(PictureName, 0, 0, g.Width, g.Height, 0, 0, PictureName.Width, PictureName.Height) .. _/topics/graphics/getting_started_with_graphics/copying_a_portion_of_a_picture: Copying a portion of a picture ****************************** Sometimes you may need to extract (or crop) just a portion of a picture. You can do that using the DrawPicture method by specifying the coordinates for the portion of the picture you want: .. code:: xojo g.DrawPicture(PictureName, 0, 0, 30, 30, 10, 10, 40, 40) .. _/topics/graphics/getting_started_with_graphics/scrolling_a_picture: Scrolling a picture ******************* Scrolling pictures are only supported with the Desktop Canvas class. A picture that is drawn into a Canvas with the DrawPicture method can be scrolled by calling the Canvas Scroll method. It takes three parameters: the picture to be scrolled, and the amounts to be scrolled in the horizontal and vertical directions. To use the Scroll method to scroll the picture in a Canvas control, you need to store the last scroll value for the axis you are scrolling so you can use this to calculate the amount to scroll. This can be done by adding properties to the window that contains the Canvas control or by creating a new class based on the Canvas control that contains properties to hold the last X scroll amount and last Y scroll amount. The code below shows you how you can scroll a picture. The picture has been added to the project. The properties XScroll and YScroll have been added to the window to hold the amount of points the picture has been scrolled. A convenient way to scroll a picture is with the four arrow keys. To do this, you place code in the KeyDown event handler of the active window. This event receives each keystroke. Your code can test whether any of the arrow keys have been pressed and then take the appropriate action. For example, this code in the KeyDown event of the window scrolls the picture 8 pixels at a time: .. code:: xojo Function KeyDown (Key As String) As Boolean Select Case Key.Asc Case 31 ' up arrow Yscroll = YScroll - 8 Canvas1.Scroll(0, -8) Case 29 ' Right arrow Xscroll = XScroll - 8 Canvas1.Scroll(-8, 0) Case 30 ' Down arrow Yscroll = Yscroll + 8 Canvas1.Scroll(0, 8) Case 28 ' Left arrow Xscroll = Xscroll + 8 Canvas1.Scroll(8, 0) End Select End Function The Paint event of the Canvas has the line of code that draws the picture: .. code:: xojo g.DrawPicture(PictureName, XScroll, YScroll) .. _/topics/graphics/getting_started_with_graphics/drawing_lines: Drawing lines ************* Lines are drawn using the DrawLine method. The color of the line is the color stored in the :ref:`DrawingColor` property. To use the DrawLine method, you pass it starting coordinates and ending coordinates of the line. This code uses the DrawLine method to draw a grid inside a DesktopCanvas control or window background. The size of each box in the grid is defined by the value of the kBoxSize constant: .. code:: xojo Var i As Integer Const kBoxSize = 10 For i = 0 To Me.Width Step kBoxSize g.DrawLine(i, 0, i, Me.Height) Next For i = 0 To Me.Height Step kBoxSize g.DrawLine(0, i, Me.Width, i) Next .. _/topics/graphics/getting_started_with_graphics/drawing_rectangles: Drawing rectangles ****************** Rectangles are drawn using the DrawRectangle, FillRectangle, DrawRoundRectangle, and FillRoundRectangle methods. You supply the X and Y coordinates for the upper-left corner of the rectangle as well as the width and height of the rectangle. Use the :ref:`DrawingColor` property to specify the color of the oval. The Draw versions draw a rectangle with just a border. The Fill versions fill the rectangle with the specified :ref:`DrawingColor`. RoundRectangles are rectangles with rounded corners. Therefore, DrawRoundRectangle and FillRoundRectangle require two additional parameters: the width and height of the curve of the corners. This code draws a rectangle and fills it with the color red: .. code:: xojo g.DrawRectangle(0, 0, 150, 100) g.DrawingColor = &cFF0000 ' Red g.FillRectangle(0, 0, Me.Width, Me.Height) .. _/topics/graphics/getting_started_with_graphics/drawing_ovals: Drawing ovals ************* Ovals are drawn with the DrawOval and FillOval methods. You supply the X and Y coordinates for the top-left corner of the oval and the width and height of the oval. Use the :ref:`DrawingColor` property to specify the color of the oval. DrawOval draws only the border of the oval (use PenSize to specify the width of the border). FillOval fills the oval with the :ref:`DrawingColor`. This code draws an oval: .. code:: xojo g.DrawOval(0, 0, 50, 75) .. _/topics/graphics/getting_started_with_graphics/drawing_polygons: Drawing polygons **************** Polygons are drawn using the DrawPath and FillPath methods. You create a GraphicsPath object and use it to move the drawing point from place to place either to draw a line from the previous location or move it without drawing a line. This code in the Paint event draws a triangle: .. code:: xojo Var p As New GraphicsPath p.MoveToPoint(0, 0) ' Start location p.LineToPoint(20, 44) p.LineToPoint(40, 0) p.LineToPoint(0, 0) g.LineColor = Color.Blue g.DrawPath(p) .. _/topics/graphics/getting_started_with_graphics/drawing_text: Drawing text ************ The DrawText method is used to draw text. You supply the X and Y coordinates for the bottom left of the text and optional parameters to specify whether the text should wrap or be condensed if it cannot fit in the specified area. This code draws text: .. code:: xojo g.DrawingColor = &cff0000 g.FontName = "Helvetica" g.FontSize = 16 g.DrawText("Hello world", 10, 130) .. _/topics/graphics/getting_started_with_graphics/drawing_into_regions: Drawing into regions ******************** When you are drawing a complex image that involves many calls to `Graphics` methods, you may want to create non-overlapping regions within the area. You then draw into each “child” area, with the assurance that each drawing will not inadvertently overlap another object and perhaps cause unwanted flicker. You create a child region within the parent area with the Clip method. You pass it the top-left corner of the child region and its width and height. It returns a new Graphics object that is the specified region inside the parent area. You can then draw into the child area just as with any other `Graphics`` object. The only difference is that the drawing will be confined to the child area. The coordinates of each call are with respect to the top-left corner of the child area. Here is an example of how this works. Two regions at the top of the Canvas are defined by calls to the Clip method. Subsequent calls to the DrawRect method show where the clippings are. Calls to the DrawOval method draw shapes within the clipped areas. Notice that the first call attempts to draw outside the area. If you were drawing from the parent Graphics object, the first oval would bump into the second, but because the drawing is clipped, there is no overlap. .. code:: xojo Var clip1 As Graphics = g.Clip(0, 0, 150, 15) Var clip2 As Graphics = g.Clip(150, 0, 150, 15) ' Draw the border of the Canvas in black g.DrawingColor = &c000000 g.DrawRectangle(0, 0, g.Width, g.Height) ' Draw into the first area in red clip1.DrawingColor = &cff0000 clip1.DrawRectangle(0, 0, clip1.Width, clip1.Height) ' Try to draw outside its clip clip1.DrawOval(0, 0, 200, 15) ' Draw into the second area in blue clip2.DrawingColor = &c0000ff ' Draw the border clip2.DrawRectangle(0, 0, clip2.Width, clip2.Height) clip2.DrawOval(0, 0, 150, 15) The Clip method is only available for Desktop `Graphics`. .. _/topics/graphics/getting_started_with_graphics/custom_controls: Custom controls --------------- Visible controls (controls that have a graphical interface the user can interact with directly, like Buttons) are pictures that have code that controls how they are drawn. This means that a Canvas control can easily be used to supplement the built-in controls. .. image:: https://documentation.xojo.com/topics/graphics/images/getting_started_with_graphics_simple_canvas_button.png Suppose you wanted to create a simple custom control like a rectangle whose fill color toggles from black to gray when clicked. To do so, follow these steps: 1. Drag a Canvas onto a window. #. Add a property to the window called “mFilled As Boolean”. #. Select the Canvas and add the MouseDown event handler. In this event handler, you toggle mFilled and tell the Canvas to redraw itself. .. code:: xojo mFilled = Not mFilled Me.Refresh 1. Finally, in the Paint event, you draw a black rectangle if mFilled = True, otherwise you draw a white rectangle. .. code:: xojo Var fillColor As Color If mFilled Then fillColor = &c000000 ' Black Else fillColor = &caaaaaa ' Gray End If g.DrawingColor = fillColor g.FillRectangle(0, 0, Me.Width, Me.Height) .. _/topics/graphics/getting_started_with_graphics/see_also: .. seealso:: :doc:`Picture`, :doc:`Graphics`, :doc:`WebGraphics`, :doc:`DesktopImageViewer`, :doc:`WebImageViewer`, :doc:`DesktopCanvas`, :doc:`WebCanvas`, :doc:`MobileCanvas`, :doc:`MobileImageViewer` classes; :doc:`Image Set Editor`, :doc:`HiDPI Support`, :doc:`HiDPI Support for Web Apps` topics ================== Working with color ================== Colors are specified using the :doc:`Color` data type. There are several ways to define a color, using several different formats including RGB, HSV and CMY. .. _/topics/graphics/working_with_color/rgb: RGB --- RGB (red, green, blue) is the most common way to specify a color. With RGB you specify the red, green and blue components of a color using an Integer ranging from 0 to 255. This can be done using the RGB method or a hexadecimal color literal. For example, to create a red color using the RGB method: .. code:: xojo Var red As Color = Color.RGB(255, 0, 0) You can also specific a Color using a color literal, which consists of the characters "&c" followed by the hexadecimal value RGB color in this format: ``&cRRGGBB``. This specifies red using a color literal: .. code:: xojo Var red As Color = &cff0000 .. image:: https://documentation.xojo.com/topics/graphics/images/working_with_color_white_color_constant.png You can also use these color literals to create constants on a project item. .. _/topics/graphics/working_with_color/hsv: HSV --- The HSV method specifies a color using the hue, saturation and value which are each represented by Doubles between 0 and 1. .. code:: xojo Var m c As Color = Color.HSV(0.8, 0.5, 0.75) .. _/topics/graphics/working_with_color/cmy: CMY --- The CMY method specifies a color using the cyan, magenta and yellow components of the color, also as Double values between 0 and 1. CMY is a global function s you code it like this: .. code:: xojo Var c As Color = Color.CMY(0.35, 0.9, 0.6) .. _/topics/graphics/working_with_color/color_constants: Color constants --------------- The Color data type has several constants for commonly used colors. These constants are: Black, Blue, Brown, Clear, Cyan, DarkGray, Gray, Green, LightGray, Magenta, Orange, Purple, Red, Teal, White and Yellow. This specifies red using the constant: .. code:: xojo Var red As Color = Color.Red .. _/topics/graphics/working_with_color/inspector: Inspector --------- When using the Inspector with Color properties you will see a small "color box" next to the color property. You can click it to display the OS color chooser to choose a color for the property. .. _/topics/graphics/working_with_color/transparency: Transparency ------------ Transparency indicates how much of the background shows through the color. When you use any of the techniques described above, the color value is not transparent at all and is referred to as opaque. If you want to have a color with transparency, use the RGB or HSV methods which have an additional parameter where you can supply an alpha value from 0, the default of opaque, to 255 which is fully transparent. The alpha parameter is optional with the CMY method. Here are examples: .. code:: xojo Var c1 As Color = Color.RGB(255, 0, 0, 255) Var c2 As Color = Color.HSV(0.8, 0.5, 0.75, 50) Var c3 As Color = Color.CMY(0.8, 0.5, 0.75, 50) For the &c color literal, you can specify the transparency as an additional hexadecimal value. This would be a fully transparent red color: .. code:: xojo Var c As Color = &cFF0000FF .. _/topics/graphics/working_with_color/usage: Usage ----- All of the above techniques return a Color that you can then use with various color properties available on classes. For example, in Paint event of a Canvas the :ref:`DrawingColor` property of the Graphics object is set to blue-is so the text drawn will be in that color: .. code:: xojo g.DrawingColor = Color.RGB(9, 13, 80) g.DrawText("Hello World", 50, 50) .. _/topics/graphics/working_with_color/transparency_example_code: Transparency example code ************************* .. image:: https://documentation.xojo.com/topics/graphics/images/working_with_color_color_rgb_transparency.png This code draws sample color patches in a Canvas at varying levels of transparency. The code is in the Paint event: .. code:: xojo Var red As Color For i As Integer = 0 To 4 red = Color.RGB(255, 0, 0, i * 50) g.DrawingColor = red g.FillRectangle(0, i * 20, 200, 20) g.DrawingColor = &c000000 g.DrawText("Transparency = " + Str(i * 50), 0, i * 20 + 15) Next .. image:: https://documentation.xojo.com/topics/graphics/images/working_with_color_color_cmy_transparency.png This code code (in the Paint event of a Canvas) uses the CMY function to draw sample color patches in a Canvas with varying levels of transparency: .. code:: xojo g.DrawingColor = Color.CMY(0.0, 1.0, 1.0) ' red, no transparency g.DrawRectangle(0, 0, 200, 50) g.FillRectangle(0, 0, 200, 50) g.DrawingColor = Color.CMY(0.0, 0.0, 0.0) g.DrawText("Translucent = 0", 210, 10) g.DrawingColor = Color.CMY(0.0, 1.0, 1.0, 76) ' transparency = 0.3 g.FillRectangle(0, 70, 200, 50) g.DrawingColor = Color.CMY(0.0, 0.0, 0.0) g.DrawText("Translucent = 30%", 210, 80) g.DrawingColor = Color.CMY(0.0, 1.0, 1.0, 127) ' transparency = 0.5 g.FillRectangle(0, 140, 200, 50) g.DrawingColor = Color.CMY(0.0, 0.0, 0.0, 0) g.DrawText("Translucent = 50%", 210, 150) g.DrawingColor = Color.CMY(0.0, 1.0, 1.0, 178) ' transparency = 0.7 g.FillRectangle(0, 210, 200, 50) g.DrawingColor = Color.CMY(0.0, 0.0, 0.0, 0) g.DrawText("Translucent = 70%", 210, 220) g.DrawingColor = Color.CMY(0.0, 1.0, 1.0, 229) ' transparency = 0.9 g.FillRectangle(0, 280, 200, 50) g.DrawingColor = Color.CMY(0.0, 0.0, 0.0, 0) g.DrawText("Translucent = 90%", 210, 290) .. _/topics/graphics/working_with_color/selecting_a_color_with_the_color_picker: Selecting a color with the Color Picker *************************************** If you want to let the user choose a color at runtime you can use the Color Picker by calling the :ref:`Color.SelectedFromDialog` method. The following code displays the Color Picker: .. code:: xojo Var c As Color Var b As Boolean b = Color.SelectedFromDialog(c, "Choose a color") If b Then ' user chose a color ' do something with the color (c) End If If the user cancels out of the Color Picker dialog box, the boolean variable, b, is False; otherwise, the selected color is returned in the color object parameter, c which you can then use to assign to a color property. In the macOS color pickers, you click on a color and a sample of the color appears in the patch area at the top of the screen. When you click OK, the RGB values appear. The Windows version of the Color Picker uses only one format. You can either select one of the predefined colors or click the Define Custom Colors button to display the “advanced” color picker, which depicts colors on a continuum and lets you specify the color using either the RGB or HSV models. Select a color by clicking on a point in the color spectrum or enter values in the RGB or HSV areas. Click Add to Custom Colors to add the custom color to one of the Custom Color samples on the left side of this dialog. In the Code Editor you can also right-click and select "Insert Color" to display the Color Picker so that you can choose a color. When you choose a color, the literal color hex value is placed at the current position of the cursor in the Code Editor. .. _/topics/graphics/working_with_color/see_also: .. seealso:: :doc:`Color` data type; :doc:`Color Groups`, :ref:`Color.SelectedFromDialog` function ============================ Drawing with Vector Graphics ============================ A vector graphic (as opposed to a bitmap graphic) is composed entirely of primitive objects — lines, rectangles, text, circles and ovals, and so forth — that retain their identity in the graphic. They can be resized to display at any size and do not pixelate or otherwise “decompose” like bitmap graphics can. The Object2D class is the base class for all the classes that create primitive objects, which include: :doc:`ArcShape`, :doc:`CurveShape`, :doc:`FigureShape`, :doc:`OvalShape`, :doc:`PixmapShape`, :doc:`RectShape`, :doc:`RoundRectShape` and :doc:`TextShape`. Each of these classes allow you to specify borders, fill and fill colors, rotation, scale and positioning. .. _/topics/graphics/drawing_with_vector_graphics/drawing_and_displaying_a_vector_object: Drawing and displaying a vector object -------------------------------------- You draw a single vector object simply by instantiating it and specifying its properties. For example, the following code defines a RoundRectShape: .. code:: xojo Var r As New RoundRectShape r.Width = 120 r.Height = 120 r.BorderOpacity = 100 r.BorderColor = Color.RGB(0, 0, 0) ' black r.FillColor = Color.RGB(255, 102, 102) r.CornerHeight = 15 r.CornerWidth = 15 r.BorderWidth = 2.5 The only problem with this is that the shape doesn't appear anywhere. It's just “defined” — ready for your use. You need to add a command to draw the vector object in another object. As you learned in the previous section, a :doc:`DesktopCanvas` is a great place to draw stuff. Use the DrawObject method of Graphics to draw Vector graphics. Adding DrawObject to the above code and adding it to the Paint event of a Canvas control: .. code:: xojo Var r As New RoundRectShape r.Width = 120 r.Height = 120 r.BorderOpacity = 100 r.BorderColor = Color.Black r.FillColor = Color.Teal r.CornerHeight = 15 r.CornerWidth = 15 r.BorderWidth = 2.5 g.DrawObject(r) .. _/topics/graphics/drawing_with_vector_graphics/combining_vector_objects: Combining vector objects ------------------------ You can also create composite vector graphics objects that are made up of several individual vector graphics objects. The composite object is a Group2D object—it's just a group of Object2D objects. Use the Append or Insert methods of the Group2D class to add individual vector graphic objects to the Group2D object. When you are finished, draw the object using one call to the DrawObject method. To illustrate this, take the above code and add a second RoundRectShape object offset from the original by 20 pixels. Use ``AddObject`` to add the two RoundRectShapes to the Group2D object and then draw it in the Canvas: .. code:: xojo Var r As New RoundRectShape r.Width = 120 r.Height = 120 r.BorderOpacity = 100 r.BorderColor = Color.Black r.FillColor = Color.Teal r.CornerHeight = 15 r.CornerWidth = 15 r.BorderWidth = 2.5 Var s As New RoundRectShape s.Width = 120 s.Height = 120 s.BorderOpacity = 100 s.BorderColor = Color.Black s.FillColor = Color.Teal s.CornerHeight = 15 s.CornerWidth = 15 s.BorderWidth = 2.5 s.X = r.X + 20 s.Y = r.Y + 20 Var group As New Group2D group.AddObject(r) group.AddObject(s) g.DrawObject(group) .. _/topics/graphics/drawing_with_vector_graphics/combining_bitmap_and_vector_graphics: Combining bitmap and vector `graphics` ------------------------------------- .. image:: https://documentation.xojo.com/topics/graphics/images/drawing_with_vector_graphics_object2d_text_rotation.png You can combine both bitmap and vector `graphics` by first loading the bitmap graphic into a vector object using the PixmapShape class. You then group this with your other vector objects to create a single image. This example combines a bitmap graphic that was added to the project with a TextShape: .. code:: xojo Var px As PixmapShape px = New PixmapShape(PictureName) Var s As TextShape s = New TextShape s.Y = 70 s.Text = "Hello, world!" s.FontName = "Helvetica" s.Bold = True Var d As New Group2D d.AddObject(px) d.AddObject(s) g.DrawObject(d) .. _/topics/graphics/drawing_with_vector_graphics/saving_and_loading_vector_graphics: Saving and loading vector `graphics` ----------------------------------- Vector `graphics` can be saved using the Save method of the :doc:`Picture` class. Vector `graphics` are stored as PICT files on macOS and as EMF files on Windows. .. note:: Saving and Loading vector graphics is not supported on Linux. Since you need a Picture object to call save, you should draw your vector `graphics` to a picture first rather than directly to the Canvas. This gives you the Picture that you can save and allows you to also draw the picture to the Canvas if needed. In this example, *SavePicture* is a Picture property on the window: .. code:: xojo Var r As New RoundRectShape r.Width = 120 r.Height = 120 r.BorderOpacity = 100 r.BorderColor = Color.RGB(0, 0, 0) ' black r.FillColor = Color.RGB(255, 102, 102) r.CornerHeight = 15 r.CornerWidth = 15 r.BorderWidth = 2.5 SavePicture.Graphics.DrawObject(r, 100, 100) g.DrawPicture(SavePicture, 0, 0) ' Draw SavePicture in the Canvas To save this picture, add a button called Save to the window and use this code: .. code:: xojo Var file As FolderItem If TargetMacOS Then file = FolderItem.ShowSaveFileDialog("", "vector.pict") Else file = FolderItem.ShowSaveFileDialog("", "vector.emf") End If If file <> Nil Then SavePicture.Save(file, Picture.SaveAsDefaultVector) End If To load a vector image, you use the OpenAsVectorPicture method of :doc:`FolderItem`. This code prompts the user to select a vector image and then displays it in an ImageWell: .. code:: xojo Var file As FolderItem file = FolderItem.ShowOpenFileDialog("") If file <> Nil Then Var p As Picture p = Picture.OpenVector(file) If p <> Nil Then VectorWell.Image = p End If End If .. _/topics/graphics/drawing_with_vector_graphics/see_also: .. seealso:: :doc:`Object2D` class ============= HiDPI support ============= Xojo supports high dots per inch displays for Mac, Windows and Linux desktop apps. Apple calls these displays "Retina Displays", but the generic term is HiDPI. For most of your apps, you will likely only need to turn on the Supports Hi-DPI switch in the Inspector for the Shared Build Settings to enable your app to support HiDPI displays. When you do this, all UI elements will take advantage of HiDPI displays. Xojo also supports :doc:`HiDPI web apps`. On Windows, Microsoft's `Writing DPI-Aware Desktop and Win32 Applications `_ document is a useful reference. To make it as easy as possibly for you to support HiDPI displays in your apps that have custom graphics, there are several changes to these desktop classes: :doc:`DesktopApplication`, :doc:`Picture`, :doc:`Graphics`, :doc:`DesktopWindow` and :doc:`DesktopCanvas`. .. _/topics/graphics/hidpi_support/overview: Overview -------- Enable HiDPI support for your web apps by turning on the "Supports HiDPI" property in the Shared Build Settings Inspector of the project. When HiDPI is enabled, measurements are done in points rather than pixels. For example, if you set a ListBox Width property to 100, that means 100 points, which on a 2x HiDPI screen would actually be 200 pixels. By using points, this allows you UI elements to be the same relative size regardless of the display. The most significant changes are to :doc:`Picture`, which now essentially can contain three different types of "pictures": Bitmap, Immutable Bitmap, Vector and Image. Of these, the Image type is the one that supports HiDPI displays. The other Picture types work as they have before. To be precise, it breaks down like this * **Bitmap**: A mutable Picture object that is identical to what exists now. * **Immutable Bitmap**: A single-representation read-only bitmap. If the bitmap is RGB with 8, 16, 24, or 32 bits per pixel, there will be a read-only RGBSurface that can be used to access raw pixel data that hasn't gone through color conversion or pre-multiplied alpha (typically). * **Image**: An array of mutable or immutable bitmaps that can be loaded from an image set, a TIFF, or created at runtime. When drawn to a graphics object, the "best" bitmap (as described below) is chosen and drawn. .. _/topics/graphics/hidpi_support/image_sets: Image sets ---------- An Image Set project item can contain pictures at three different scales: 1x, 2x and 3x. All images must have the same aspect ratio. For example if your 1x image is 64x64 @ 72DPI, then the 2x image must be the same ratio (1:1) and would typically be 128x128 @ 72DPI or 64x64 @ 144DPI. Make sure that that the tool you use to create or edit your images properly sets the image DPI (dots per inch). Some tools (such as Photoshop) do not properly set the DPI which can cause Xojo to interpret the image as the wrong size. You can add Images to your projects by adding an Image Set from the Insert menu: Insert > Image. .. note:: When the Supports HiDPI property is turned ON, dragging a picture file to the project creates an Image Set. When it is OFF, dragging a picture to the project adds it as a Picture. If you want to add a picture to an Image Set when Supports HiDPI is OFF, first add the Image Set to the project and then drag the picture file to the appropriate 1x, 2x or 3x slot. Picture files added to the project in Image Sets are treated as Images and follow the rules described in the Image section. Picture files in your existing projects are treated as 1x and will be scaled as necessary. Should you want to update your projects with multiple sizes for your pictures, you would create an Image Set with the same name as the original picture and add images to the 1x, 2x and 3x slots as necessary. Your code that refers to these images will continue to work without changes since the name used for the picture now refers to the Image Set. .. _/topics/graphics/hidpi_support/image: Image ----- A Picture that is actually an Image type can consist of one or more bitmaps. When run on an HiDPI display, the image that best matches the display is used based on these rules: 1. The algorithm iterates through the indexed images, first finding picture with the closest number of pixels to the destination, preferring the next largest image if no exact match is found. #. If multiple pictures match, the picture with the DPI closest to the destination scale is chosen. The Picture class has many existing properties which have been updated to work with Images. This table shows you which properties can be used for the various types of pictures: * Not relevant for Vector Image This table indicates which methods can be used for the various types of pictures: .. csv-table:: :header: "Picture Property", "Mutable Bitmap", "Immutable Bitmap", "Vector", "Image" :widths: auto "**Graphics**","Valid","Nil","Nil","Nil" "**Depth**","User-specified","File-specified","0","0" "**HasAlphaChannel**","User-specific","File-specified","False","False" "**Height**","Pixels","Pixels","Points","Points" "**HorizontalResolution**","User-specified","File-specified","72*","72" "**ImageCount**","0","0","0","File-specified" "**Mask**","User-specified","Nil","Nil","Nil" "**Objects**","Nil","Nil","Valid","Nil" "**RGBSurface**","Valid","Nil","Valid","Nil" "**Transparent**","User-specified","0","0","0" "**VerticalResolution**","User-specified","File-specified","72*","72" "**Width**","Pixels","Pixels","Points","Points" * Not relevant for Vector pictures. This table indicates which methods can be used for the various types of pictures: .. csv-table:: :header: "Picture Method", "Bitmap", "Immutable Bitmap", "Vector", "Image" :widths: auto "ApplyMask","Valid","IllegalOperationException","IllegalOperationException","IllegalOperationException" "CopyMask","Valid","Valid","IllegalOperationException","IllegalOperationException" "CopyOSHandle","Valid","Valid","IllegalOperationException","IllegalOperationException if not an NSImage" "GetData","Valid","Valid","IllegalOperationException","IllegalOperationException if lossy" "Save","Valid","Valid","Valid","IllegalOperationException if lossy" .. _/topics/graphics/hidpi_support/picture_notes: Picture notes ------------- * Pictures in the project, loaded from disk, received from drag and drop, or loaded from data will be loaded as Images if the format supports it. This better preserves color spaces, loads multiple resolutions if the format supports it, and reduces memory usage. Simple bitmap formats will load as Immutable Bitmaps. * The ImageCount and IndexedImage properties provide a way to access the different bitmaps that comprise an image. It is possible for this to be zero. * Pictures in the project will be composited at build time if they have a mask or transparent property set. At runtime the picture will be loaded as an image. * Use Image Sets to add multi-resolution Images (with the same aspect ration) to a project. * When a Picture is drawn via DrawPicture or used in a UI element like a :doc:`menu item`, the best available representation is used. Vectors are rasterized (converted to bitmap) at the required size. * For Pictures that are used in user interface elements like :doc:`menu items` or :doc:`bevel buttons`, the chosen image representation is automatically recalculated when the backing store scale factor changes. * To convert from an Image to a Bitmap, a bitmap can be created with the desired pixel dimensions and then the source image drawn into it. This mirrors how a bitmap with a mask can be converted to a bitmap with an alpha channel. * Using :ref:`DrawInto` may result in scaling the image up or down if the backing store scale factor for the control's window does not match the targel's backing store scale factor. .. _/topics/graphics/hidpi_support/framework_api_changes: Framework API changes --------------------- .. _/topics/graphics/hidpi_support/application: Application *********** SupportsHiDPI As Boolean (read-only) You can only set this property in the Inspector for the Shared Build Settings. Set to ON to make the app HiDPI-aware. This is set to ON by default. .. _/topics/graphics/hidpi_support/:doc:`picture`: :doc:`Picture` ************************************* Constructor(width As Integer, height As Integer, bitmaps() As Picture) This constructor creates an Image from one or more bitmap pictures. :ref:`Property Type As Types` (read-only) The type of this Picture. Used with the Types Enum. :ref:`Enum Types` An enumeration that contains all the valid picture types. :ref:`Enum HandleTypes.MacNSImage` An NSImage object that has been autoreleased, so an explicit release is not necessary. This is supported for all kinds of Pictures, including vector images. Supported in Mac GUI applications. :ref:`BestRepresentation(width As Integer, height As Integer, scale As Double) As Picture` Calculates which picture is the best to use for drawing at the requested size. :ref:`CopyOSHandle(width As Integer, height As Integer, scale As Double, type As Picture.HandleTypes) As Ptr` Returns a platform-specific image handle that is the best match for drawing at the given resolution, using the same logic as BestRepresentation. .. _/topics/graphics/hidpi_support/graphics: `Graphics` ********** The Graphics class has the following changes. :ref:`Property ScaleX As Double` :ref:`Property ScaleY As Double` The scale factor used when converting user space coordinates to backing store coordinates. These can be modified at runtime and must be greater than zero. Graphics.Pixel This is now deprecated. Use :ref:`Picture` as a replacement. .. _/topics/graphics/hidpi_support/window: Window ****** The :doc:`DesktopWindow` class has the following changes. :ref:`Event Sub ScaleFactorChanged` The backing store scale factor has changed for this Window and the application should invalidate any cached bitmaps or other relevant state. This is only called for Mac apps. :ref:`Property ScaleFactor As Double (read-only)` The scale factor used when converting user space coordinates to backing store coordinates for this Window. :ref:`BitmapForCaching(width As Integer, height As Integer) As Picture` Returns a bitmap that is configured correctly for using as a cache for content to be drawn to this Window. Raises exceptions in the following cases: * InvalidArgumentException if width, height, or scale are less than or equal to zero. * OutOfMemoryException if the picture could not be allocated. .. _/topics/graphics/hidpi_support/canvas: Canvas ****** :ref:`Event Sub ScaleFactorChanged` The backing store scale factor has changed and the canvas should invalidate any cached bitmaps or other relevant state. This only gets called for Mac apps. .. _/topics/graphics/hidpi_support/other_information: Other information ----------------- .. _/topics/graphics/hidpi_support/windows_hidpi_known_issues: Windows HiDPI known issues ************************** * :doc:`DesktopMoviePlayer` is not scaled on HiDPI screens. .. _/topics/graphics/hidpi_support/related: .. seealso:: * :doc:`HiDPI Support for Web Apps` ========================== HiDPI support for web apps ========================== Xojo supports high dots per inch displays for web apps. Apple calls these displays "Retina Displays", but the generic term is HiDPI. For most of your web apps, you will likely only need to turn on the Supports Hi-DPI switch in the Inspector for the Shared Build Settings to enable your app to support HiDPI displays. When you do this, all UI elements will take advantage of HiDPI displays. .. _/topics/graphics/hidpi_support_for_web_apps/overview: Overview -------- Enable HiDPI support for your web apps by turning on the "Supports HiDPI" property in the Shared Build Settings Inspector of the web project. Use Image Sets to add multi-resolution images to your projects. Multi-resolution Images are now available in Web Projects. Just like iOS and Desktop projects, you can now use Image Sets to contain multiple representations of the same image and the Web Framework will automatically deliver the correct version to the browser. .. _/topics/graphics/hidpi_support_for_web_apps/image_sets: Image sets ---------- Image Sets contain multiple size representations of the same image. The Web Framework automatically delivers the correct size to the browser. An Image Set project item can contain pictures at three different scales: 1x, 2x and 3x. All images must have the same aspect ratio. For example if your 1x image is 64x64 @ 72DPI, then the 2x image must be the same ratio (1:1) and would typically be 128x128 @ 72DPI or 64x64 @ 144DPI. Make sure that that the tool you use to create or edit your images properly sets the image DPI (dots per inch). Some tools (such as Photoshop) do not properly set the DPI which can cause Xojo to interpret the image as the wrong size. You can add Images to your projects by adding an Image Set from the Insert menu: Insert > Image. When the Supports HiDPI property is turned ON, dragging a picture file to the project creates an Image Set. When it is OFF, dragging a picture to the project adds it as a Picture. If you want to add a picture to an Image Set when Supports HiDPI is OFF, first add the Image Set to the project and then drag the picture file to the appropriate 1x, 2x or 3x slot. The 3x image is not typically needed at this time. Picture files added to the project in Image Sets are treated as Images. Picture files in your existing projects are treated as 1x and will be scaled as necessary. Should you want to update your projects with multiple sizes for your pictures, you would create an Image Set with the same name as the original picture and add images to the 1x, 2x and 3x slots as necessary. Your code that refers to these images will continue to work without changes since the name used for the picture now refers to the Image Set. .. _/topics/graphics/hidpi_support_for_web_apps/framework_api_changes: Framework API changes --------------------- .. _/topics/graphics/hidpi_support_for_web_apps/webpicture: WebPicture ********** :doc:`Constructor(width As Integer, height As Integer, files() As FolderItem)` Creates a multi-resolution image from picture files on the drive. It creates a file-based WebPicture which saves on memory and is only loaded/delivered to a browser when it is requested. :doc:`WebImageViewer`, :doc:`WebCanvas` and :doc:`WebToolbar` have all been updated to use the new WebPicture format and deliver high resolution images to browsers that support them. .. _/topics/graphics/hidpi_support_for_web_apps/websession: WebSession ********** Property :ref:`ScaleFactor` As Double Returns the ScaleFactor of the current session. Event :ref:`ScaleFactorChanged` Called if/when the user moves their browser between screens if the ScaleFactor has changed. Note: The built-in controls automatically request new images when ScaleFactor changes. .. _/topics/graphics/hidpi_support_for_web_apps/other_changes: Other changes ------------- * Pictures dragged into your projects (whether directly or as part of an Image) are now exported to disk, placed in the Resources directory and loaded dynamically at runtime. * High-resolution versions of all of the graphics that make up the built-in web controls are now automatically included with your built apps. .. _/topics/graphics/hidpi_support_for_web_apps/see_also: .. seealso:: * :doc:`HiDPI Support` =============================================== Supporting Dark Mode and more with Color Groups =============================================== Many controls have properties that allow you to change the color of one or more parts of the control. Color is also used when drawing entirely new, custom controls using the :doc:`MobileCanvas` control. While Xojo does support color constants for this purpose, they are insufficient for managing color when the user's device changes from light mode to dark mode or dark mode to light mode. They also provide no way for you to access colors predefined by the OS. :doc:`Color Group` objects solve these problems. .. _/topics/graphics/supporting_dark_mode_and_more_with_color_groups/what_is_a_color_group?: What is a Color Group? ---------------------- A :doc:`Color Group` is a class you can add to your project (or instantiate via code) that can be either a single color or two colors (one for light mode and the other for dark mode). A :doc:`Color Group` object can be used in all the same ways you use the intrinsic :doc:`Color` type. The primary advantage of a :doc:`Color Group` is that when you use one, your app will automatically use the appropriate color when the user's device switches from light mode to dark mode and back. A Color Group has three modes: .. csv-table:: :header: "Type", "Description" :widths: auto .. image:: https://documentation.xojo.com/topics/graphics/images/supporting_dark_mode_and_more_with_color_groups_color_group_-_single.png,"A single color which does not change when the OS changes from light mode to dark mode." .. image:: https://documentation.xojo.com/topics/graphics/images/supporting_dark_mode_and_more_with_color_groups_color_group_dual.png,"Two colors, one for light mode and the other for dark mode." .. image:: https://documentation.xojo.com/topics/graphics/images/supporting_dark_mode_and_more_with_color_groups_color_group_-_named.png,"An OS-defined named color. For example, on iOS *labelColor* is a named color for labels which is black in light mode and white in dark mode." .. _/topics/graphics/supporting_dark_mode_and_more_with_color_groups/adding_a_color_group_to_a_project: Adding a Color Group to a project --------------------------------- You add a :doc:`Color Group` by choosing Color Group from the Insert toolbar button, from the Insert menu or dragging one from the Library to the Navigator. .. note:: Color Groups are supported for desktop, mobile and web projects. .. _/topics/graphics/supporting_dark_mode_and_more_with_color_groups/retrieving_colors_via_code: Retrieving colors via code -------------------------- You can retrieve named colors via code using the :ref:`ColorGroup.NamedColor` function: .. code:: xojo NamedColor(name as String) As Color This allows you to look up a named color based on the current state of the device/platform without having to create a ColorGroup first. .. warning:: Named colors are case-sensitive. As an example, you can get the previously mentioned labelColor like this: .. code:: xojo Var myLabelColor As Color = ColorGroup.NamedColor("labelColor") If you attempt to look up a named color which doesn't exist on the current OS, the iOS 13+ equivalent is returned. If you attempt to look up a named color that doesn't exist at all, an :doc:`InvalidArgumentException` is raised. If you attempt to look up a named color on a platform that doesn't support named colors (or Color Groups at all), an :doc:`UnsupportedOperationException` is raised. ============================================= Updating code that used the Graphics property ============================================= Prior to 2018r3 Window and Canvas both had a Graphics property that you could access and draw to. This was deprecated in 2011 because it had significant performance issues on all platforms. The preferred way to draw your graphics since 2011 has been to use the :ref:`DesktopWindow.Paint` or :ref:`DesktopCanvas.Paint` event handlers and the supplied parameter *g As Graphics*. Starting with 2018r3, this Graphics property was removed from Window and Canvas so if you had code that was still relying on it, that code will no longer compile. Here are some tips on how you can migrate your code to use the Paint event handlers and tell the Canvas to update with a call to Refresh. .. _/topics/graphics/updating_code_that_used_the_graphics_property/simple_drawing: Simple drawing -------------- If your code was simple and just drawing a :doc:`Picture` to the Graphics property, then you would just move the code to the Paint event handler. For example, this code is drawing a picture: .. code:: xojo Canvas1.Graphics.DrawPicture(MyPic, 0, 0) Where that code existed, you would tell it to redraw Canvas1: .. code:: xojo Canvas1.Refresh And then in the :ref:`DesktopCanvas.Paint` event handler you would draw the picture: .. code:: xojo g.DrawPicture(MyPic, 0, 0) .. _/topics/graphics/updating_code_that_used_the_graphics_property/more_advanced_drawing: More advanced drawing --------------------- If your code was doing more advanced drawing to the Graphics property then you will need to do more substantial changes to your code. Since you ideally want to do all the drawing in the Paint event handler you may need to use a separate means to store what was being updated (such a properties or a class) and then in the Paint event handler use these values to do the drawing. So say you had code in a :ref:`MouseDown` that was drawing a point on the Canvas when the mouse was clicked like this: .. code:: xojo Canvas1.Graphics.FillRectangle(X, Y, 5, 5) You would now create properties to store the X and Y values from the MouseDown event: .. code:: xojo LastClickX As Integer LastClickY As Integer In MouseDown you would now save the values to the properties and tell the Canvas to update when the mouse was clicked: .. code:: xojo LastClickX = X LastClickY = Y Canvas1.Refresh(False) Lastly you would put code in :ref:`DesktopCanvas.Paint` to draw the point: .. code:: xojo g.FillRectangle(LastClickX, LastClickY, 5, 5) Although the above is recommended, you may find that making all the changes needed is more than you want to change. Another technique you can use is to do the drawing in an offscreen picture and then draw the picture in the Canvas. With this technique you create a :doc:`Picture` property for your drawing: .. code:: xojo MyDrawing As Picture You then initialize it to the size you want using the :ref:`DesktopWindow.BitmapForCaching` method (so that it is HiDPI compatible): .. code:: xojo MyDrawing = Self.BitmapForCaching(500, 500) And then everywhere where you were drawing directly to Graphics you now draw to your Picture property and tell the Canvas to update. So instead of: .. code:: xojo Canvas1.Graphics.DrawingColor =Color.RGB(255, 0, 0) Canvas1.Graphics.DrawLine(0, 0, 100, 100) your code would look like this: .. code:: xojo MyDrawing.Graphics.DrawingColor =Color.RGB(255, 0, 0) MyDrawing.Graphics.DrawLine(0, 0, 100, 100) Canvas1.Refresh(False) And in the Paint event for Canvas1: .. code:: xojo g.DrawPicture(MyDrawing, 0, 0) .. _/topics/graphics/updating_code_that_used_the_graphics_property/do_not_use_graphics_outside_of_paint_event_lifetime: Do not use `Graphics` outside of Paint event lifetime ------------------------------------------------------ Related to this is another thing to keep in mind. Do not use this `Graphics` object outside the lifetime of the Paint event. You can certainly pass the `Graphics` parameter from the Paint event to other methods, but you should not keep a reference of it in a property or elsewhere. When the Paint event eventually completes, this `Graphics` object is no longer valid and the copy you've saved in a property also becomes invalid. Attempting to use a saved `Graphics` object outside of the Paint event's lifetime may raise an exception in future versions of Xojo. .. _/topics/graphics/updating_code_that_used_the_graphics_property/see_also: .. seealso:: * :doc:`HiDPI Support` topic; :doc:`Picture`, :doc:`DesktopCanvas`, :doc:`DesktopWindow` classes iOS === .. toctree:: :maxdepth: 1 :name: sec-ios Differences between iOS and Android Getting the user's location from their mobile device How to apply a blur effect on iOS Sending and receiving mobile notifications Loading 3rd Party Frameworks in Xojo iOS Getting started --------------- * :doc:`QuickStart` * :doc:`Tutorial` .. seealso:: :doc:`User interface topics` ==================================================== Getting the user's location from their mobile device ==================================================== The Location control provides a way for you to get location information for the device. This is not a visual control, so dragging it appears on the Shelf when dragged to the layout. Use the Debug > Location menu in the iOS Simulator to provide locations for testing. Frequently getting the location can cause significant battery drain, so only be sure to set Enabled = False when it is not needed. .. image:: https://documentation.xojo.com/topics/ios/images/getting_the_user's_location_from_their_mobile_device_ios_location_library_icon.png Below is a list of commonly used events, properties and methods. Refer to :doc:`MobileLocation` for the complete list. .. _/topics/ios/getting_the_user's_location_from_their_mobile_device/events: Events ------ :ref:`LocationChanged`: Called when a location update is received from the OS. .. _/topics/ios/getting_the_user's_location_from_their_mobile_device/properties: Properties ---------- :ref:`Accuracy`: Specifies the level of accuracy for the location using the Accuracies enum. The "Best" accuracy is used by default, but you can increase or decrease the accuracy as necessary to manage battery usage. :ref:`AuthorizationState`: Indicates the level of authorization that is allowed for location. Check this before enabling. :ref:`Enabled`: When enabled is True, the LocationChanged event is called with location updates. You should set this to False when the location is not needed. .. _/topics/ios/getting_the_user's_location_from_their_mobile_device/methods: Methods ------- :ref:`RequestUsageAuthorization`: Requests approval to use location either always or when the app is in use. .. _/topics/ios/getting_the_user's_location_from_their_mobile_device/usage: Usage ----- Before you can get the location you must request authorization. This is typically done like this: .. code:: xojo If MyLocation.AuthorizationState = MobileLocation.AuthorizationStates.AppInUse Then ' we've got our requested authorization state, start getting LocationChanged events MyLocation.Enabled = True Else ' we don't have authorization yet, so ask for it MyLocation.RequestUsageAuthorization End If Once the control is enabled you will start getting LocationChanged events when the location is updated. .. _/topics/ios/getting_the_user's_location_from_their_mobile_device/see_also: .. seealso:: :doc:`MobileLocation` class ================================= How to apply a blur effect on iOS ================================= Since Xojo 2019r3 we have the ability to apply a blur effect on any of the :doc:`MobileRectangle` instances we may be using in the design of our iOS apps. Why we would want to do that? For example, in order to make our frontmost view contents to stand over those found in the background! And the best part is that we can achieve this very easy. The steps involved in applying this technique are really simple: * Make sure that the parent view containing your MobileRectangle instance (the one you want to use to apply the blur effect) has its background color set to transparent. How you can achieve this? just make sure that its **FillColor** is set to **Clear**. * Invoke the **SetEffect** method on the MobileRectangle instance that will apply the Blur effect (this control should be the frontmost layer to blur all the contents behind it). * Use any of the :ref:`MobileRectangle.BlurStyles` values you want to apply from the 21 available (it is an Enumeration). .. _/topics/ios/how_to_apply_a_blur_effect_on_ios/adding_the_base_view_in_the_hiearchy: Adding the base view in the hierarchy ------------------------------------- Let's put these easy rules into practice! For that, create a new iOS project in the Xojo IDE. With the default **Screen1** item selected in the Project Viewer, add an **MobileRectangle** Control from the Library into the Design Layout. This will be the one we will use as the Parent (or Super) in order to apply the Blur effect. The added rectangle will appear in the Project Browser with the default name "RectControl". Select it and use the Inspector Panel to change its name to "BaseRectangle". Then, move it in the Layout Editor so its size and position is similar to the one displayed in the following screenshot: .. image:: https://documentation.xojo.com/topics/ios/images/how_to_apply_a_blur_effect_on_ios_blureffect_1.png .. _/topics/ios/how_to_apply_a_blur_effect_on_ios/adding_a_middle_image: Adding a middle image --------------------- Let's add now an image as the middle image, so we can see more clearly the blur effect when the app is run. For that, add a new :doc:`Picture` object from the Library and select it in the Project Browser. Next, change its name to "AppleImage" using the Inspector Panel. When selected in the Project Browser, the Picture will bring the Image Editor in. Here you can see three image placeholders where you can simply drag and drop three different versions of the same image in order to be displayed at the best possible resolution in the device (1x, 2x and 3x). The image displayed will be based mostly on the ppi resolution offered by the screen on the iOS device. (Is not mandatory to fill-in all the three placeholders for the image, but in a real iOS app you'll probably want to do it). .. image:: https://documentation.xojo.com/topics/ios/images/how_to_apply_a_blur_effect_on_ios_iosimageeditor.png With our image already set, let's add now the control that will be in charge of displaying it! Once again, select the **Screen1** item to bring in the Layout Editor and add this time a :doc:`MobileImageViewer` from the Library. Drag the just added instance so it is included as a child of the "BaseRectangle" control. To make that, resize the MobileImageViewer and place it inside the limits of the "BaseRectangle". The IDE will show this being a child of the "BaseRectangle" control displaying the "BaseRectangle" outline in red color. (You also can set its Super property to "BaseRectangle" in the Inspector Panel.) .. image:: https://documentation.xojo.com/topics/ios/images/how_to_apply_a_blur_effect_on_ios_iosimageviewlayout.png Now, with the MobileImageViewer still selected, use the Inspector Panel to change its Name property to "AppleImage", DisplayMode to the **ScaleAspectToFit** option, and select the **AppleImage** entry in the Image dropdown menu. Now, the view will display our apple image right in the Layout Editor. .. _/topics/ios/how_to_apply_a_blur_effect_on_ios/adding_the_rectangle_in_charge_to_apply_the_blur_effect: Adding the rectangle in charge to apply the blur effect ------------------------------------------------------- The third item we are going to add to the project is the rectangle in charge to apply the blur effect itself. Drag a new **MobileRectangle** from the Library and put it right over the "BaseRectangle, making sure that it becomes a child in that view. Use the Inspector Panel to change its Name to "BlurRectangle", and make sure that its Parent property is set to "BaseRectangle". If you want, you can add a :doc:`MobileLabel` from the Library so it is centered inside the 'BlurRectangle' view (i.e: "BlurRectangle" is its Super class). The layout at this point should be similar to the one displayed in the following screenshot: .. image:: https://documentation.xojo.com/topics/ios/images/how_to_apply_a_blur_effect_on_ios_iosblurhierarchy.png .. _/topics/ios/how_to_apply_a_blur_effect_on_ios/adding_a_table_with_blur_modes_to_choose_from: Adding a table with blur modes to choose from --------------------------------------------- Lastly, let's add a :doc:`iOSMobileTable` to our user interface so it displays some of the available Blur options. Place it in the upper third of the main view and use the Inspector Panel to change its name to "OptionsTable". .. image:: https://documentation.xojo.com/topics/ios/images/how_to_apply_a_blur_effect_on_ios_blurfinallayout.png With the "OptionsTable" still selected, add the **Opening** and **RowActionSelected** **Event Handlers** to it. Write the following fragment of code into the Open Code Editor, in order to add the Rows with the options: .. code:: xojo Me.AddSection("Options") Me.AddRow(0, "Extra Light") Me.AddRow(0, "Light") Me.AddRow(0, "Dark") Me.AddRow(0, "ExtraDark") Me.AddRow(0, "Regular") Me.AddRow(0, "Prominent") Me.AddRow(0, "UltraThinMaterial") Me.AddRow(0, "ThisMaterial") Me.AddRow(0, "Material") Me.AddRow(0, "ThickMaterial") Me.AddRow(0, "ChromeMaterial") Me.AddRow(0, "UltraThinMaterialLight") Me.AddRow(0, "ThinMaterialLight") Me.AddRow(0, "MaterialLight") Me.AddRow(0, "ThickMaterialLight") Me.AddRow(0, "ChromeMaterialLight") Me.AddRow(0, "UltraThinMaterialDark") Me.AddRow(0, "ThinMaterialDark") Me.AddRow(0, "MaterialDark") Me.AddRow(0, "ThickMaterialDark") Me.AddRow(0, "ChromeMaterialDark") Then, write the following code in the RowActionSelected Code Editor. This is the one that will be fired every time the user taps on any of the available options, so we can use the selected row number to apply the desired blut mode to our "BlurRectangle" view: .. code:: xojo Select Case row Case 0 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.ExtraLight) Case 1 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.Light) Case 2 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.Dark) Case 3 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.ExtraDark) Case 4 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.Regular) Case 5 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.Prominent) Case 6 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.UltraThinMaterial) Case 7 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.ThinMaterial) Case 8 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.Material) Case 9 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.ThickMaterial) Case 10 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.ChromeMaterial) Case 11 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.UltraThinMaterialLight) Case 12 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.ThinMaterialLight) Case 13 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.MaterialLight) Case 14 BlurRectangle.SetEffect(MobileRectangle.BlurStyles.ThickMaterialLight) End Select The last step is making sure that the superclass in this hierarchy has its background set to fully transparent, because otherwise the blur effect wouldn't work. For this, select the 'BaseRectangle' in the Project Browser and add the Opening Event Handler to it. Then, we only need to write the following line of code in the resulting Code Editor: .. code:: xojo Me.FillColor = Color.Clear .. _/topics/ios/how_to_apply_a_blur_effect_on_ios/testing_the_app: Testing the app --------------- That's all! We are ready to Run our example iOS app and play with some of the options listed in the table. As you tap, the "BlurRectangle" will apply the selected blur style affecting the contents of the views behing it (the "AppleImage" in this case). .. image:: https://documentation.xojo.com/topics/ios/images/how_to_apply_a_blur_effect_on_ios_bluronios_12.29.22.gif .. youtube:: oqnjBei_yDM ========================================== Sending and receiving mobile notifications ========================================== .. _/topics/ios/sending_and_receiving_mobile_notifications/notification_fundamentals: Notification fundamentals ------------------------- When sending and/or receiving user notifications in iOS, it's important to understand that: 1. You must request permission from the user of the device to present notifications. #. A user can revoke permission at any time which means your notifications will no longer be delivered. #. Timely delivery is not guaranteed. Notifications can be delayed for a number of reasons including (but not limited to): * The user's Screen Time or Sleep settings * The user's preferences for your app. * The device's available power. #. Remote Notifications that are pushed through a server do not work in the iOS simulator. That said, you can create remote notification packet and drag and drop it onto the simulator for testing purposes. These are limitations of iOS itself, not Xojo. .. _/topics/ios/sending_and_receiving_mobile_notifications/preparing_to_work_with_notifications: Preparing to work with notifications ------------------------------------ To add Notification support to your project, you need to add a :doc:`NotificationCenter` object to your project. Without it, trying to send and/or receive notifications will result in an :doc:`UnsupportedOperationException`. .. _/topics/ios/sending_and_receiving_mobile_notifications/requesting_authorization_to_send_notifications: Requesting authorization to send notifications ---------------------------------------------- To send notifications to the user, you must request permission. This is done by calling :ref:`NotificationCenter.RequestNotification`. .. code:: xojo NotificationCenter.RequestAuthorization(ParamArray options() as MobileNotifications.AuthorizationOptions) When you do so, you tell the system the ways in which you'd like the notification to be presented to the user. The options are: .. csv-table:: :header: "Type", "Description" :widths: auto "Badge", "Adds a badge to your app icon when a notification is delivered." "Sound", "Plays a sound when a notification is delivered." "Alert", "Displays an alert when a notification is delivered." "CarPlay", "Indicates that notifications should be displayed when received while CarPlay is in use." "CriticalAlert", "Shows notifications regardless of sound and banner restrictions. This option is designed for government agencies and requires a special entitlement from Apple." "ProvidesNotificationSettings", "Indicates to the system that your app provides its own notification settings view." "Provisional", "Allows the app to show notifications on a provisional basis which the user can decide to accept later." "Announcement", "Asks that notifications be read by Siri should the user be using their AirPods when the notification is received." Requesting authorization will result in NotificationCenter raising either an AuthorizationSucceeded event (along with information about how the user will receive the notification based upon their notification settings) or an Error event because they have notifications turned off for your app. Methods for sending notifications --------------------------------- .. _/topics/ios/sending_and_receiving_mobile_notifications/sending_local_notifications: Sending local notifications --------------------------- There are two ways of sending notifications: locally and remotely. Local notifications are those that are sent by your app. Immediately sending a simple notification is easy. You create a :doc:`NotificationContent` object, give it some text and send it on its way: .. code:: xojo Var content As New NotificationContent("Hello, world!") NotificationCenter.Send(content) In this case, Send is a bit of a misnomer. What Send is doing is handing off the notification content you created to the system and asking it to send it ASAP. In addition to creating notifications that are sent immediately, you can also schedule them to be sent later. In this example, the notification will be sent in 30 seconds: .. code:: xojo Var content As New NotificationContent("Hello, world!") NotificationCenter.Send(30, content) If you need to send a notification some number of years, months and days in the future, you can do so using a :doc:`DateInterval`. This example sends the notification 3 days from now: .. code:: xojo Var di As New DateInterval(0, 0, 3) NotificationCenter.Send(di, content) If you need to send the notification on a specific date, use a :doc:`DateTime` object: .. code:: xojo Var dt As New DateTime(2023, 4, 16, 0, 0, 0, Timezone.Current) NotificationCenter.Send(dt, content) You can also send notifications when the user arrives at or leaves a location: .. code:: xojo ' Set up some content Var content As new NotificationContent("Hello, world!") NotificationCenter.Send(90, 0, 300, "NorthPole", LocationNotification.Modes.NotifyOnEntry, content) .. note:: iOS uses some heuristics to figure out if a user is actually entering or leaving the region and that they are not just skimming the edge, so your notification may not appear immediately. Once notification has been successfully delivered (via the Send method) to the system, the NotificationSent event will be raised. In all these examples, the content of the notification is very simple. Notifications can contain :doc:`much more` than just a single message. .. _/topics/ios/sending_and_receiving_mobile_notifications/canceling_notifications: Canceling local notifications ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Send method can also be called as a function in which case, it returns the ID (specifically a UUID) that uniquely identifies the notification so that you can cancel it later if it becomes irrelevant before it's actually been sent. To cancel a notification, pass it's ID to the RemovePendingRequests method. If the notification has already been delivered but you wish to remove it from the user's Notification Center, pass the ID to the RemoveDeliveredNotifications method. .. _/topics/ios/sending_and_receiving_mobile_notifications/sending_remote_notifications: Sending remote notifications ---------------------------- A remote notification is one that is sent to the device rather than sent from the device. For example, when your weather app notifies you of an extreme weather event in your area, that's a remote notification. To send remote notifications to your app, you'll need to do the following: 1. Apple Developer website * Remote Notifications will need to be added to the application's identifier. * You will also need an Apple Push Services certificate (which allows Apple to authenticate from whom a remote notification is being sent). #. Xojo * The Remote Notifications entitlement needs to be enabled in the Xojo iOS Build Settings on the Advanced Tab under Capabilities. #. A service provider for interfacing with the Apple Push Notification system (APNs). Xojo Cloud great simplifies this. Once you have these things, sending remote notifications to your app is pretty simple. If you want to send notifications that are visible to the user like the ones above, you will still need to get the user's permission for that. In addition, your app needs to make it known that it wishes to receive remote notifications by calling: .. code:: xojo NotificationCenter.RegisterForRemoteNotifications This method asynchronously contacts Apple's Push Notification service (APNs) servers in order to get a globally unique identifier for your app on this particular device. If successful, the RemoteRegistrationSucceeded event will be raised and provide you with a token (a hex-encoded string) for use when you need to send a message to the device. You should immediately send this token to a server and store it with other user relevant data. This token will change if the user reinstalls your app so you should request this information every time the app is launched. For more information about sending remote notifications see `Apple's documentation on the subject `_. .. _/topics/ios/creating_notifications_on_the_user's_mobile_device/using_xojo_cloud_to_send_remote_notifications: Using Xojo Cloud to send remote notifications ********************************************* Remote notifications are sent from a server to devices. This is typically done by using a service that specializes in sending remote notifications. You will need to: 1. Provide them with your Apple Push Services certificate so that they can send notifications on your behalf. #. Learn whatever complex API they provide to send remote notifications. #. Budget for the cost because while they all provide some number of remote notifications for free, they charge for them once you exceed that number. `Xojo Cloud `_ provides a simple solution to all of this. You get a `Xojo Cloud `_ server (or use the one you already have), upload your Apple Push Services certificate through your :doc:`Xojo Cloud control panel`, and you're ready to go. Xojo already has an easy to use :doc:`remote notifications API` built-in to it designed to work with `Xojo Cloud `_ servers. Unlike other services, since your `Xojo Cloud `_ server is under your control, you are always in control of your Apple Push Services certificate. This is important because if someone obtained your certificate, they could send notifications that look like they are coming from you. Finally, there's no need to wonder about the cost as a `Xojo Cloud `_ server allows for unlimited remote notifications. .. _/topics/ios/creating_notifications_on_the_user's_mobile_device/testing_remote_notifications: Testing remote notifications ---------------------------- While you can't send remote notifications to the iOS simulator (Requires Xcode 11.4 or higher) for testing, you drag a remote notification packet directly into the simulator. Here's an example: .. code:: json { "Simulator Target Bundle": "your.bundle.identifier.here", "aps": { "alert": { "title" : "Chicken Avocado Taco Platter", "subtitle" : "All New Flavors!", "body" : "Special - Today Only $4.99" } } } If you would like to test sending remote notifications to a physical device, we suggest using this utility: https://github.com/onmyway133/PushNotifications/releases .. _/topics/ios/sending_and_receiving_mobile_notifications/receiving_notifications: Receiving notifications ----------------------- How your app receives a notification depends upon whether the notification is local or remote. Receiving local notifications ***************************** The NotificationReceived event is raised when your app receives a local notification while in the foreground. If you wish to deliver the notification directly to the user yourself, this is the time to do it. If you would rather have the system deliver it, you will need to return a PresentationMode value to tell the system how it should notify the user that they have received a notification. If you're not sure, return the All value. .. code:: xojo Return MobileNotifications.PresentationModes.All If your app does not display the notification itself or return a value from this event, the user will never know the notification was received. .. note:: Your app may be in the background when a notification is received. Responding to the user when they acknowledge a notification ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When the notification is presented to the user, the ProcessUserResponse event is raised. This is where your app can act upon the user's response to the notification. By default, your app will only know that the user tapped on the notification. Categories allow users to respond directly to a delivered notification through buttons and text fields which can be created using a :doc:`NotificationResponseCategory`. In this example, a category created and named "REPLY". A text field with a Send button and a Cancel button are then added to it. This will allow the user to enter text and then press the Send or Cancel buttons: .. code:: xojo Var cat As New NotificationResponseCategory("REPLY") 'Create a text field with a action button caption, a hint, a send button caption and the category identifier Var replyTextField As New NotificationResponseTextField("Reply", "Please type your reply", "Send", "REPLY") cat.actions.Add(replyTextField) 'Add a button with the caption Cancel and the identifier cat.actions.Add(New NotificationResponseButton("Cancel", "REPLY")) 'Add the category to the NotificationCenter so it can be used when sending notifications NotificationCenter.AddResponseCategory(cat) Once you have this category defined, you can use the "REPLY" category when sending local notifications. Receiving remote notifications ****************************** When your app receives a remote notification, the BackgroundNotificationReceived event is raised. If your application is not running when the notification is received, your app will be launched in the background and given approximately 30 seconds to retrieve whatever information it needs and must then return a value indicating if it was successful or not. .. warning:: During this event, your code should spend very little time in a tight loop as it will make the user's device appear unresponsive for an unknown reason. Other events ------------ .. _/topics/ios/creating_notifications_on_the_user's_mobile_device/opennotificationsettings(notification_as_notification): OpenNotificationSettings(notification as Notification) ****************************************************** This event is raised if you have specified that your app has its own notifications settings and the user clicks the app's notification settings link in the system notification settings for your app. .. _/topics/ios/creating_notifications_on_the_user's_mobile_device/see_also: .. seealso:: :doc:`MobileNotifications`, :doc:`CalendarNotification`, :doc:`LocationNotification`, :doc:`Notification`, :doc:`NotificationContent`, :doc:`NotificationSettings`, :doc:`NotificationException`, :doc:`NotificationResponseCategory`, :doc:`NotificationSettings`, :doc:`RemoteNotification`, and :doc:`TimeIntervalNotification` classes. ======================================== Loading 3rd Party Frameworks in Xojo iOS ======================================== Did you know that it's possible to load and use 3rd Party Frameworks in your Xojo iOS projects? There's quite a number of good projects out there, many of which are on sites like GitHub and freely available for use in your projects. If you're familiar with Declares in iOS, loading a 3rd Party framework requires just a couple of extra lines of code and a CopyFilesStep. Last year at XDC 2018, Jérémie Leroy talked about making sure your screenshots mimicked the Apple method so that the date was always Jun 9th, the time was always 9:41 AM, the battery always shows as full and the WiFi strength always shows full. It got me thinking that it might be handy to be able to make the simulator always show those values when you do a debug run so that you don't need to think about it when you are ready to start taking screenshots and movies of your app. One way to do that is to build & run project like `SimulatorStatusMagic `_ on the simulator before running your project, but it would be more useful if it was automatic. Build the Framework ------------------- First of all, go to the SimulatorStatusMagic Github page and clone or download the project. Just like Xojo iOS development, you'll need Xcode for this step. After you've downloaded the project, double-click on the SimulatorStatusMagic.xcodeproj file to open it in Xcode. When the project opens, change the target to SimulatorStatusMagiciOS and build it by either pressing :kbd:`CMD B` or pressing the Play button. Once the build is complete, go to the project navigator, scroll down to the bottom and open the Products group to reveal the built parts of this project. The only item we're concerned with here is the file named SimulatorStatusMagiciOS.framework. Right-click on it and select Show in Finder. Build the Project ----------------- Let's start off by making sure the framework will be available to your project at runtime. 1. Create a folder on your drive named SimulatorStatusMagic and copy the framework file that you just built into it. 2. Launch Xojo and create a new iOS project. Save the project into that folder as well. 3. To make sure the framework will be available to your project when debugging, go to the iOS target in the Xojo navigator, right-click and select Build Step > Copy Files. Make sure the step is between the Build step and the Sign step. 4. Drag the SimulatorStatusMagiciOS.framework file from the Finder into the Copy Files Step. 5. In the Xojo inspector, set Applies To to “Debug” and Destination to “App Parent Folder”. Now let's make it so you can access the framework! We're going to add this code in the App.Open event so that the status bar gets set up before you do anything else. Getting the framework to load is fairly straightforward: .. code:: xojo #If DebugBuild Declare Function dlopen Lib "/usr/lib/libSystem.dylib" (name As CString, flags As Int32) As Ptr Call dlopen("@executable_path/SimulatorStatusMagiciOS.framework/SimulatorStatusMagiciOS", 1 Or 8) #EndIf That magic string “@executable_path” gets translated into “wherever the current application currently is” at runtime. Now this just loads the framework. We still need to hook up the methods and properties. Note: If you're working with a framework that “activates itself” like the one that comes with `Reveal `_ (an excellent tool for tracking down iOS layout issues), this is as far as you would need to go. To figure out what we need to do next, we need to look at the documentation and header files that come with SimulatorStatusMagic. Go back to Xcode and look at the navigator. What we're looking for is a header file (with a .h extension) which has the definitions of each of the methods and properties which are available in this framework. The only file that meets this criteria (and the one that all the others seem to point to) is named SDStatusBarManager.h. There's a bit of functionality available here, but the framework is set up to use the Apple recommended settings by default, so all we're going to do here is call the enableOverrides method to activate it. Go back to Xojo and update App.Open event: .. code:: xojo #If DebugBuild Declare Function dlopen Lib "/usr/lib/libSystem.dylib" (name As CString, flags As Int32) As Ptr Call dlopen("@executable_path/SimulatorStatusMagiciOS.framework/SimulatorStatusMagiciOS", 1 Or 8) ' Start by creating a pointer to the SDStatusBarManager class itself Declare Function NSClassFromString Lib "Foundation" (clsName As CFStringRef) As Ptr Var smclass As Ptr = NSClassFromString("SDStatusBarManager") ' Next, we need a pointer to the shared instance of the SDStatusBarManager class Declare Function sharedInstance Lib "Foundation" Selector "sharedInstance" (clsref As Ptr) As Ptr Var smclassShared As Ptr = sharedInstance(smclass) ' Last, turn on the overrides ' NOTE: We're specifically NOT using the actual lib name here. ' This is just to satisfy the Xojo linker. The correct framework will be used at runtime. Declare Sub enableOverrides Lib "Foundation" Selector "enableOverrides" (obj As Ptr) enableOverrides(smclassShared) #EndIf Now if you run the project, when it opens the status bar automatically changes to the recommended defaults! .. Note:: These changes persist until the simulator device that you are running on is completely erased. If you need to disable it, you can do that with the disableOverrides method. After posting this article, it was brought to my attention that I'd forgotten to show you how to sign the frameworks so you can build for the app store. To do that, you'll need to add a script build step to your project, just after the Copy Files step you added above. The step can probably be set to Applies To: Release. The code should look like this: .. code:: xojo ' Replace the text in the signingIdentity property with your own Var signingIdentity As String = "iPhone Distribution: Your Company (XXXXXXXXXX)" Var code As Integer Var cmd As String Var result As String Var builtApp As String builtApp = CurrentBuildLocation + "/" + CurrentBuildAppName.ReplaceAll(" ", "\ ") ' Get a list of all frameworks, one per line cmd = "ls -d1 " + builtApp + "/*.framework" result = DoShellCommand(cmd, 30, code).Trim ' If there was no error (like none being there), sign each one If code = 0 Then Var frameworks() As String = result.Split(Chr(13)) For i As Integer = 0 To frameworks.LastIndex frameworks(i) = frameworks(i).ReplaceAll(" ", "\ ") cmd = "/usr/bin/codesign -fs """ + signingIdentity + """ " + frameworks(i) result = DoShellCommand(cmd, 30, code) ' If the sign fails, print the error and stop the build If code <> 0 Then Print(result) CancelBuild End If Next i End If Linux ===== .. toctree:: :maxdepth: 1 :name: sec-linux Running apps on Linux Getting started --------------- * :doc:`QuickStart` * :doc:`Tutorial` .. seealso:: :doc:`User interface topics` ===================== Running apps on Linux ===================== Unlike Windows and macOS it can sometimes be tricky for non-Linux users to ensure that Linux is properly configured to run Xojo apps. Below are some tips to help you get your Xojo apps running on Linux. .. _/topics/linux/running_apps_on_linux/start_with_a_supported_version_of_linux: Start with a supported version of Linux --------------------------------------- Refer to the :doc:`System Requirements` to see which Linux distributions are supported. Unless you are a Linux expert you should stick with one of the supported distributions. Although other distributions definitely work, they may require significant configuration and installation of libraries and other things. The most common library that needs installation is libunwind8. :ref:`Required Linux Libraries` .. _/topics/linux/running_apps_on_linux/32-bit_or_64-bit?: 32-bit or 64-bit? ----------------- Is your Linux distribution 64-bit or 32-bit? Did you build your app as 32-bit or 64-bit? For best results you should build 32-bit apps for 32-bit Linux distributions and 64-bit apps for 64-bit Linux distributions. It is possible to get 32-bit apps to run on 64-bit versions of Linux but you again may need to install additional libraries (as noted in :ref:`System Requirements`). .. _/topics/linux/running_apps_on_linux/start_from_the_terminal: Start from the terminal ----------------------- If your app does not start or seems like it might be crashing, try start it from the Terminal even if it is not a Console app. On Linux the Terminal will display additional information and errors that may help with troubleshooting. To start an app from the Terminal, go to its folder and prefix the app name with "./": .. code:: xojo ./MyApp If there are spaces in the name you'll have to use quotes or escape them: .. code:: xojo ./"My App" ./My\\ App" .. _/topics/linux/running_apps_on_linux/handle_exceptions: Handle Exceptions ----------------- If your app has unhandled exceptions, fix their causes. For example, files on Linux are often in different locations than you'll expect on Windows or Mac. So you should make use of :doc:`SpecialFolder`, :doc:`#If...#Endif`, etc. Localizing your apps ==================== .. toctree:: :maxdepth: 1 :name: sec-localizing_your_apps Introduction to app localization Using the Lingua App to localize your app ================================ Introduction to app localization ================================ Localization is the process of making your app display appropriately for a specific country or region. For example, this may involve: * Displaying text in a different language * Formatting numbers using alternative thousands and decimal separators * Formatting dates using an alternative date pattern * Formatting currency using a specific currency symbol .. _/topics/localizing_your_apps/introduction_to_app_localization/text_localization_with_localizable_strings: Text localization with localizable strings ------------------------------------------ You localize text for your apps using localizable strings created with the :doc:`Constant Editor`. These are a special form of a constant that is added to a project item such as a module, window, web page or class. Only constants of type String or Text can be marked as localizable. When the constant is localizable, it can have different values to use for localization. To identify a constant as localizable, create a String or Text constant and then enable the Localizable switch in the Inspector. A recommended approach is to create a separate module for these localizable strings, but you can put them anywhere that is publicly accessible. Localized strings are not actually constants, so they cannot be used in places where a standard constant can be used, such as :ref:`method parameter default values` or with :doc:`conditional compilation`. You can enter different localized values for the string based on platform and language. Do this using the :doc:`Constant Editor`. Use the “+” and “-” buttons to add or remove a specific localization. You can choose the Platform and the Language for which to specify a value. When the user runs an app, the language specified on their system is used to look up the localized value of the string to use. Localized Strings created in this manner can then be used as the Text or Caption of controls so that the control displays the appropriate localized value. To use a localized string as the Text or Caption, you prefix it with the “#” character when entering it in the appropriate property. If you have a module called LocalizedStrings and have a protected localized string called kWelcomeMessage, then you add it to a Label as the Text property like this: .. code:: xojo #LocalizedStrings.kWelcomeMessage You can also refer to the localized string in your code just as you would a constant, but since this is a special type of constant, you can also specify which localization you want to use. You do this by supplying the language code (usually two-character) and optionally the region code as a parameter to the localized string name. For example, if you had a localized string called *kHello* that had localizations for both French (“Bonjour”) and English (“Hello”), you could force the French version to be displayed by doing this: .. code:: xojo Var s As String s = kHello("fr") ' s = "Bonjour" s = kHello("en") ' s = "Hello" s = kHello("en_UK") ' s = "Welcome" And you could grab the default value like this, although you're better off getting the specific language value you want: .. code:: xojo s = kHello("default") .. _/topics/localizing_your_apps/introduction_to_app_localization/building_the_localized_app: Building the localized app -------------------------- On the Shared Build Settings, there is a Language property in the Build section of the Inspector. This property determines the language that is used by any localized strings that have “Default” as the language. It is important that you select a specific language in this build setting. If you also leave this setting at “Default”, you will run into confusion if the project file is shared with people that do not have the same system language as you. For example, if you leave both the localized string language and build language as “Default” then “Default” becomes “English” for users that build with an English system and becomes “French” for users that build with a French system. To prevent this confusion, always at least choose a specific language in the Build setting. Alternatively, don't use “Default” for your localized strings and instead always choose the exact language for them. .. _/topics/localizing_your_apps/introduction_to_app_localization/lingua: Lingua ------ If you have a lot of text to localize, it can get tedious to enter all the values using the Constant Editor. The :doc:`Lingua` app is used to simplify the localization process. With Lingua you can localize your app strings outside of the project by using the localized strings you have already created. Refer to the :doc:`Lingua` topic to learn more. .. _/topics/localizing_your_apps/introduction_to_app_localization/date_and_number_localization: Date and number localization ---------------------------- You can localize how dates and numbers are displayed by using the :doc:`Locale` class. The shared Current method returns the locale that is specified in the system or device settings. You can then use this with the ToString method on :doc:`DateTime`, Double and Integer to display values formatted properly for the locale. This code (in a Label's Opening event handler), displays the current date: .. code:: xojo Me.Text = DateTime.Now.ToString(Locale.Current, DateTime.FormatStyles.Long, DateTime.FormatStyles.None) On an US English system, this displays: January 9, 2017 On a French system, this displays: 9 janvier 2017 This code (also in a Label's Opening event handler), displays a formatted number: .. code:: xojo Var num As Double = 1234.56 Me.Text = num.ToString(Locale.Current, "#,###.##") On an US English system, the number displays like this: 1,234.56 On a French system, it displays like this: 1 234,56 .. _/topics/localizing_your_apps/introduction_to_app_localization/localizing_web_apps: Localizing web apps ------------------- Web apps can also localize their text using dynamic constants, but the language that is displayed is determined by the language setting in the browser being used to access the web app. More specifically, it is using the constant value that is most appropriate for the HTTP header language setting of the current session. There are also several properties of the WebSession that are helpful for localization: * LanguageCode * LanguageRightToLeft In addition, if you select Session in the Navigator and then open the Inspector, you can directly localize the messages that appear in dialogs when the connection is interrupted disconnected or when the user must confirm they want to leave the session. You can also use constants instead to provide localization in multiple languages. Finally, WebApplication has an HTMLHeader property for which you can provide a value using your own dynamic constants. .. _/topics/localizing_your_apps/introduction_to_app_localization/dates_and_numbers: Dates and numbers ***************** In desktop apps, you can use :doc:`Locale` to get the user's locale for formatting dates and numbers. However, in a web app this value returns the locale used by the web server rather than the locale of the current user session. To display dates formatted in the locale of the user session, you need to get the :ref:`LanguageCode` from :doc:`WebSession` and use that to create a locale that you can then use to display the date. This code (in a WebLabel Shown event handler) gets the language code for the user session and then uses it to display the current date: .. code:: xojo Var langCode As String = Session.LanguageCode.DefineEncoding(Encodings.UTF8) Var locale As New Locale(langCode) Me.Text = DateTime.Now.ToString(locale, DateTime.FormatStyles.Long, DateTime.FormatStyles.None) When the browser is set to English, this displays: January 9, 2017 When it is set to French, this displays: 9 janvier 2017 Use the same technique with number formatting. This code (in a WebLabel Shown event handler) gets the language code for the user session and then uses it to display a formatted number: .. code:: xojo Var langCode As String = Session.LanguageCode.DefineEncoding(Encodings.UTF8) Var locale As New Locale(langCode) Var num As Double = 1234.56 Me.Text = num.ToString(locale, "#,###.##") When the browser is set to English, the number displays like this: 1,234.56 When it is set to French, it displays like this: 1 234,56 To get locale information that is more specific than just the language, you should check the value of the "Accept-Language" HTTP header: .. code:: xojo Var langHeader As String = Session.Header("Accept-Language") Which returns both the language and region code in a format like this: en-US. Note that there may be more than one value returned in this header so you may need to parse it to ensure you get the value you actually want. .. _/topics/localizing_your_apps/introduction_to_app_localization/changing_the_language: Changing the language --------------------- .. _/topics/localizing_your_apps/introduction_to_app_localization/android: `Android` ******** .. image:: https://documentation.xojo.com/topics/localizing_your_apps/images/introduction_to_app_localization_android_language_settings.png :scale: 40% :align: center To change the language on `Android`, open the Settings app and select System > Language & input > Languages. On this screen you can add languages. `Android` allows you to set your preferred language for each application. To do this, open the Settings app, then select System > Language & input > App Languages then select the app whose language you wish to change. .. _/topics/localizing_your_apps/introduction_to_app_localization/ios: iOS *** .. image:: https://documentation.xojo.com/topics/localizing_your_apps/images/introduction_to_app_localization_ios_language_settings.png :scale: 25% :align: center To change the language on iOS, open the Settings app and select General > Language & Region. On this screen you can change the preferred language order and region formats. .. _/topics/localizing_your_apps/introduction_to_app_localization/linux: Linux ***** .. image:: https://documentation.xojo.com/topics/localizing_your_apps/images/introduction_to_app_localization_linux_mint_18_language_settings.png The specific way to change the language and region varies by Linux distribution. On Linux Mint 18, you can make the changes by going to Preferences > Languages and making the appropriate changes. .. _/topics/localizing_your_apps/introduction_to_app_localization/mac: macOS ***** .. image:: https://documentation.xojo.com/topics/localizing_your_apps/images/introduction_to_app_localization_mac_language_settings.png To change the language on Mac, open System Preferences and select "Languages & Region" (top row). In the list of Preferred languages on the left, you can drag the language you want to be the primary language to the top. .. _/topics/localizing_your_apps/introduction_to_app_localization/windows: Windows ******* .. image:: https://documentation.xojo.com/topics/localizing_your_apps/images/introduction_to_app_localization_windows_10_language_setting.png To use a different language on Windows, you need to change the Language setting on the Format tab of the Region control panel. Changing the Language on the Keyboard and Languages tab will not affect your applications. For Windows 10/11: * Open Settings * Click Time & Language * In "Related Settings", click "Additional date, time & regional settings" * In the Region section, click "Change location" * Click the Formats tab to display the drop-down where you can change the language .. _/topics/localizing_your_apps/introduction_to_app_localization/web_browsers: Web browsers ************ To see a web app in a different language, you'll need to change the settings for the browser that is connecting to the web app. This varies by browser. .. csv-table:: :header: "Browser", "Language Setting" :widths: auto "Edge","Uses the system settings in Settings / Control Panel." "Internet Explorer","Go to Internet Options. In the General tab click the Languages button to display the screen to set the language." "Safari","Uses the system settings in System Preferences." "Chrome","Go to Settings and then click Advanced settings. Look for the Languages section." "Firefox","Go to Preferences, select Content and look for the Languages section." .. _/topics/localizing_your_apps/introduction_to_app_localization/see_also: .. seealso:: :doc:`Using the Lingua App to localize your app`, :doc:`Constant Editor` topics ========================================= Using the Lingua app to localize your app ========================================= .. image:: https://documentation.xojo.com/topics/localizing_your_apps/images/using_the_lingua_app_to_localize_your_app_export_localized_values.png If you have a lot of text to localize, it can get tedious to enter all the values using the Constant Editor. The Lingua app is used to simplify the localization process. With Lingua you can localize your application strings outside of the project by using the localized strings you have already created. The Lingua app is located in the Extras/Lingua folder in your Xojo installation folder. To use Lingua you first have to make sure all the appropriate text in your project has been defined as localized strings (refer to :doc:`Localization` for information on how to do this). |images/using_the_lingua_app_to_localize_your_app_lingua_welcome.png| .. |images/using_the_lingua_app_to_localize_your_app_lingua_welcome.png| image:: images/using_the_lingua_app_to_localize_your_app_lingua_welcome.png Once your project text is ready, in Xojo choose File > Export Localizable Values. A dialog box appears, asking you to select the language you want to localize to. Select the language you are localizing to and click Export. (Also make sure that the Language in the Build Settings is set to either Default or any other language that you want to use as the originating language in the export.) When you click Export, you'll be prompted to choose a location and name for the Linga file (xojo_locale) to create. This file can be opened by Lingua. Start Lingua, click the Open button on the Welcome window and choose the file you just exported. The main Lingua window opens, showing a list of all the localized strings that were exported from your project. The values are grayed out when there is no localized text yet. If there are any different values specific to Windows, Linux, or macOS, there will be an icon to the far right of the string in the list. Like the strings, the icons are grayed out if the value hasn't been localized yet. .. image:: https://documentation.xojo.com/topics/localizing_your_apps/images/using_the_lingua_app_to_localize_your_app_lingua_main_window.png To localize a string, select it in the list. The original value is displayed in its entirety in the upper right text rea, and in you can type the translated text in the lower right text area. To add a value specific to a platform, expand the string in the list, and select the individual platform to edit it. To test the strings in your app, in Lingua choose File > Export to Application. Lingua presents an open-file dialog box. Select the target app and click Open. When the import is complete, switch back to Xojo and debug the application normally. When finished localizing, you can save the file from within Lingua and then import it back into your Xojo project by dragging it into a project or choosing File > Import. .. _/topics/localizing_your_apps/using_the_lingua_app_to_localize_your_app/using_xojoscript_for_custom_text_handling: Using XojoScript for custom text handling ----------------------------------------- You can create your own XojoScripts that can be used to process text in the Default Value and Translation text areas. Your XojoScripts can use the standard XojoScript language with these additional commands: * ShowDialog: Works the same as ShowDialog in IDE Scripting. * DefaultText As String: Used to get or set the text in the Default Value text area. * TranslatedText As String: Use to get or set the text in the Translation text area. As a simple example, this XojoScript applies Trim to the text in the Translation text area: .. code:: xojo ' Apply Trim to the translated text TranslatedText = TranslatedText.Trim To make your own scripts available, create them as text files with the extension "lingua_script" and save them in the Scripts folder for Lingua. To get to the Scripts folder, in Lingua go to the Scripts menu and choose "Show Scripts Folder". The scripts in that folder appear in the Scripts menu. Select a script to run it against the currently display localizable string. .. _/topics/localizing_your_apps/using_the_lingua_app_to_localize_your_app/see_also: .. seealso:: * :doc:`Introduction to app localization`, :doc:`Constant Editor` topics macOS ===== .. toctree:: :maxdepth: 1 :name: sec-macos Accessing the Keychain Advanced Apple Events Using AppleScripts in your app Getting started --------------- * :doc:`QuickStart` * :doc:`Tutorial` .. seealso:: :doc:`User interface topics` ====================== Accessing the Keychain ====================== The Keychain is a Mac feature used for storing account passwords for applications. By taking advantage of the keychain, your users do not have to type their password if their keychain is unlocked on their system. Use the Keychain class to access Mac Keychains for your applications. The classes are: :doc:`Keychain`, :doc:`KeychainItem` and :doc:`KeychainException`. You should always ask for permission from the user before storing anything in a keychain. You use the System module to get a reference to the default Keychain. You use the KeychainItem class to create, update or find items in the Keychain. If you have more than one Keychain, then you can use the KeyChain constructor to access specific key chains by number. This code stores a password in the default Keychain: .. code:: xojo Var newItem As KeychainItem If System.KeychainCount > 0 Then newItem = New KeychainItem ' Indicate the name of the application newItem.ServiceName = "MyApplication" ' Assign a password to the item System.Keychain.AddPassword(newItem, "SecretPassword") Else System.Beep MessageBox("You don't have a key chain.") End If Exception e As KeychainException MessageBox("Keychain error: " + e.Message) And this code retrieves the password: .. code:: xojo Var itemToFind As KeychainItem Var password As String itemToFind = New KeychainItem ' Name to find ItemToFind.ServiceName = "MyApplication" ' Get the password password = System.Keychain.FindPassword(itemToFind) MessageBox("Password: " + password) Exception e As KeychainException MessageBox("Keychain error: " + e.Message) .. _/topics/macos/accessing_the_keychain/see_also: .. seealso:: :doc:`Keychain`, :doc:`KeychainItem` :doc:`KeychainException` classes ===================== Advanced Apple Events ===================== Newer versions of macOS may require additional permissions or entitlements in order to use Apple Events. .. _/topics/macos/advanced_apple_events/managing_errors: Managing errors --------------- The boolean value returned by the AppleEvent's :ref:`Send` command only informs you that the event has been received by the target application. However, your command may have failed, e.g. if you refer to a non-existent object. Errors in AppleEvents are stored as a parameter in the reply AppleEvent. The error number is stored as 'errn' and the optional error message as 'errm' or 'errs'. However, only the direct parameter '----' can be retrieved from a reply in your code, so you may need to use :doc:`Declare` statement. .. code:: xojo ' TITLE: In a module, create the following methods. Sub SizeAndTypeOfParam(Extends ae As AppleEvent, param As String, inReply As Boolean, ByRef size As Integer, ByRef type As String) ' Get the size and type of one parameter. Set inReply to true if you want to access the reply AppleEvent Declare Function AESizeOfParam Lib "Carbon" (evnt As Integer, AEKeyword As OSType, ByRef oDesc As OSType, ByRef oSize As Integer) As UInt16 Var err As Integer Var oDesc As OSType Var oSize As Integer If inReply Then err = AESizeOfParam(ae.replyptr, param, oDesc, oSize) Else err = AESizeOfParam(ae.ptr, param, oDesc, oSize) End If If err <> 0 Then ' We get a -1701 error if there is no parameter with this keyword type = "" size = 0 Else type = oDesc size = oSize End If End Sub Function ReplyRawData(Extends ae As AppleEvent, param As String, ByRef type As String) As MemoryBlock ' Get a binary data param in the reply AppleEvent Declare Function AEGetParamPtr Lib "Carbon" (AEPtr As Integer, AEKeyword As OSType, inType As OSType, ByRef outType As OSType, data As Ptr, maxSize As Integer, ByRef actSize As Integer) As UInt16 Var data As MemoryBlock Var err As Integer Var oType As OSType Var aSize As Integer Var paramSize As Integer Var paramType As String ae.SizeAndTypeOfParam(param, True, paramSize, paramType) If paramType.IsEmpty Then ' No parameter with this key Return Nil End If data = New MemoryBlock(paramSize) ' Get the data err = AEGetParamPtr(ae.ReplyPtr, param, type, oType, data, data.Size, aSize) If err <> 0 Then Return Nil Else ' Update the actual type and return the data type = oType Return data.StringValue(0, aSize) End If End Function Sub ReplyRawData(Extends ae As AppleEvent, param As String, type As String, Assigns data As MemoryBlock) ' Add some binary data as a reply AppleEvent parameter Declare Function AEPutParamPtr Lib "Carbon" (AEPtr As Integer, AEKey As OSType, dType As OSType, data As Ptr, dsize As Integer) As UInt16 Var err As Integer err = AEPutParamPtr(ae.Replyptr, param, type, data, data.size) End Sub You can now get or set any parameter in the reply AppleEvent and use it to get or set an error. As an example, the following code should return an error: .. code:: xojo ' TITLE: Sending a bad AppleEvent and getting the error number Var ae As AppleEvent Var o As AppleEventObjectSpecifier ' We will try to activate the 100th window of the Finder. It is very likely to raise an error. ae = New AppleEvent("misc", "actv", "com.apple.finder") o = GetIndexedObjectDescriptor("cwin", Nil, 100) ae.ObjectSpecifierParam("----") = o If Not ae.Send Then MessageBox("Couldn't send AppleEvent") Return End If Var type As String = "long" Var data As MemoryBlock ' Get the 'errn' parameter of the reply. It should be -1728 in such case (Object not found). data = ae.ReplyRawData("errn", type) If data <> Nil Then ' There is an error number parameter MessageBox("Finder returned error " + data.Int32Value(0).ToString) End If .. _/topics/macos/advanced_apple_events/getting_a_textual_representation: Getting a textual representation -------------------------------- It is often useful to get the textual representation of an AppleEvent, because such string contains **all** the attributes, parameters, types and data. The following method takes an AppleEvent and a boolean which indicates if you want the description of the AppleEvent itself or its reply. It returns a string. .. code:: xojo //TITLE: This method must be stored in a module Function PrintDesc(Extends ae As AppleEvent, getReply As Boolean = False) As String Soft Declare Function AEPrintDescToHandle Lib "Carbon" (theEvent As Integer, hdl As Ptr) As Integer Soft Declare Sub DisposeHandle Lib "Carbon" (hdl As Ptr) Var myHandle As MemoryBlock Var err As Integer Var mb As MemoryBlock Var result As String ' Will hold the pointer to the data myHandle = New MemoryBlock(4) If getReply Then err = AEPrintDescToHandle(ae.ReplyPtr, myHandle) Else err = AEPrintDescToHandle(ae.Ptr, myHandle) End If If err <> 0 Then Return "" ' Check for error ' Get the data mb = myHandle.Ptr(0) mb = mb.Ptr(0) result = mb.CString(0) DisposeHandle myHandle.Ptr(0) ' We must free the handle to get memory back Return result End Function Considering the example above "Sending a bad AppleEvent and getting the error number", you can use the following code to get the AppleEvent's descriptions: .. code:: xojo Var s, t As String s = ae.PrintDesc ' returns 'misc'\\'actv'{ '----':'obj '{ 'want':'cwin', 'from':'null'(), 'form':'indx', 'seld':100 } } t = ae.PrintDesc(True) ' Pass "true" to get the reply's description ' returns 'aevt'\\'ansr'{ 'erob':'obj '{ 'want':'cwin', 'from':'null'(), 'form':'indx', 'seld':100 }, 'errn':-1728 } For *s*, 'misc'\\'actv' is the command. It is immediately followed by the parameters between curly brackets. The only parameter is '----' of type 'obj ' (note the extra space), i.e. an object specifier. It is composed of the data you used to create it: * 'want' is the class you asked for; here 'cwin' * 'from' is the parent object. As we passed Nil, it is represented as 'null'() in the textual representation * 'form' is the form of the request. As we asked the window by its index, the form is of type 'indx'. * 'seld' is the selector descriptor, i.e. the value(s) to be used according to the form of the request. Here, it is equal to the integer value 100 since we asked for the window whose index is 100. For *t*, 'aevt'\\'ansr' is the signature for any AppleEvent reply ('ansr' stands for answer). There are 2 parameters: * 'erob', of type 'obj ', contains the object descriptor which caused the error ('erob' stand for ERror OBject). * 'errn': an integer parameter of value -1728. .. _/topics/macos/advanced_apple_events/restrictions: Restrictions ------------ In order to use AppleEvents on newer versions of macOS (Mojave and later) you may need to include the NSAppleEventsUsageDescription key in your :doc:`plist` file. More information here: * https://www.felix-schwarz.org/blog/2018/08/new-apple-event-apis-in-macos-mojave * https://indiestack.com/2018/08/apple-events-usage-description/ * https://developer.apple.com/documentation/security/nsappleeventsusagedescription .. _/topics/macos/advanced_apple_events/see_also: .. seealso:: :doc:`AppleEvent` class, :doc:`Using AppleScripts in your app` topic ============================== Using AppleScripts in your app ============================== AppleScript is the macOS system-level scripting language than can be used to control applications. You can create AppleScripts using the `AppleScript Editor `_. In order for Xojo to run an AppleScript, the script must be saved as a “compiled” script. You can do this using the Script Editor and selecting Compile on the toolbar and saving the script. Now you can use the AppleScript with Xojo by dragging the compiled script onto the Navigator. The script appears in the Navigator with a script icon next to it. This item is added to Xojo as an “external” item and includes it with the application when you build. To run the AppleScript, just call it by the name it has in the Navigator. Here is a simple script that you can compile and drag into your project to launch the Music app: .. code:: xojo tell application "Music" launch end tell .. _/topics/macos/using_applescripts_in_your_app/passing_parameters: Passing parameters ------------------ To pass parameters, add an “on run” handler to contain your script and specify the parameters using curly brackets: .. code:: xojo on run {value1, value2} ' your script code goes here end run Xojo Integers passed to AppleScripts are sent as Integer values and are treated as Integers by AppleScript. All other Xojo types (including other numeric types such as Int8 and Double) are sent as Strings and are treated as Strings by AppleScript. .. _/topics/macos/using_applescripts_in_your_app/returning_values: Returning values ---------------- You can also return values from an AppleScript back to your Xojo code by using the return command in the script. This example adds two values and returns the result: .. code:: xojo on run {value1, value2} return value1 + value2 end run AppleScript does not use types like Xojo does. All values are returned as Strings to Xojo. .. _/topics/macos/using_applescripts_in_your_app/calling_applescripts_from_xojo: Calling AppleScripts from Xojo ------------------------------ AppleScripts are called just like built-in global methods and functions. You use the name it has in the Navigator. If it has parameters, you supply them after the name as you would any other method that has parameters. Scripts that return values can be assigned to a Xojo variable. This command calls the above script that adds two values: .. code:: xojo Var sum As String sum = Add(5, 10) Note: If there is an error in your AppleScript, it is logged to the Messages panel at runtime. .. _/topics/macos/using_applescripts_in_your_app/restrictions: Restrictions ------------ In order to use AppleEvents on newer versions of macOS (Mojave and later) you may need to include the NSAppleEventsUsageDescription key in your :doc:`plist` file. More information here: * https://www.felix-schwarz.org/blog/2018/08/new-apple-event-apis-in-macos-mojave * https://indiestack.com/2018/08/apple-events-usage-description/ * https://developer.apple.com/documentation/security/nsappleeventsusagedescription .. _/topics/macos/using_applescripts_in_your_app/appleevents: AppleEvents ----------- AppleEvents are a way for macOS apps to communicate with one another. An AppleEvent is a self-contained block of data which consists of a sequence of key-type-value data (called an AppleEvent Descriptor, or AEDesc). Each descriptor can contain other descriptors as an ordered array or as a mixture of keyed data. .. _/topics/macos/using_applescripts_in_your_app/see_also: .. seealso:: * :doc:`AppleEvent` class, :doc:`Advanced Apple Events` topic Migrating from other development tools ====================================== .. toctree:: :maxdepth: 1 :name: sec-migrating_from_other_development_tools Migrating from 4D Migrating from FileMaker Migrating from Microsoft Access Migrating from PowerBASIC Migrating from Visual Basic Migrating from Visual FoxPro ================= Migrating from 4D ================= 4D is a database application development environment. It is often used to create specialized in-house database applications. If you have an 4D application and are running into limitations, Xojo is a great choice to take your application to the next level. .. _/4d.similarities: Similarities ------------ Like 4D, Xojo is an IDE that includes a :doc:`layout editor`, :doc:`code editor`, :doc:`debugger`, compiler, :doc:`object-oriented programming language` and more. Like 4D, Xojo provides a library of user interface controls for :doc:`desktop`, :doc:`mobile` and :doc:`web` as well as an :doc:`extensive framework of functions` that abstracts you from the details of the underlying operating systems. .. _/4d.differences: Differences ----------- Unlike 4D, Xojo layouts (:doc:`windows`, :doc:`webpages` and :doc:`mobile screens`) are not tied to database tables. They are independent objects. Because of this, data from rows are not loaded automatically into the user interface controls. While you can write code easily enough to do this, a better option is to use :doc:`DBKit` which is a free add-on for Xojo that can load data into controls without you having to write code. Unlike 4D, Xojo is designed to work equally well with :doc:`MySQL`, :doc:`PostgreSQL`, :doc:`ODBC` and :doc:`SQLite`. In fact, nearly all the code you write to access these databases will be identical regardless of which database engine you choose to use. Unlike 4D, there is no per end-user licensing fee or runtime license of any kind. The purchase of your Xojo developer license allows you to deploy as many applications to as many end users as you like. Unlike 4D, Xojo uses SQL as its exclusive database query language. However, it provides an extensive database API that abstracts you from SQL with the sole exception of queries. .. _/4d.migrating: Migrating --------- The eight steps for migration are as follows: 1. Make a list of any functions upon which your application depends and make sure that those functions are available in Xojo, in a Xojo add-on or can be replaced by you via some other means. 2. Recreate your database schema in the database engine of your choice. 3. Export the data from your existing 4D database and then import into your new database file. There are database tools such as `TablePlus `_ that make this easy. If the data is likely to change before you are finished migrating your application, you will of course have to do this again but it's a good idea to have very good sample data while recreating your project in Xojo. 4. Recreate your layouts in Xojo. This may seem like a huge job (and if you have a lot of layouts, it likely will be) but keep in mind that you spent a long time deciding how the layouts should look. Recreating them doesn't require redesigning them and as a result the process should go much faster. 5. Recreate your logic in Xojo. If you've created classes in 4D you should be able to create similar classes in Xojo. Make sure to start by going through the Xojo :doc:`QuickStart` and :doc:`Tutorials`. Read about :doc:`DBKit` and the `Database` classes such as :doc:`Database`, :doc:`RowSet` and more. If you need help with understanding how things work, check the documentation, example projects (which can be found right in the Xojo IDE by choosing File > New Project then clicking on the Examples tab), ask on our `user forum `_ or `contact us directly `_. 6. You may be able to use the built-in Xojo :doc:`report editor` for some of your reports. Otherwise, printing can be done via the :doc:`Graphics` class. Anything you can draw via the :doc:`Graphics` class can be displayed on the screen or sent to the printer. 7. Initial testing can be done on your development machine. You'll want to make sure to test on all platforms that you intend to support. Xojo's :doc:`Remote Debugger` makes it easy to test on other operating systems. 8. Ship! .. _/4d.forms: Forms/Layouts ------------- Layouts in Xojo work similarly to 4D. Unlike 4D they do not have sections. Creating them is essentially the same as 4D in that you drag and drop to add controls and then use the Inspector pane to configure them. If you need to display a list of rows, the best solution is to use a :doc:`DesktopListBox`, :doc:`WebListBox` or :doc:`iOSMobileTable` or :doc:`AndroidMobileTable` depending upon the type of project you are creating. If your rows are taller and more complex than a `ListBox` allows, there are other options such as using Containers (:doc:`DesktopContainer`, :doc:`MobileContainer` and :doc:`WebContainer` as well as HTMLViewers (:doc:`DesktopHTMLViewer`, :doc:`MobileHTMLViewer`, :doc:`WebHTMLViewer`). .. image:: https://documentation.xojo.com/topics/migrating_from_other_tools/images/layout_editor.png :scale: 25% :align: center 4D to Xojo Project Converter **************************** This app can convert 4D forms into Xojo desktop windows. It's open source. You can download it `here `_. .. _/4d.source_code: Source code ----------- Xojo is event-driven and includes an object-oriented language that is strongly-typed. :doc:`Desktop`, Mobile (:doc:`Android` and :doc:`iOS`) and :doc:`Web` projects will have an Application class (:doc:`DesktopApplication`, :doc:`MobileApplication`, :doc:`WebApplication`) that has an `Opening` event which where your application's code executes first as your app is launched and a `Closing` event that is the last code that executes when the application is shutting down. This `Opening` and `Closing` event pairing also exists for layouts, controls and some other classes. In Xojo the user interface class names, property names, method names and more are generally the same regardless of project type. There are of course some differences as there are features that are unique to a particular project type. Most of the classes that are not user interface-related are supported for all project types and operating systems. This makes it easy to use them across operating systems and even project types. Xojo's backend compiler is `LLVM `_ which is an optimizing compiler. Apple's XCode and many other tools use LLVM. .. _/4d.example_code: Example code ************ This is example code connects to a local SQLite database, finds a row and then updates it. .. code:: xojo Var db As New SQLiteDatabase db.DatabaseFile = New FolderItem("CompanyData.sqlite") Try db.Connect Var rows As RowSet rows = db.SelectSQL("SELECT * FROM Employees WHERE ID=134560") rows.EditRow rows.Column("YearsOfService").IntegerValue = 5 rows.SaveRow db.CommitTransaction Catch error As DatabaseException MessageBox("Error: " + error.Message) db.RollbackTransaction End Try Xojo uses the concept of an :doc:`exception` to deal with error handling. The assumption is that no errors will occur and that should one occur, that is an exception. Code is placed in a :doc:`Try Catch` statement. Should any database error occur on any line inside the statement, the Catch portion will be executed. In most cases, an error is not recoverable and the user will have to start the operation again. In a real application, the db variable would likely be a property in a global location such as the App object that every project has or in a web application the :doc:`Session` class. If this code was instead connecting to a database server, the only change would be that db variable would be set to a difference database class and the additional info need (such as the IP address of the server) would be provided: .. code:: xojo Var db As New PostgreSQLDatabase db.Host = "192.168.1.172" db.Port = 5432 db.DatabaseName = "CompanyData" db.AppName = "MyApp" db.Username = "Charlie" db.Password = "mashie" .. _/4d.protecting_your_database: Protecting Your Database ************************ Xojo uses the concept of :doc:`Prepared Statements to prevent SQL-Injection attacks`. Anytime your code is going to perform a query with data entered by the user, make sure you use Xojo's :ref:`SelectSQL` syntax that allows you provide those values as separate parameters allowing the database engine to make sure they don't have any SQL commands embedded within them. .. _/4d.code_editor: Code editor *********** The Code Editor includes syntax highlighting, autocomplete, syntax help, refactoring tools, documentation access and more. .. image:: https://documentation.xojo.com/topics/migrating_from_other_tools/images/code_editor.png :scale: 25% :align: center .. _/4d.debugger: Debugger ******** When you run your project from the Xojo IDE it is compiled to a binary application with debug code so that it can communicate with the Xojo IDE's Debugger. Desktop applications are launched, web apps open in your browser and mobile apps can be run either in the simulator or on a mobile device. With the Remote Debugger, Xojo can send your compiled app to a remote computer or local virtual machine to run while you debug from the Xojo IDE on your computer. .. image:: https://documentation.xojo.com/topics/migrating_from_other_tools/images/debugger.png :scale: 25% :align: center .. _/4d.version_control: Version control --------------- By default, Xojo projects are saved in a binary format. However, for use with version control you can choose Xojo Project format which is a text-based format where each project item is a separate text file. .. _/4d.deploying_your_app: Deploying your app ------------------ With Xojo you can build desktop applications for Linux, MacOS and Windows. They can be deployed in any way you deploy your desktop applications today. MacOS desktop applications can additionally be deployed via the Mac App Store :doc:`directly from the Xojo IDE`. You can build web applications that can be deployed on any server that will allow you to run a console application or quite easily via :doc:`Xojo Cloud`. Mobile applications for iOS and be :doc:`deployed directly to the iOS App Store right from the Xojo IDE`. They can also be deployed through Apple's Enterprise deployment offering. Android applications can be submitted to the Google Play store, other stores or directly to the device. Mobile operating systems do not allow applications to keep synchronous connections open very long as they use up too much energy and run down the battery. To avoid this, you will need to use an asynchronous connection. It is recommended that you do this by creating a Xojo web application that acts as the middleware between your mobile app and your database. You can then use Xojo's :doc:`URLConnection` class to communicate with it asynchronously. .. _/4d.learn_more: Learn more ---------- * :doc:`Getting started accessing databases` * :doc:`Connecting to a database` * :doc:`Adding, updating and deleting rows` * :doc:`DBKit` * :doc:`SQLiteDatabase` * :doc:`PostgreSQLDatabase` * :doc:`MySQLCommunityServer` * :doc:`Protecting your database from attack` * :doc:`Xojo Cloud` ======================== Migrating from FileMaker ======================== .. rst-class:: forsearch FileMaker .. rst-class:: forsearch File Maker FileMaker is a database tool that runs on both Windows and macOS. It is often called the Mac version of Access. Much like with Access, FileMaker has its own database engine, form designer and scripting language. .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/migrating: Migrating --------- Migrating a FileMaker application is typically a three-step process where you migrate the database itself, the forms that are used to manipulate the data and the scripting code. .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/database: `Database` ********** When migrating a FileMaker application, you first need to consider the database. Although you can connect to a FileMaker database using ODBC, you will need to get the appropriate drivers. As an alternative, you can migrate your data to SQLite (a fast, cross-platform database) by first converting the FileMaker data to XML. You can also connect to a FileMaker database by using Custom Web Publishing and an API. .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/forms: Forms ***** Your FileMaker forms are likely used to edit data in tables. You can recreate these forms as Windows (or Web Pages or iOS Views) in your Xojo application. In most cases you will use Label, TextField and TextArea to recreate form fields, but there are lots of other available controls as well. Either way, you would likely have your application connect to the database at startup and then populate the form with the first record. Next and Previous buttons can be added to fetch and display the appropriate information from the database. .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/scripting_code: Scripting code ************** FileMaker is programmed using a scripting language that is somewhat similar to the Xojo programming language. You will have to rewrite your code, but at the same time will find the Xojo programming language to be familiar. Here are some FileMaker commands and their Xojo equivalents: .. csv-table:: :header: "FileMaker Command", "Xojo Command" :widths: auto "Exit Script",":doc:`Return`" "Set Error Capture",":doc:`Try...Catch`" "Set Variable",":doc:`Var`" "If..End If",":doc:`If...Then...Else`" "Loop..End Loop",":doc:`Do...Loop`" "Go to Field",":ref:`TextField.SetFocus`" "Field assignment",":ref:`TextField.Text` = *value*" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/filemaker_library_(xojo.fm): FileMaker library (Xojo.FM) --------------------------- To make it easier for developers to transition from FileMaker to Xojo, there is an open-source library on GitHub that implements many of the FileMaker functions so that you can use them by name in Xojo. To use the library, download the library from GitHib and open the project. In the Navigator, select the FM module and copy it. Then switch to your project and paste the module to it. `Xojo.FM on GitHub `_ You can refer to any of the functions in the module by using the FM prefix. .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/filemaker_commands_to_xojo_commands: FileMaker commands to Xojo commands ----------------------------------- This section highlights FileMaker functions that have an equivalent command or function built-in to Xojo. If there is no built-in equivalent, the corresponding function in Xojo.FM is noted. .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/aggregate_functions: Aggregate functions ******************* .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "Average","n/a","FM.Average" "Min","n/a","FM.Min" "Max","n/a","FM.Max" "Sum","n/a","FM.Sum" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/container_functions: Container functions ******************* .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "Base64Decode",":doc:`DecodeBase64`","FM. Base64Decode" "Base64Encode",":doc:`EncodeBase64`","FM. Base64Encode" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/date_functions: Date functions ************** .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "Date",":doc:`DateTime`","FM.Date" "Day",":ref:`DateTime.Day`","FM.Day" "DayName","n/a","FM.DayName" "DayOfWeek",":ref:`DateTime.DayOfWeek`","FM.DayOfWeek" "DayOfYear",":ref:`DateTime.DayOfYear`","FM.DayOfYear" "Month",":ref:`DateTime.Month`","FM.Month" "MonthName","n/a","FM.MonthName" "WeekOfYear",":ref:`DateTime.WeekOfYear`","FM.WeekOfYear" "Year",":ref:`DateTime.Year`","FM.Year" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/financial_functions: Financial functions ******************* .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "FV","n/a","FM.FV" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/get_functions: Get functions ************* .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "Get(ApplicationVersion)",":doc:`XojoVersionString`","FM.Get.ApplicationVersion" "Get(CurrentDate)","Var d As :doc:`DateTime` = DateTime.Now","FM.Get.CurrentDate" "Get(CurrentTime)","Var d As :doc:`DateTime` = DateTime.Now","FM.Get.CurrentTime" "Get(CurrentTimestamp)","Var d As :doc:`DateTime` = DateTime.Now","FM.Get.CurrentTimeStamp" "Get(DesktopPath)",":doc:`SpecialFolder`.Desktop","FM.Get.DesktopPath" "Get(Device)","n/a","FM.Get.Device" "Get(DocumentsPath)",":doc:`SpecialFolder`.Documents","FM.Get.DocumentsPath" "Get(DocumentsPathListing)","n/a","FM.Get.DocumentsPathListing" "Get(PreferencesPath)",":doc:`SpecialFolder`.Preferences","FM.Get.PreferencesPath" "Get(ScreenHeight)",":ref:`DesktopDisplay.AvailableHeight`","FM.Get.ScreenHeight" "Get(ScreenScaleFactor)","n/a","FM.Get.ScreenScaleFactor" "Get(ScreenWidth)",":ref:`DesktopDisplay.AvailableWidth`","FM.Get.ScreenWidth" "Get(SystemDrive)",":ref:`FolderItem.DriveAt` (0)","FM.Get.SystemDrive" "Get(SystemPlatform)","n/a","FM.Get.SystemPlatform" "Get(SystemVersion)","System.:doc:`VersionData`","FM.Get.SystemVersion" "Get(TemporaryPath)",":doc:`SpecialFolder`.Temporary","FM.Get.TemporaryPath" "Get(UUID)","n/a","n/a" "Get(WindowContentHeight)",":ref:`Window.Bounds`.Height","FM.Get.WindowContentHeight" "Get(WindowContentWidth)",":ref:`Window.Bounds`.Width","FM.Get.WindowContentWidth" "Get(WindowHeight)",":ref:`Window.Height`","FM.Get.WindowHeight" "Get(WindowLeft)",":ref:`Window.Left`","FM.Get.WindowLeft" "Get(WindowTop)",":ref:`Window.Top`","FM.Get.WindowTop" "Get(WindowWidth)",":ref:`Window.Width`","FM.Get.WindowWidth" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/logical_functions: Logical functions ***************** .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "Case",":doc:`Select...Case`","n/a" "Choose",":doc:`Select...Case`","n/a" "Evaluate","n/a","FM.Evaluate" "ExecuteSQL",":ref:`Database.SelectSQL`, :ref:`Database.ExecuteSQL`","n/a" "If",":doc:`If...Then...Else`, :doc:`If`","n/a" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/number_functions: Number functions **************** .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.Fm" :widths: auto "Abs",":doc:`Abs`","n/a" "Ceiling",":doc:`Ceiling`","FM.Ceiling" "Exp",":doc:`Exp`","n/a" "Floor",":doc:`Floor`","n/a" "Int",":doc:`CType` (value, Integer)","FM.Int" "Ln",":doc:`Log`","FM.Ln" "Mod",":doc:`Mod`","n/a" "Random",":doc:`Rnd`, :doc:`Random`","FM.Random" "Round",":doc:`Round`","n/a" "Sign",":doc:`Sign`","n/a" "Sqrt",":doc:`Sqrt`","n/a" "Truncate","n/a","FM.Truncate" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/text_functions: Text functions ************** .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "Char",":doc:`Chr`","FM.Char" "Code",":ref:`String.Codepoints`","FM.Code" "Exact",":ref:`String.Compare`","FM.Exact" "Filter","n/a","FM.Filter" "FilterValues","n/a","FM.FilterValues" "GetAsDate",":ref:`DateTime.FromString`","FM.GetAsDate" "GetAsText",":doc:`Format`","FM.GetAsText" "GetAsURLEncoded",":doc:`EncodeURLComponent`","FM.GetAsURLEncoded" "GetValue",":doc:`Arrays`","n/a" "Left",":ref:`String.Left`","FM.Left" "LeftValues","n/a","FM.LeftValues" "LeftWords","n/a","FM.LeftWords" "Length",":ref:`String.Length`","" "Lower",":ref:`String.Lowercase`","FM.Lower" "Middle",":ref:`String.Middle`","FM.Middle" "MiddleValues","n/a","FM.MiddleValues" "MiddleWords","n/a","FM.MiddleWords" "PatternCount",":doc:`RegEx`","" "Position",":ref:`String.IndexOf`","FM.Position" "Proper",":ref:`String.Titlecase`","FM.Proper" "Quote","n/a","FM.Quote" "Replace",":ref:`String.Replace`","FM.Replace" "Right",":ref:`String.Right`","n/a" "RightValues","n/a","FM.RightValues" "RightWords","n/a","FM.RightWords" "Substitute",":ref:`String.ReplaceAll`","FM.Substitute" "Trim",":ref:`String.Trim`","" "TrimAll","n/a","FM.TrimAll" "Upper",":ref:`String.Uppercase`","FM.Upper" "ValueCount",":ref:`String.CountFields`","FM.ValueCount" "WordCount","n/a","FM.WordCount" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/text_formatting_functions: Text formatting functions ************************* .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "RGB",":ref:`Color.RGB`","n/a" "TextColor","TextColor property","FM.TextColor" "TextFont","FontName property","" "TextSize","FontSize property","" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/time_functions: Time functions ************** .. csv-table:: :header: "FileMaker", "Xojo", "Xojo.FM" :widths: auto "Hour",":ref:`DateTime.Hour`","n/a" "Minute",":ref:`DateTime.Minute`","n/a" "Seconds",":ref:`DateTime.Second`","n/a" "Time",":doc:`DateTime`","n/a" "n/a",":ref:`System.Ticks`","n/a" "n/a",":ref:`System.Microseconds`","n/a" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/trigonometric_functions: Trigonometric functions *********************** .. csv-table:: :header: `FileMaker`, "Xojo", "Xojo.FM" :widths: auto "Acos",":doc:`Acos`","n/a" "Asin",":doc:`Asin`","n/a" "Atan",":doc:`Atan`","n/a" "Cos",":doc:`Cos`","n/a" "Degrees","n/a","FM.Degrees" "Pi","n/a","FM.Pi" "Radians","n/a","FM.Radians" "Sin",":doc:`Sin`","n/a" "Tan",":doc:`Tan`","n/a" .. _/topics/migrating_from_other_development_tools/migrating_from_filemaker/learn_more: Learn more ---------- To learn more about Xojo and how you can use it to replace or supplement FileMaker, check out these topics: * :doc:`Getting Started` * :doc:`Desktop App Tutorial` * `Web App Tutorial `_ * :doc:`iOS App Tutorial` * :doc:`Using SQLite` * `Xojo.FM `_: Open-source FileMaker library for Xojo * `Develop iOS Apps that Integrate with FileMaker `_ * `Xojo Q&A for FileMaker Developers `_ * `FileMaker with FMLuna `_ * `FileMaker and Aloe `_ * `Using Temper API with FileMaker `_ * `Xojo Blog Posts about FileMaker `_ =============================== Migrating from Microsoft Access =============================== Microsoft Access is `database` software that runs on Windows and is part of specific versions of Office. It is often used to create specialized in-house `database` applications. But Access cannot create real, stand-alone applications. If you have an Access application and are running into its limitations, Xojo is a great choice to take your application to the next level. .. _/topics/migrating_from_other_development_tools/migrating_from_microsoft_access/similarities: Similarities ------------ Access has a form designer, `database` designer and a programming language (VBA: Visual Basic for Applications). Xojo has all these components as well, but expands on each of them. Xojo has a form designer with many more controls than Access provides and Xojo allows you to layout your user interface any way you want. It uses SQLite as its built-in `database` and has a `database` designer for designing your tables. And of course, Xojo has a much more robust programming language. .. _/topics/migrating_from_other_development_tools/migrating_from_microsoft_access/migrating: Migrating --------- Migrating an Access application is typically a three-step process where you migrate the `database` itself, the forms that are used to manipulate the data and the source code. .. _/topics/migrating_from_other_development_tools/migrating_from_microsoft_access/database: `Database` ********** When migrating a Microsoft Access application, you first need to consider the `database`. If you are using the Access “Jet” `database` engine, you will most likely want to migrate it to another `database` engine. Although you can connect to a Jet `database` using :doc:`ODBC` or ADO on Windows, Jet is not a cross-platform `database` format. On a Mac you can only connect to a Jet `database` using ODBC in read-only mode. Your best option in this case is to use :doc:`SQLite`, which is much faster than the Access Jet `database` and is fully cross-platform. You can easily migrate your `database` tables and data from an Access `database` to SQLite. This can be done using ODBC, ADO or a variety of 3rd party products. If your Access `database` is connecting to another `database` as its data source, then you can use the :doc:`ODBCDatabase` class and an ODBC driver to connect. Or you can use Xojo's built-in support for :doc:`PostgreSQL` and :doc:`MySQL`. .. _/topics/migrating_from_other_development_tools/migrating_from_microsoft_access/forms: Forms ***** Your Access forms are likely used to edit data in tables. You can recreate these forms as Windows, Web Pages or Views (iOS) in your Xojo application. .. _/topics/migrating_from_other_development_tools/migrating_from_microsoft_access/source_code: Source code *********** Access is programmed using the Visual Basic for Applications language which is quite similar to the Xojo programming language. You will have to rewrite your code, but at the same time will find the Xojo programming language to be familiar in its syntax and commands. .. _/topics/migrating_from_other_development_tools/migrating_from_microsoft_access/learn_more: Learn more ---------- To learn more about Xojo and how you can use it to replace Microsoft Access, check out these topics: * :doc:`Getting Started` * :doc:`Desktop App Tutorial` * :doc:`Web App Tutorial` * :doc:`iOS App Tutorial` * :doc:`Android App Tutorial` * :doc:`DBKit Tutorial` ========================= Migrating from PowerBASIC ========================= .. rst-class:: forsearch Power Basic PowerBASIC use a language very similar to the Xojo language. You will notice that many of the commands are nearly the same, but there are differences as well. Unfortunately, PowerBASIC appears to no longer be updated by PowerBASIC Inc with no new updates since 2012 and no further updates planned. For those that need a more modern compiler that is both similar and actively updated, Xojo is a good fit. Xojo uses the open-source LLVM compiler to generate 64-bit builds with several optimization settings. The Xojo language is similar to BASIC, but is more advanced and fully object-oriented. Xojo has a robust layout editor so you won't have to tedously create your user interfaces in code. And lastly, Xojo supports multiple platforms. The Xojo IDE itself can run on Windows, Mac and Linux. And Xojo can create these types of native apps: * Desktop apps for Windows, Mac, Linux and Raspberry Pi * Console apps for Windows, Mac, Linux and Raspberry Pi * Web apps for Windows, Mac, Linux and Raspberry Pi servers * iOS apps for iPhones and iPads .. _/topics/migrating_from_other_development_tools/migrating_from_powerbasic/comparison_to_powerbasic: Comparison to PowerBASIC ------------------------ Based on PowerBASIC online docs from here: http://www.powerbasic.com/help/pbwin/wf_njs.htm When first released in 1998, Xojo was originally called REALbasic, which ought to highlight the origin of its BASIC-like programming language. But even then, Xojo was more advanced that your typical BASIC. Today Xojo is a modern, object-oriented programming language in its own right that should feel very familiar to users of PowerBASIC. .. _/topics/migrating_from_other_development_tools/migrating_from_powerbasic/the_xojo_integrated_development_environment: The Xojo integrated development environment ******************************************* Xojo utilizes a powerful, modern IDE for creating your apps. The main areas of the IDE are the Project Navigator that appears on the left and the Library/Inspector that appears on the right. In the center you'll see either the UI Layout Editor or the Code Editor. The Code Editor has full syntax highlighting with auto-complete. The Layout Editor lets you easily create your user interfaces using drag and drop. For more information on the Xojo IDE: * Navigator * Library * Inspector * Code Editor * Layout Editor .. _/topics/migrating_from_other_development_tools/migrating_from_powerbasic/programming_language: Programming language ******************** The language syntax of PowerBASIC is very similar to Xojo. You 'll see familiar syntax for If..Then..Else, For..Next, While..Wend, Dim and many other commands. Someone who has used PowerBASIC will have no trouble understanding the Xojo programming language. Some other information about the Xojo Programming Language: * Strongly typed * Fully object-oriented * Uses Automated Reference Counting for memory management * Compiles to native machine code .. _/topics/migrating_from_other_development_tools/migrating_from_powerbasic/data_types: Data types ********** Although Xojo data types are not always named exactly the same as PowerBASIC data types, all the equivalent types are there. For example, Integer is equivalent to a PowerBASIC Long (for 32-bit apps) and PowerBASIC Quad (for 64-bit apps). Here is a mapping of some PowerBASIC data types to Xojo data types: .. csv-table:: :header: "PowerBASIC Data Type", "Xojo Data Type" :widths: auto "n/a",":doc:`Boolean`" "BYTE",":doc:`UInt8`" "WORD",":doc:`UInt16`" "CUR",":doc:`Currency`" "DATE$",":doc:`DateTime`" "DOUBLE",":doc:`Double`" "INTEGER",":doc:`Int16`" "LONG",":doc:`Int32`" "DWORD",":doc:`UInt32`" "QUAD",":doc:`Int64`" "SINGLE",":doc:`Single`" "STRING, WSTRING",":doc:`String`" "VARIANT",":doc:`Variant`" .. _/topics/migrating_from_other_development_tools/migrating_from_powerbasic/variables_and_variable_scope: Variables and variable scope **************************** Xojo is strongly typed, so all variables must have a defined type. Related Documentation topics: * Variables and Constants .. _/topics/migrating_from_other_development_tools/migrating_from_powerbasic/operators: Operators ********* Xojo has arithmetic and relational operators that are equivalent to what PowerBASIC has. You can read more about them in the Reference Guide: * Operators * Operator Precedence Error Handling Like PowerBASIC you can have both compile-time errors and runtime errors. Compile-time errors are displayed in the Errors panel when you run your project. Clicking on an error takes you to the line of code in error. For handling runtime errors, Xojo uses something a bit more modern than ON ERROR GOTO called Exception Handling. Essentially, when a runtime error occurs, an exception is raised. Your code can "catch" the exception in order to handle the error. There is even a global event that can be used to catch any error. .. _/topics/migrating_from_other_development_tools/migrating_from_powerbasic/controls: Controls ******** Unlike PowerBASIC, Xojo uses a powerful layout editor for creating your user interface layouts. The default UI controls included with PowerBASIC are, for the most part, also included with Xojo, but Xojo also has several controls that are not included with PowerBASIC. Here is a list of some PowerBASIC controls and their Xojo equivalents: .. csv-table:: :header: "PowerBASIC Control", "Xojo Desktop Control", "Xojo Web Control", "Xojo iOS Control" :widths: auto "BUTTON",":doc:`DesktopButton`",":doc:`WebButton`",":doc:`MobileButton`" "CHECK3STATE, CHECKBOX",":doc:`DesktopCheckBox`",":doc:`WebCheckBox`",":doc:`MobileSwitch`" "COMBOBOX",":doc:`DesktopComboBox`",":doc:`WebComboBox`","n/a" "FRAME",":doc:`DesktopGroupBox`","n/a","n/a" "GRAPHIC",":doc:`DesktopCanvas`",":doc:`WebCanvas`",":doc:`MobileCanvas`" "IMAGE",":doc:`DesktopImageViewer`",":doc:`WebImageViewer`",":doc:`MobileImageViewer`" "IMGBUTTON",":doc:`DesktopBevelButton`","n/a","n/a" "LABEL",":doc:`DesktopLabel`",":doc:`WebLabel`",":doc:`MobileLabel`" "LINE",":doc:`DesktopSeparator`","WebSeparator":doc:`MobileSeparator`" "LISTBOX, LISTVIEW",":doc:`DesktopListBox`",":doc:`WebListBox`",":doc:`iOSMobileTable`" "OPTION",":doc:`DesktopRadioGroup`",":doc:`WebRadioGroup`","n/a" "PROGRESSBAR",":doc:`DesktopProgressBar`",":doc:`WebProgressBar`",":doc:`MobileProgressBar`" "TAB", ":doc:`DesktopTabPanel`", :doc:`WebTabPanel`, ":doc:`iOSTabBar`" "TEXTBOX",":doc:`DesktopTextField`, :doc:`DesktopTextArea`",":doc:`WebTextField`, :doc:`WebTextArea`",":doc:`MobileTextField`, :doc:`MobileTextArea`" "TOOLBAR",":doc:`DesktopToolbar`",":doc:`WebToolbar`",":doc:`MobileToolbar`" "TREEVIEW",":doc:`DesktopListBox`","n/a","n/a" Because Xojo is object-oriented, any of its built-in controls can be subclassed to add features or change their behavior. In addition, Xojo has ContainerControls which can be used to create your own controls by combining other controls. =========================== Migrating from Visual Basic =========================== .. rst-class:: forsearch VisualBasic Visual Basic (6 or earlier) and Visual Basic .NET use a language very similar to the Xojo language. You will notice that many of the commands are nearly the same, but there are differences as well. You can find further information in these blog posts: `A Modern Alternative to Visual Basic `_ and `Migrate Your Visual Basic Apps `_ to Xojo and the `Migrating to Visual Basic video `_. To help make your transition from Visual Basic easier you can also download the open-source VB library which maps many VB commands to their Xojo equivalents for you: * `Xojo.VB `_: A library of Visual Basic functions for use with the Xojo programming language. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/similarities_to_visual_basic: Similarities to Visual Basic ---------------------------- Visual Basic 6 (VB6) is no longer supported by Microsoft, which recommends you instead migrate to Visual Basic .NET (VB.NET). But Visual Basic .NET is large and complex, not to mention not cross-platform. Xojo is usually a better choice for Visual Basic 6 apps because it has the simplicity of VB6, but is a fully object-oriented language like VB.NET. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/programming_language: Programming language ******************** To start with, the language syntax of VB is very similar to Xojo. You 'll see familiar syntax for :doc:`If...Then...Else`, :doc:`For...Next`, :doc:`While...Wend`, :doc:`Dim` and many other commands. Someone who has used either VB6 or VB.NET will have no trouble understanding the Xojo programming language. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/data_types: Data types ********** Although Xojo data types are not always named exactly the same as VB6 data types, all the equivalent types are there. For example, :doc:`Integer` is equivalent to a VB6 Long. Here is a mapping of some VB data types to Xojo data types: .. csv-table:: :header: "VB Data Type", "Xojo Data Type" :widths: auto "Boolean",":doc:`Boolean`" "Byte",":doc:`UInt8`" "Currency",":doc:`Currency`" "Date",":doc:`DateTime` class" "Double",":doc:`Double`" "Integer",":doc:`Int16`" "Long",":doc:`Integer`" "Object",":doc:`Object`" "Single",":doc:`Single`" "String",":doc:`String`" "Variant",":doc:`Variant`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/commands: Commands ******** Below are some common VB commands and their corresponding Xojo commands. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/string_manipulation: String manipulation ^^^^^^^^^^^^^^^^^^^ .. csv-table:: :header: "VB Command", "Xojo Command" :widths: auto "Asc",":ref:`String.Asc`" "Chr",":ref:`String.ChrByte`" "Len",":ref:`String.Length`" "Left",":ref:`String.Left`" "Right",":ref:`String.Right`" "Mid",":ref:`String.Middle`" "Trim",":ref:`String.Trim`" "LCase",":ref:`String.Lowercase`" "UCase",":ref:`String.Uppercase`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/mathematical_functions: Mathematical functions ^^^^^^^^^^^^^^^^^^^^^^ .. csv-table:: :header: "VB Command", "Xojo Command" :widths: auto "Abs",":doc:`Abs`" "Cos",":doc:`Cos`" "Sin",":doc:`Sin`" "Tan",":doc:`Tan`" "Val",":doc:`Val`" "Rnd",":doc:`Rnd`" "Int",":doc:`Floor`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/input_and_output: Input and output ^^^^^^^^^^^^^^^^ .. csv-table:: :header: "VB Command", "Xojo Command" :widths: auto "MsgBox",":doc:`MessageBox`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/date_and_time: Date and time ^^^^^^^^^^^^^ .. csv-table:: :header: "VB Command", "Xojo Command" :widths: auto "Date",":doc:`DateTime`" "Time",":doc:`DateTime`" "Timer",":doc:`Timer`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/miscellaneous: Miscellaneous ^^^^^^^^^^^^^ .. csv-table:: :header: "VB Command", "Xojo Command" :widths: auto "IsNull",":doc:`Is` :doc:`Nil`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/file_handling: File handling ^^^^^^^^^^^^^ .. csv-table:: :header: "VB Command", "Xojo Command" :widths: auto "Open",":ref:`TextInputStream.Open`, :ref:`BinaryStream.Open`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/controls: Controls ******** The default UI controls included with VB are, for the most part, also included with Xojo. But Xojo also has several controls that are not included by default with VB. Of course VB had plenty of additional, but Windows-specific, controls that could be added to its default setup and many of these controls can be added to Xojo using ActiveX, but they will also remain Windows-only. Here is a list of some VB controls and their Xojo equivalents for desktop, web and iOS apps: .. csv-table:: :header: "VB Control", "Xojo Desktop Control", "Xojo Web Control", "Xojo Mobile Control" :widths: auto "PictureBox",":doc:`DesktopCanvas`, :doc:`DesktopImageViewer`",":doc:`WebCanvas`, :doc:`WebImageViewer`",":doc:`MobileCanvas` :doc:`MobileImageViewer`" "Label",":doc:`DesktopLabel`",":doc:`WebLabel`",":doc:`MobileLabel`" "TextBox",":doc:`DesktopTextField`, :doc:`DesktopTextArea`",":doc:`WebTextField`, :doc:`WebTextArea`",":doc:`MobileTextField`, :doc:`MobileTextArea`" "Frame",":doc:`DesktopGroupBox`","n/a","n/a" "CommandButton",":doc:`DesktopButton`, :doc:`DesktopBevelButton`",":doc:`WebButton`",":doc:`MobileButton`" "CheckBox",":doc:`CheckBox`",":doc:`WebCheckBox`",":doc:`MobileSwitch`" "Listbox",":doc:`DesktopListBox`, :doc:`DesktopPopupMenu`",":doc:`WebListBox`, :doc:`WebPopupMenu`",":doc:`iOSMobileTable`" "HScrollBar, VScrollBar",":doc:`DesktopScrollbar`","n/a",":doc:`MobileScrollableArea`" "Timer",":doc:`Timer`",":doc:`WebTimer`",":doc:`Timer`" "`Shape`",":doc:`DesktopOval`, :doc:`DesktopRectangle`",":doc:`WebRectangle`",":doc:`MobileOval`, :doc:`MobileRectangle`" "WebBrowser",":doc:`DesktopHTMLViewer`",":doc:`WebHTMLViewer`",":doc:`MobileHTMLViewer`" "TreeView",":doc:`DesktopListBox`","n/a","n/a" "Toolbar",":doc:`DesktopToolbar`",":doc:`WebToolbar`",":doc:`MobileToolbar`" "MediaPlayer",":doc:`DesktopMoviePlayer`",":doc:`WebMoviePlayer`",":doc:`MobileHTMLViewer`" .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/differences_from_visual_basic: Differences from Visual Basic ----------------------------- Xojo definitely feels familiar to VB developers, but there are differences as well. A big difference is that Xojo cannot create DLLs, ActiveX controls or any kind of shared libraries. Since these are all Windows-specific technologies, they are not useful for cross-platform apps. Xojo can access DLLs and many ActiveX controls, but using them means your application will only run on Windows and cannot be cross-platform. Of course, Xojo can easily create web and iOS apps, something VB6 cannot do. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/file_i/o: File I/O ******** File input and output in VB6 uses direct, path-based access to files. This is not something that works for cross-platform apps, so Xojo consolidates all file processing into a few classes: :doc:`FolderItem`, :doc:`TextInputStream`, :doc:`TextOutputStream` and :doc:`BinaryStream`. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_basic/data_typing: Data typing *********** Xojo is a strongly typed programming language. VB6 (and older versions) would allow you to use a variable that had not been previously declared. It would infer a type based on a special character in its name (name$ would be a String, for instance). Before trying to migrate VB6 code, you should use the OPTION EXPLICIT command to make sure that all your variables are declared. .. seealso:: `Xojo blog posts about Visual Basic `_ ============================ Migrating from Visual FoxPro ============================ Visual FoxPro (VFP) is a Windows programming tool made by Microsoft. It has been given its end-of-life and is no longer supported by Microsoft. VFP has its own tightly integrated `database` engine, a form designer and a programming language. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_foxpro/migrating: Migrating --------- Migrating a Visual FoxPro application is typically a three-step process where you migrate the `database` itself, the forms that are used to manipulate the data and the scripting code. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_foxpro/database: `Database` ********** When migrating a VFP application, you first need to consider the `database`. You can connect to a VFP `database` using ODBC on Windows, but it makes more sense to migrate your data to SQLite, which is a fast, cross-platform `database`. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_foxpro/forms: Forms ***** Your VFP forms are likely used to edit data in tables. You can recreate these forms as Windows (or web pages) using drag and drop just as you can with VFP. For simple desktop applications, you may find the DataControl control to be an easy way to map your fields to `database` tables and columns without having to write a lot of code. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_foxpro/source_code: Source code *********** VFP is programmed using a proprietary language that is quite similar to the Xojo programming language. You will have to rewrite your code, but at the same time will find the Xojo programming language to be familiar. Cully Technologies has a tool that can help you `migrate your Visual Fox Pro projects to Xojo `_. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_foxpro/language_syntax: Language syntax --------------- The syntax of the two languages is different, but the concepts are quite similar. For example, to create an instance of a new class in VFP, you might use this code: .. code:: xojo LOCAL oMyClass oMyClass = CREATEOBJECT("MyClass") In Xojo you would write: .. code:: xojo Var oMyClass As New MyClass The VFP MessageBox command is similar. Instead of writing this: .. code:: xojo MessageBox("Hello, World!") You would write this: .. code:: xojo MessageBox("Hello, World!") Here are some other VFP commands and their Xojo equivalents: .. csv-table:: :header: "VFP Command", "Xojo Command" :widths: auto "ON ERROR",":doc:`Exception`" "TRY..CATCH..END TRY",":doc:`Try...Catch`" "DO WHILE..ENDDO",":doc:`While...Wend` :doc:`Do...Loop`" "FOR EACH..ENDFOR",":doc:`For Each...Next`" "FOR..ENDFOR",":doc:`For...Next`" "IF..ENDIF",":doc:`If...Then...Else`" "LOOP",":doc:`Continue`" "DECLARE",":doc:`Var`" "DO CASE..END CASE",":doc:`Select Case`" Special thanks to Kevin Cully of Cully Technologies for assistance with the information in this section. .. _/topics/migrating_from_other_development_tools/migrating_from_visual_foxpro/learn_more: Learn more ---------- To learn more about Xojo and how you can use it to replace Microsoft Visual FoxPro, check out these topics: * `Xojo Q&A for FoxPro Developers `_ * `FoxCon 2015: Xojo for Desktop and Web `_ * :doc:`Getting Started` * :doc:`Desktop App Tutorial` * `Web App Tutorial `_ * :doc:`iOS App Tutorial` * :doc:`Using SQLite` Office Automation ================= .. toctree:: :maxdepth: 1 :name: sec-office_automation Controlling Microsoft Office from your app Reserved words when using Microsoft Office Automation ========================================== Controlling Microsoft Office from your app ========================================== Office Automation consists of a module and classes that are used to access the Microsoft Office Automation Object model for controlling Word, Excel and PowerPoint. You will need to copy the MSOfficeAutomation plugin (located in the Extras folder of the installation) to the Plugins folder before you can use these classes. .. note:: Office Automation only works on Microsoft Windows. .. _/topics/office_automation/controlling_microsoft_office_from_your_app/module_and_class_overview: Module and class overview ------------------------- .. _/topics/office_automation/controlling_microsoft_office_from_your_app/office_module: Office module ************* This module contains all the enums you'd need for Office automation. * Excel constants begin with xl * PowerPoint constants begin with pp * Word constants begin with wd * General Office constants begin with mso The classes below give you access to the Microsoft Office Automation Object model for controlling Word, Excel and PowerPoint. Refer to Microsoft's official documentation to learn about the Office Automation Object model: * `How to find and use the Office object model documentation `_ * `Office Developer Documentation `_ .. _/topics/office_automation/controlling_microsoft_office_from_your_app/excelapplication_class: ExcelApplication class ********************** The :doc:`ExcelApplication` class inherits from OLEObject and is used to automate Excel. .. _/topics/office_automation/controlling_microsoft_office_from_your_app/powerpointapplication_class: PowerPointApplication class *************************** The :doc:`PowerPointApplication` class inherits from OLEObject and is used to automate PowerPoint. .. _/topics/office_automation/controlling_microsoft_office_from_your_app/wordapplication_class: WordApplication class ********************* The :doc:`WordApplication` class inherits from OLEObject and is used to automate Word. .. _/topics/office_automation/controlling_microsoft_office_from_your_app/office_automation_under_xojo_versus_visual_basic_for_applications_(vba): Office automation under Xojo versus Visual Basic for Applications (VBA) ----------------------------------------------------------------------- In most cases it ought to be fairly straightforward to port VBA Office Automation code to Xojo, but there are differences. Below are some ways that Xojo differs from VBA: * No implied Application instance * Xojo prefixes all Excel, PowerPoint and Word classes * Office constants live inside Office Module * Collection objects cannot be iterated using For Each loop * No named parameter support * Some keywords conflict with method names * Xojo provides better exception handling .. _/topics/office_automation/controlling_microsoft_office_from_your_app/working_from_the_application_class_in_vba: Working from the Application class in VBA ***************************************** There is an implied Application instance when you write VBA code from within Excel, PowerPoint, or Word. For Example: .. code:: xojo ' This is VBA code Var pres as Presentation Var slide1 as Slide Set pres = Presentations.Add ' The above is the same as saying: ' Set pres = Application.Presentations.Add Set slide1 = pres.Slides.Add(1, ppLayoutText) If you are in PowerPoint itself, then the above code would run just fine since it knows what a Presentation object is. Obviously if you typed this code in either Word or Excel, it would generate errors. So what does this code look like in Xojo? Here's the Xojo code: .. code:: xojo Var powerPoint As New PowerPointApplication Var pres As PowerPointPresentation Var slide1 As PowerPointSlide pres = powerPoint.Presentations.Add slide1 = pres.Slides.Add(1, Office.ppLayoutText) .. _/topics/office_automation/controlling_microsoft_office_from_your_app/using_collection_objects: Using Collection objects ************************ Xojo does not understand VB/OLE collection objects, therefore you cannot use the For Each...Next statement to iterate through the collection. This just means you need to use a counter variable in your For loop. Here is a VBA example: .. code:: xojo ' VBA code For Each doc In Documents doc.Range.Text = "Hello world!" Next This is the above code translated to Xojo: .. code:: xojo ' Xojo code For i As Integer = 1 To Word.Documents.Count Word.Documents(i).Range.Text = "Hello world!" Next .. _/topics/office_automation/controlling_microsoft_office_from_your_app/passing_parameters_by_name: Passing parameters by name ************************** Xojo doesn't support passing parameters by name, however you can still achieve the equivalent behavior. First of all, you have to understand how to use the :doc:`OLEObject`. You can read up on the docs, but for brevity here's a quick example you can use as a template. Here's a find and replace macro in Word VBA: .. code:: xojo ' This is VBA code Selection.Find.ClearFormatting Selection.Find.Replacement.ClearFormatting With Selection.Find .Text = "find this" .Replacement.Text = "replace with" .Wrap = wdFindContinue .Format = false .MatchCase = false .MatchWholeWord = false .MatchWildcards = false .MatchSoundsLike = false .MatchAllWordForms = false End With Selection.Find.Execute Replace:=wdReplaceAll Here's what the above code looks like in Xojo: .. code:: xojo Var word As New WordApplication Var find As WordFind ' Setting the properties is largely the same find = word.Selection.Find find.ClearFormatting find.Replacement.ClearFormatting find.text = "find this" find.Replacement.Text = "replace with" find.Wrap = Office.wdFindContinue find.Format = False find.MatchCase = False find.MatchWholeWord = False find.MatchWildcards = False find.MatchSoundsLike = False find.MatchAllWordForms = False ' To execute the Find, you have to create an OLEParameter ' and then set its position based on the MS Office model docs. Var replaceParam As New OLEParameter replaceParam.Value = Office.wdReplaceAll ' According to the docs on Find.Execute the Replace parameter is the 11th replaceParam.Position = 11 find.Execute(replaceParam) That's all there is to it. Obviously, finding the correct position of that named parameter is the trickiest part. But it's about the only time when you really need to launch VBA and look it up in its Object Browser. .. _/topics/office_automation/controlling_microsoft_office_from_your_app/conflicting_keywords: Conflicting keywords ******************** There are certain reserved keywords in Xojo (these will usually be hilighted in a different color, such as'select' or 'End') that cannot be used as method names or property names. Unfortunately, Excel, as an example, uses some of these names for its methods/properties. To get around this problem, you can suffix the method/property name that you want access to with an underscore character. Here's an example of how you'd call the "Select" method in Excel from Xojo: .. code:: xojo Excel.Range("A1", "A3").Select_ Since the keyword'select' is reserved, you suffix it with an underscore character and Xojo will hand it off to Excel as "Select". .. _/topics/office_automation/controlling_microsoft_office_from_your_app/usage: Usage ----- This simple example creates a new Word document and adds some text to it from a :doc:`TextArea` on the :doc:`Window` called SampleTextArea: .. code:: xojo Try ' Creates a new Word document and ' adds the text from SampleTextArea to the ' document Var doc As WordDocument Var style As WordStyle Var wordApp As New WordApplication wordApp.Visible = True doc = wordApp.Documents.Add doc.Range.Text = SampleTextArea.Value Catch err As OLEException MessageBox(err.Message) End Try .. _/topics/office_automation/controlling_microsoft_office_from_your_app/trouble_shooting: Troubleshooting --------------- .. _/topics/office_automation/controlling_microsoft_office_from_your_app/how_can_you_tell_if_windows_has_the_required_ole_libraries_installed?: How can you tell if Windows has the required OLE libraries installed? ********************************************************************* One easy way is to load up Visual Basic Editor (under the Tools and Macros menu), and do some automation with VBA. Here are the steps you can use to automate PowerPoint from Word: 1. Start Word. #. Start Visual Basic Editor in Word. #. Insert a UserForm. #. Add a CommandButton to the form. #. In the click event of the button, put in this code: .. code:: xojo Var obj As Object Set obj = CreateObject("PowerPoint.Application") obj.Activate 1. Run the program and click on the button. #. If PowerPoint loads up and you don't get any errors, then the OLE libraries are installed. .. _/topics/office_automation/controlling_microsoft_office_from_your_app/what_are_the_possible_errors_that_can_be_caught?: What are the possible errors that can be caught? ************************************************ Errors come through OLE, so you need to catch :doc:`OLEException`. This will report the last command that failed along with any additional information about the exception. You can use either the :doc:`Exception` or :doc:`Try...Catch` commands to catch exceptions. With Exception: .. code:: xojo Try Var word As New WordApplication word.ShowClipboard Catch err As OLEException MessageBox(err.Message) End Try With Try...Catch: .. code:: xojo Try Var word As New WordApplication word.ShowClipboard Catch e As OLEException MessageBox(e.Message) End Try .. _/topics/office_automation/controlling_microsoft_office_from_your_app/more_information: More information ---------------- * `Office Automation video `_ * Example projects can be found at Examples/Platforms/Desktop/Windows/Office Automation. * These 3rd party books are available: * `Excel 2016 with Xojo `_ * `Program Word 2010-2016 with Xojo `_ ===================================================== Reserved words when using Microsoft Office automation ===================================================== .. _/topics/office_automation/reserved_words_when_using_microsoft_office_automation/office_reserved_words: Office reserved words --------------------- If you have the OfficeAutomation plugin loaded then the following words, which are the names of Office classes, cannot be used as names of your own variables, objects, or event definitions. In the following lists they are grouped by Office product and by VBE. .. _/topics/office_automation/reserved_words_when_using_microsoft_office_automation/vbe_defines: VBE defines *********** .. csv-table:: :header: "Reserved Words", "Reserved Words", "Reserved Words" :widths: 25, 25, 25 "VBE","VBEComponents","VBEReferencesEvents" "VBEAddIn","VBEEvents","VBESelectedComponents" "VBEAddins","VBELinkedWindows","VBEVBComponent" "VBEApplication","VBEProjectTemplate","VBEVBComponents" "VBECodeModule","VBEProperties","VBEVBProject" "VBECodePane","VBEProperty","VBEVBProjects" "VBECodePanes","VBEReference","VBEWindow" "VBECommandBarEvents","VBEReferences","VBEWindows" "VBEComponent","","" .. _/topics/office_automation/reserved_words_when_using_microsoft_office_automation/excel_reserved_words: Excel reserved words ******************** .. csv-table:: :header: "Reserved Word", "Reserved Word", "Reserved Word", "Reserved Word" :widths: 25, 25, 25, 25 "ExcelAddIns","ExcelCustomView","ExcelHyperlink","ExcelIOval" "ExcelAdjustments","ExcelCustomViews","ExcelHyperlinks","ExcelIOvals" "ExcelApplication","ExcelDataLabel","ExcelIInterior","ExcelIPageSetup" "ExcelArc","ExcelDataLabels","ExcelILabel","ExcelIPane" "ExcelArcs","ExcelDataTable","ExcelILabels","ExcelIPanes" "ExcelAreas","ExcelDefaultWebOptions","ExcelILeaderLines","ExcelIParameter" "ExcelAutoCorrect","ExcelDialog","ExcelILegend","ExcelIParameters" "ExcelAutoFilter","ExcelDialogFrame","ExcelILegendEntries","ExcelIPhonetic" "ExcelAxes","ExcelDialogs","ExcelILegendEntry","ExcelIPhonetics" "ExcelAxis","ExcelDialogSheet","ExcelILegendKey","ExcelIPicture" "ExcelAxisTitle","ExcelDialogSheets","ExcelILine","ExcelIPictures" "ExcelBorder","ExcelDisplayUnitLabel","ExcelILines","ExcelIPivotCache" "ExcelBorders","ExcelDownBars","ExcelILinkFormat","ExcelIPivotCaches" "ExcelButton","ExcelDrawing","ExcelIListBox","ExcelIPivotField" "ExcelButtons","ExcelDrawingObjects","ExcelIListBoxes","ExcelIPivotFields" "ExcelCalculatedFields","ExcelDrawings","ExcelIMailer","ExcelIPivotFormula" "ExcelCalculatedItems","ExcelDropDown","ExcelIMenu","ExcelIPivotFormulas" "ExcelCalloutFormat","ExcelDropDowns","ExcelIMenuBar","ExcelIPivotItem" "ExcelCharacters","ExcelDropLines","ExcelIMenuBars","ExcelIPivotItems" "ExcelChart","ExcelEditBox","ExcelIMenuItem","ExcelIPivotLayout" "ExcelChartArea","ExcelEditBoxes","ExcelIMenuItems","ExcelIPivotTable" "ExcelChartColorFormat","ExcelErrorBars","ExcelIMenus","ExcelIPivotTables" "ExcelChartFillFormat","ExcelFillFormat","ExcelIModule","ExcelIPlotArea" "ExcelChartGroup","ExcelFilter","ExcelIModules","ExcelIPoint" "ExcelChartGroups","ExcelFilters","ExcelIName","ExcelIPoints" "ExcelChartObject","ExcelFloor","ExcelINames","ExcelIPublishObjects" "ExcelChartObjects","ExcelFont","ExcelInterior","ExcelIQueryTable" "ExcelCharts","ExcelFormatCondition","ExcelIODBCError","ExcelIQueryTables" "ExcelChartTitle","ExcelFormatConditions","ExcelIODBCErrors","ExcelIRange" "ExcelCheckBox","ExcelFreeformBuilder","ExcelIOLEDBError","ExcelIRecentFile" "ExcelCheckBoxes","ExcelGlobal","ExcelIOLEDBErrors","ExcelIRecentFiles" "ExcelColorFormat","ExcelGridlines","ExcelIOLEFormat","ExcelIRectangle" "ExcelComment","ExcelGroupBox","ExcelIOLEObject","ExcelIRectangles" "ExcelComments","ExcelGroupBoxes","ExcelIOLEObjectEvents","ExcelIRefreshEvents" "ExcelConnectorFormat","ExcelGroupObject","ExcelIOLEObjects","ExcelIRoutingSlip" "ExcelControlFormat","ExcelGroupObjects","ExcelIOptionButton","ExcelIScenario" "ExcelCorners","ExcelGroupShapes","ExcelIOptionButtons","ExcelIScenarios" "ExcelCubeField","ExcelHiLoLines","ExcelIOutline","ExcelIScrollBar" "ExcelCubeFields","ExcelHPageBreak","ExcelIOval","ExcelIScrollBars" "ExcelISeries","ExcelISpinners","ExcelIToolbarButton","ExcelIVPageBreak" "ExcelISeriesCollection","ExcelIStyle","ExcelIToolbarButtons","ExcelIVPageBreaks" "ExcelISeriesLines","ExcelIStyles","ExcelIToolbars","ExcelIWalls" "ExcelIShape","ExcelITextBox","ExcelITrendline","ExcelIWindow" "ExcelIShapeRange","ExcelITextBoxes","ExcelITrendline","ExcelIWindows" "ExcelIShapes","ExcelITextFrame","ExcelITrendlines","ExcelIWorkbookEvents" "ExcelISoundNote","ExcelITickLabels","ExcelIUpBars","ExcelIWorksheetFunction" "ExcelISpinner","ExcelIToolbar","ExcelIValidation","ExcelIWorksheets" .. _/topics/office_automation/reserved_words_when_using_microsoft_office_automation/powerpoint_reserved_words: PowerPoint reserved words ************************* .. csv-table:: :header: "Reserved Word", "Reserved Word", "Reserved Word" :widths: 25, 25, 25 "PowerPoint ActionSetting","PowerPointMouseDownHandler","PowerPointPPTabSheets" "PowerPointAddIn","PowerPointMouseTracker","PowerPointPPToggleButton" "PowerPointAddIns","PowerPointNamedSlideShow","PowerPointPresentation" "PowerPointAdjustments","PowerPointNamedSlideShows","PowerPointPresentations" "PowerPointAnimationSettings","PowerPointObjectVerbs","PowerPointPrintOptions" "PowerPointApplication","PowerPointOCXExtender","PowerPointPrintRange" "PowerPointBorders","PowerPointOLEControl","PowerPointPrintRanges" "PowerPointBulletFormat","PowerPointOLEFormat","PowerPointPublishObject" "PowerPointCalloutFormat","PowerPointPageSetup","PowerPointPublishObjects" "PowerPointCell","PowerPointPane","PowerPointRGBColor" "PowerPointCellRange","PowerPointPanes","PowerPointRow" "PowerPointCollection","PowerPointParagraphFormat","PowerPointRows" "PowerPointColorFormat","PowerPointPictureFormat","PowerPointRuler" "PowerPointColorScheme","PowerPointPlaceholderFormat","PowerPointRulerLevel" "PowerPointColorSchemes","PowerPointPlaceholders","PowerPointRulerLevels" "PowerPointColumn","PowerPointPlaySettings","PowerPointShadowFormat" "PowerPointColumns","PowerPointPPAlert","PowerPointShape" "PowerPointConnectorFormat","PowerPointPPBitmap","PowerPointShapeNode" "PowerPointDefaultWebOptions","PowerPointPPBitmapButton","PowerPointShapeNodes" "PowerPointDocumentWindow","PowerPointPPCheckBox","PowerPointShapeRange" "PowerPointDocumentWindows","PowerPointPPControl","PowerPointShapes" "PowerPointExtraColors","PowerPointPPControls","PowerPointSlide" "PowerPointFileDialog","PowerPointPPDialog","PowerPointSlideRange" "PowerPointFileDialogExtension","PowerPointPPDialogs","PowerPointSlides" "PowerPointFileDialogExtensionList","PowerPointPPDropDown","PowerPointSlideShowSettings" "PowerPointFileDialogFileList","PowerPointPPDropDownEdit","PowerPointSlideShowTransition" "PowerPointFillFormat","PowerPointPPEditText","PowerPointSlideShowView" "PowerPointFont","PowerPointPPFrame","PowerPointSlideShowWindow" "PowerPointFonts","PowerPointPPListBox","PowerPointTabStop" "PowerPointFreeformBuilder","PowerPointPPPushButton","PowerPointTabStops" "PowerPointGlobal","PowerPointPPRadioButton","PowerPointTags" "PowerPointGroupShapes","PowerPointPPRadioCluster","PowerPointTextEffectFormat" "PowerPointHeaderFooter","PowerPointPPScrollBar","PowerPointTextFrame" "PowerPointHeadersFooters","PowerPointPPSlideMiniature","PowerPointTextRange" "PowerPointHyperlink","PowerPointPPSpinner","PowerPointTextStyle" "PowerPointHyperlinks","PowerPointPPStaticText","PowerPointTextStyleLevel" "PowerPointLineFormat","PowerPointPPStrings","PowerPointTextStyleLevels" "PowerPointLinkFormat","PowerPointPPTabControl","PowerPointTextStyles" "PowerPointMarker","PowerPointSlideShowWindows","PowerPointThreeDFormat" "PowerPointMaster","PowerPointSoundEffect","PowerPointView" "PowerPointPPGroupBox","PowerPointSoundFormat","PowerPointWebOptions" "PowerPointPPIcon","PowerPointTable","" .. _/topics/office_automation/reserved_words_when_using_microsoft_office_automation/word_reserved_words: Word reserved words ******************* .. csv-table:: :header: "Reserved Word", "Reserved Word", "Reserved Word" :widths: 25, 25, 25 "WordAddin","WordDropDown","WordIApplicationEvents2" "WordAddIns","WordEmail","WordIndex" "WordAdjustments","WordEmailAuthor","WordIndexes" "WordApplication","WordEmailOptions","WordInlineShape" "WordAutoCaption","WordEmailSignature","WordInlineShapes" "WordAutoCaptions","WordEndnote","WordKeyBinding" "WordAutoCorrect","WordEndnotes","WordKeyBindings" "WordAutoCorrectEntries","WordEnvelope","WordKeysBoundTo" "WordAutoCorrectEntry","WordField","WordLanguage" "WordAutoTextEntries","WordFields","WordLanguages" "WordAutoTextEntry","WordFileConverter","WordLetterContent" "WordBookmark","WordFileConverters","WordLineFormat" "WordBookmarks","WordFillFormat","WordLineNumbering" "WordBorder","WordFind","WordLinkFormat" "WordBorders","WordFirstLetterException","WordList" "WordBrowser","WordFirstLetterExceptions","WordListEntries" "WordCalloutFormat","WordFont","WordListEntry" "WordCaptionLabel","WordFontNames","WordListFormat" "WordCaptionLabels","WordFootnote","WordListGalleries" "WordCell","WordFootnotes","WordListGallery" "WordCells","WordFormField","WordListLevel" "WordCharacters","WordFormFields","WordListLevels" "WordCheckBox","WordFrame","WordListParagraphs" "WordColorFormat","WordFrames","WordLists" "WordColumn","WordFrameset","WordListTemplate" "WordColumns","WordFreeformBuilder","WordListTemplates" "WordComment","WordGlobal","WordMailer" "WordComments","WordGroupShapes","WordMailingLabel" "WordConnectorFormat","WordHangulAndAlphabetException","WordMailMerge" "WordCustomLabel","WordHangulAndAlphabetExceptions","WordMailMergeDataField" "WordCustomLabels","WordHangulHanjaConversionDictionaries","WordMailMergeDataFields" "WordDefaultWebOptions","WordHeaderFooter","WordMailMergeDataSource" "WordDialog","WordHeadersFooters","WordMailMergeField" "WordDialogs","WordHeadingStyle","WordMailMergeFieldName" "WordDictionaries","WordHeadingStyles","WordMailMergeFieldNames" "WordDictionary","WordHorizontalLineFormat","WordMailMergeFields" "WordDocument","WordHyperlink","WordMailMessage" "WordDocuments","WordHyperlinks","WordOLEControl" "WordDropCap","WordIApplicationEvents","WordOLEFormat" "WordOptions","WordSentences","WordTabStop" "WordOtherCorrectionsException","WordShading","WordTabStops" "WordOtherCorrectionsExceptions","WordShadowFormat","WordTask" "WordPageNumber","WordShape","WordTasks" "WordPageNumbers","WordShapeNode","WordTemplate" "WordPageSetup","WordShapeNodes","WordTemplates" "WordPane","WordShapeRange","WordTextColumn" "WordPanes","WordShapes","WordTextColumns" "WordParagraph","WordSpellingSuggestion","WordTextEffectFormat" "WordParagraphFormat","WordSpellingSuggestions","WordTextFrame" "WordParagraphs","WordStoryRanges","WordTextInput" "WordPictureFormat","WordStyle","WordTextRetrievalMode" "WordProofreadingErrors","WordStyles","WordThreeDFormat" "WordRange","WordSubdocument","WordTwoInitialCapsException" "WordReadabilityStatistic","WordSubdocuments","WordTwoInitialCapsExceptions" "WordReadabilityStatistics","WordSynonymInfo","WordVariable" "WordRecentFile","WordSystem","WordVariables" "WordRecentFiles","WordTable","WordVersion" "WordReplacement","WordTableOfAuthorities","WordVersions" "WordRevision","WordTableOfAuthoritiesCategory","WordView" "WordRevisions","WordTableOfContents","WordWebOptions" "WordRoutingSlip","WordTableOfFigures","WordWindow" "WordRow","WordTables","WordWindows" "WordRows","WordTablesOfAuthorities","WordWords" "WordSection","WordTablesOfAuthoritiesCategories","WordWrapFormat" "WordSections","WordTablesOfContents","WordZoom" "WordSelection","WordTablesOfFigures","WordZooms" .. _/topics/office_automation/reserved_words_when_using_microsoft_office_automation/see_also: .. seealso:: :doc:`Controlling Microsoft Office from your app` topic OS information ============== .. toctree:: :maxdepth: 1 :name: sec-os_information Conditional compilation Handle different operating systems ======================= Conditional compilation ======================= In the process of creating an application that runs on multiple platforms, you may find that you have some code that is only needed on a single platform. An example of this might be code that saves preferences. On Windows you might want to use the Registry, but on Mac you might want to use a plist. You can handle these special cases using conditional compilation. With conditional compilation, you are telling the compiler to include or exclude specific parts of your code depending on what is platform and/or target being compiled. This is done using the :doc:`#If` command in conjunction with :doc:`#Else, #ElseIf and #Endif`. .. _/topics/os_information/conditional_compilation/usage: Usage ----- You can use these commands along with any constant to selectively include or exclude code when building. There are many built-in compiler constants you can use with conditional compilation. Some commonly used constants include: * DebugBuild * TargetWindows * TargetMacOS * Target64bit For example if you have code that should only be included when you are running in debug mode from the IDE, you would do this: .. code:: xojo #If DebugBuild Then ' Activate logging App.Logging = True #Else App.Logging = False #Endif This code could be used to handle saving preferences differently based on platform: .. code:: xojo #If TargetWindows Then ' Use Registry SavePreferencesToRegistry #ElseIf TargetMacOS Then ' Use a plist SavePreferencesToPList #ElseIf TargetLinux Then ' Use generic XML SavePreferencesToXML #Endif You can even use your own constants, such as this to check if a beta has expired: .. code:: xojo #If App.kBetaBuild Then ' Check if the beta has expired If DateTime.Now.Year > 2022 Then Quit End If #Endif For more information, refer to . .. _/topics/os_information/conditional_compilation/selecting_a_target_for_a_class/method_in_inspector: Selecting a target for a class/method in Inspector -------------------------------------------------- .. image:: https://documentation.xojo.com/topics/os_information/images/conditional_compilation_include_in_options.png You can indicate that a particular project item (excluding Windows, Web Pages and iOS Views) should only be included for certain project types. For example, you may have a class that you only want to include when you are creating a web app. You do this using the “Include In” properties in the Advanced tab of the Inspector (the are often referred to as Compatibility Flags). To get to the Advanced tab, click the gear button at the top of the Inspector. This shows the advanced options. The "Include In" section has properties for the target (Desktop, Web, Console, IOS) and architecture (32-bit or 64-bit). By default everything is checked, but you can check any combination. When your project is being built, the project item gets excluded from any target/architecture that does not match what is checked. You can also set “Include In” for methods, properties, constants, event definitions, enums, structures, delegates and anything else you can add to a project item. .. _/topics/os_information/conditional_compilation/see_also: .. seealso:: :doc:`#If...#Endif` commands; :doc:`Constants` topic ================================== Handle different operating systems ================================== .. _/topics/os_information/handle_different_operating_systems/how_can_i_check_whether_the_user_is_running_on_windows,_macos,_linux_or_raspberry_pi?: How can I check whether the user is running on Windows, macOS, Linux or Raspberry Pi? ------------------------------------------------------------------------------------- The compilation constants tell you which OS the app is currently running on. .. code:: xojo If TargetWindows Then ' Running on Windows ElseIf TargetMacOS Then ' Running on macOS ElseIf TargetLinux And TargetARM Then ' Running on Raspberry Pi ElseIf TargetLinux Then ' Running on Linux ElseIf TargetiOS Then ' Running on iOS End If .. _/topics/os_information/handle_different_operating_systems/on_macos,_examples_projects_do_not_appear_in_the_project_chooser_and_plugins_are_not_loading.: On macOS, examples projects do not appear in the Project Chooser and plugins are not loading. --------------------------------------------------------------------------------------------- Newer versions of macOS are supposed to remove the "quarantine attribute" once the code signature for an app has been verified. There are times when macOS does not do this properly, resulting in the inaccessibility of files and folders that are stored alongside the app. If this happens with Xojo itself, then the symptoms described above occur. The fix is to manually remove the quarantine attribute. Make sure Xojo is not running, start the Terminal and switch to the folder containing Xojo and run this command: .. code:: xojo xattr -d com.apple.quarantine Xojo.app Restart Xojo and the examples and the plugins should load now. Printing ======== .. toctree:: :maxdepth: 1 :name: sec-printing Creating PDF forms Sending data to the printer The Report Editor ================== Creating PDF forms ================== A PDF form is a PDF document in which the user can enter data via various controls and then submit the form to be either emailed to an address to sent to a server. .. _/topics/printing/creating_pdf_forms/pdf_controls: PDF controls ------------ The available controls are :doc:`PDFTextField`, :doc:`PDFTextArea`, :doc:`PDFCheckBox`, :doc:`PDFRadioButton`, :doc:`PDFPopupMenu`, :doc:`PDFComboBox` and :doc:`PDFListBox`. PDF controls can **only** be added to PDF documents you create in Xojo via the :doc:`PDFDocument` class. A control is added to a :doc:`PDFDocument` object via it's :ref:`AddControl` method. .. note:: PDF documents can be created via a PDF template which describes a PDF document (which can include controls) in JSON format. See :ref:`PDFDocument` for details. .. _/topics/printing/creating_pdf_forms/drawing_controls: Drawing controls ---------------- Unlike their desktop, web and mobile counterparts, :doc:`PDFTextField`, :doc:`PDFTextArea`, :doc:`PDFCheckBox`, and :doc:`PDFRadioButton` do not draw the entire control. For :doc:`PDFTextField` and :doc:`PDFTextArea`, no border is drawn. For :doc:`PDFCheckBox` and :doc:`PDFRadioButton` no captions are drawn. :doc:`PDFButton` draws the caption but not the border around the button itself. If you want to add these, you can do so by drawing rectangles for borders and text for captions. .. _/topics/printing/creating_pdf_forms/displaying_the_form_to_the_user: Displaying the form to the user ------------------------------- To display it to the user so that they can add information and submit it, the :doc:`PDFDocument` must be saved to disk and then opened in a PDF viewer app that fully supports forms such as Acrobat Reader. .. _/topics/printing/creating_pdf_forms/sending_a_pdf_form_via_email_or_to_a_server: Sending a PDF form via email or to a server ------------------------------------------- After the user enters information into the various controls added to the PDF file, the data can be sent elsewhere by clicking on a button that has been set to either create an email with the PDF file attached, send the data from the form to a server or send the entire PDF file itself to a server. All are done by setting the :ref:`Action` property of the a :doc:`PDFButton`. .. warning:: As of MacOS Big Sur, Apple's Preview app does not support actions for buttons in PDF files. .. _/topics/printing/creating_pdf_forms/example_code: Example code ------------ This example generates a :doc:`PDFDocument` that includes all the various PDF controls and then saves the document to the desktop. You can then open it with Acrobat Reader or any other PDF viewer that supports button actions. Fill out the form and then press the Send button. A draft email to hello@example.com will be created as a draft email will then be created with the filled out PDF form attached. .. code:: xojo Var d As New PDFDocument d.Compressed = False Var f As FolderItem = SpecialFolder.Desktop.Child("AcroFormTest.pdf") Var g As Graphics = d.Graphics ' PDFForm - TextField control Var tf As New PDFTextField(1, 100, 50, 200, 22, "TextField", "Some sample text") d.AddControl(tf) ' PDFForm - TextArea control Var ta As New PDFTextArea(1, 100, 100, 200, 300, "CampoTexto", "Hi There!") d.AddControl(ta) ' PDFForm - Button control with Action set to default = "Reset" Var btn1 As New PDFButton(1, 320, 100, 50, 22, "Reset","Reset") d.AddControl(btn1) ' PDFForm - CheckBox control Var chB1 As New PDFCheckbox(1, 100, 430,15, 15,"Checkbox 1","checkbox") d.AddControl(chB1) Var chB2 As New PDFCheckbox(1, 200, 430, 15, 15,"Checkbox 2", "Checkbox 2") chB2.Value = True d.AddControl(chB2) ' PDFForm - RadioButton group control Var rBtn1GroupA As New PDFRadioButton(1, 200, 450, 15,15,"GroupA","OptionA") Var rBtn2GroupA As New PDFRadioButton(1, 200, 470, 15, 15,"GroupA", "OptionB") rBtn2GroupA.Value = True d.AddControl(rBtn1GroupA) d.AddControl(rBtn2GroupA) Var rBtn1GroupB As New PDFRadioButton(1, 200, 500, 15,15,"GroupB","OptionA") rBtn1GroupB.Value = True Var rBtn2GroupB As New PDFRadioButton(1, 200, 520, 15, 15,"GroupB", "OptionB") d.AddControl(rBtn1GroupB) d.AddControl(rBtn2GroupB) ' PDFForm - ChoiceList control, set to Editable ComboBox Var cCombo As New PDFComboBox(1,100,540,100,15,"ListC","Option A", "Option B", "Option C", "Option D") cCombo.SelectedRowText = "Option C" d.AddControl(cCombo) ' PDFForm - ChoiceList control, set to No-Editable ComboBox Var cPopup As New PDFPopupMenu(1,210,540,100,15,"List","Option A", "Option B", "Option C", "Option D") d.AddControl(cPopup) ' PDFForm - ChoiceList control, set to ListBox with Multiple Selections Var cListBox As New PDFListBox(1,320,540,100,90,"ListB","Option A", "Option B", "Option C", "Option D") cListBox.RowSelectionType = PDFListBox.RowSelectionTypes.Multiple cListBox.SelectedRowText = "Option D" d.AddControl(cListBox) ' PDFForm - Button control with Action set to "Submit" Var btn2 As New PDFButton(1, 320, 660, 50, 22, "Send","Send") btn2.Action = PDFButton.Actions.SendPDFFile btn2.URL = "mailto:hello@example.com" btn2.Caption = "TestDocument" d.AddControl(btn2) ' Drawing for the PDFForm Layout -> rectangles surrounding widget areas and labels g.DrawRectangle(95,45,210,32) g.DrawRectangle(95,95,210,310) g.DrawRoundRectangle(320,95,50, 32, 10, 10) g.DrawRectangle(100,430,15,15) g.DrawRectangle(200,430,15,15) Var y As Integer = 430 + (7.5 + ((g.TextHeight)/2)) g.DrawText("Xojo rocks!", 125,y) g.DrawText("I Love Native Multiplatform", 225,y) y = 450 + (7.5 + ((g.TextHeight)/2)) g.DrawRectangle(200,450,15,15) g.DrawText("Option A", 225,y) y = 470 + (7.5 + ((g.TextHeight)/2)) g.DrawRectangle(200,470,15,15) g.DrawText("Option B", 225,y) g.DrawRectangle(200,500,15,15) y = 500 + (7.5 + ((g.TextHeight)/2)) g.DrawText("Option A-1", 225,y) g.DrawRectangle(200,520,15,15) y = 520 + (7.5 + ((g.TextHeight)/2)) g.DrawText("Option B-1", 225,y) g.DrawRectangle(100,540,100,15) g.DrawRectangle(320,540,100,90) g.DrawRoundRectangle(320,655,50, 32, 10, 10) g.DrawRectangle(210,540,100,15) ' Saving the PDF document instance and opening it in the by default viewer App d.Save(f) .. _/topics/printing/creating_pdf_forms/see_also: .. seealso:: :doc:`PDFDocument`, :doc:`PDFButton`, :doc:`PDFCheckBox`, :doc:`PDFRadioButton`, :doc:`PDFTextField`, :doc:`PDFTextArea`, :doc:`PDFPopupMenu`, :doc:`PDFComboBox`, and :doc:`PDFListBox` classes. =========================== Sending data to the printer =========================== For desktop apps, printing is very similar to drawing to the screen. But instead of drawing into a Graphics object of a Canvas control, you draw to a Graphics object created specifically for printing. There are methods available to allow you to create new pages and get printer settings. For web apps, printing is far more limited. Usually what works best is to create HTML, display it in an :doc:`HTML Viewer` control and then print that. .. _/topics/printing/sending_data_to_the_printer/desktop_printing: Desktop printing ---------------- .. _/topics/printing/sending_data_to_the_printer/printer_settings: Printer settings **************** Before printing anything, you usually want to give the user the ability to select the printer they want to use. This is done using the :doc:`PrinterSetup` class, which displays the Page Setup dialog for the operating system. In addition, this class returns the settings so that you can use them again later without prompting the user. .. code:: xojo ' PrinterSettings is a String property on the Window Var ps As New PrinterSetup ps.Settings = mPrinterSettings If ps.ShowPageSetupDialog Then PrinterSettings = ps.Settings End If Since PrinterSettings is a String, you can save it outside of your app (such as in a database or a file) for use the next time the user prints. The PrinterSetup class has properties for printer settings such as landscape, page size and resolution. The :doc:`PrinterSetup` page has more details on these properties. PrinterSetup is not supported in Linux apps. .. _/topics/printing/sending_data_to_the_printer/printing_text_and_graphics: Printing text and graphics ************************** To print text and graphics you draw to the Graphics object for the printer. To get this Graphics objects, you call either the :ref:`ShowPrinterDialog` or :ref:`PrinterSetup.OpenPrinter` global methods. The only difference between these two methods is that one displays the Print dialog and the other does not. Since the printing system on macOS has the automatic capability to print to PDF, you can use this method to easily generate PDF files. On Windows you can install a "Print to PDF" printer driver to enable sending printed output to PDF files. If you need to generate PDF files, you can also use the :doc:`PDFDocument` class. Since you are drawing to a Graphics object, all the commands covered in the :doc:`Getting Started with Graphics` topic can be used. In addition, you also use the printing-specific method, NextPage to create multiple pages. This code prints “Hello” on page 1 and “World” on page 2: .. code:: xojo Var ps As New PrinterSetup If ps.ShowPageSetupDialog Then Var page As Graphics page = ps.ShowPrinterDialog If page <> Nil Then ' Draw text on page 1 with 1 inch left/top margin page.DrawText("Hello", ps.HorizontalResolution, ps.VerticalResolution) page.NextPage ' Draw text on page 2 with 1 inch left/top margin page.DrawText("World", ps.HorizontalResolution, ps.VerticalResolution) End If End If You can also use any printer settings that were previously stored from using the PrinterSetup class. .. code:: xojo Var ps As New PrinterSetup ps.Settings = PrinterSettings ' Property containing previously saved settings If ps.ShowPageSetupDialog Then PrinterSettings = ps.Settings ' Get new settings Var page As Graphics ' Use Printer Settings page = ps.ShowPrinterDialog If page <> Nil Then ' Draw text on page 1 with 1 inch left/top margin page.DrawText("Hello", ps.HorizontalResolution, ps.VerticalResolution) page.NextPage ' Draw text on page 2 with 1 inch left/top margin page.DrawText("World", ps.HorizontalResolution, ps.VerticalResolution) End If End If Each time you call NextPage, the current page is sent to the printer and the graphics object (in this case page) is cleared so that you can immediately begin drawing the new content. The final page gets sent to the printer when the page goes out of scope. To print without displaying the Printer dialog, you call the OpenPrinter method: .. code:: xojo Var ps As New PrinterSetup Var page As Graphics page = PrinterSetup.OpenPrinter(ps) If page <> Nil Then ' Draw text on page 1 with 1 inch left/top margin page.DrawText("Hello", ps.HorizontalResolution, ps.VerticalResolution) page.NextPage ' Draw text on page 2 with 1 inch left/top margin page.DrawText("World", ps.HorizontalResolution, ps.VerticalResolution) page.NextPage End If .. _/topics/printing/sending_data_to_the_printer/printing_multiple_pages_of_text: Printing multiple pages of text ******************************* To print multiple pages, you need to parse each line and print it if it will fit on the page. If it will not fit, then you call NextPage so that your text will start printing at the top of the next page. A common thing to do is to split the text into paragraphs and then track the height of the page and the height of the next paragraph to print to make sure that it will fit onto the page. If it will not fit, then the current page is sent to the printer and a new page is created to start printing the next paragraph. This code shows you how you can print text from a TextArea onto multiple pages: .. code:: xojo Var ps As New PrinterSetup If ps.ShowPageSetupDialog Then Var page As Graphics page = ps.ShowPrinterDialog If page <> Nil Then ' Draw text on page 1 with 1 inch left/top margin Var leftMargin As Double = ps.HorizontalResolution Var topMargin As Double = ps.VerticalResolution page.FontSize = FontPopup.Text.Val ' To print multiple pages, you need to parse each line and print it ' if it will fit on the page. If it will not fit, then ' you call NextPage so that your text will start printing at the top ' of the next page. ' First, split the text into multiple paragraphs Var paragraphs() As String = TextArea1.Text.ReplaceLineEndings(EndOfLine).ToArray(EndOfLine) Var pageTextHeight As Double = topMargin Var newpageTextHeight As Double Var lineWidth As Double For i As Integer = 0 To paragraphs.LastIndex newpageTextHeight = pageTextHeight + page.TextHeight(paragraphs(i), ps.Width - leftMargin * 2) If newPageTextHeight > ps.Height - topMargin * 2 Then ' String does not fit on page, so move to next page page.NextPage pageTextHeight = topMargin newpageTextHeight = pageTextHeight + page.TextHeight(paragraphs(i), ps.Width - leftMargin * 2) End If ' If string fits on page, then draw it page.DrawText(paragraphs(i), leftMargin, pageTextHeight, ps.Width - leftMargin * 2) ' Keep a cumulative count of the pageheight as strings are added to it pageTextHeight = newpageTextHeight ' Adjust page text height pageTextHeight = pageTextHeight + topMargin / 8 ' 1/8 inch between paragraphs Next End If End If The above code gives you a lot of control, but it is more work to track everything yourself. In the next section you'll see how you can take advantage of StyledTextPrinter to make this easier. .. _/topics/printing/sending_data_to_the_printer/printing_styled_text: Printing styled text ******************** Because TextAreas are capable of displaying styled text and multiple font sizes, you will usually want to retain the styled text when you print. The :doc:`StyledTextPrinter` class is used for this purpose, using the DrawBlock method. The StyledTextPrinter class only works with MacOS apps. To print styled text, you first create a StyledTextPrinter object and then call the StyledTextPrinter method of the TextArea (specifying the graphics to use and the width of the text) to get an instance of a StyledTextPrinter that can be used for printing. With this instance, you can call the DrawBlock method to draw the styled text to the page (specifying the starting coordinates and the height). This code prints styled text in a TextArea: .. code:: xojo Var stp As StyledTextPrinter Var g As Graphics Var p As New PrinterSetup If p.ShowPageSetupDialog Then g = ps.ShowPrinterDialog If g <> Nil Then ' Set width as 7.5 inches stp = PrintTextArea.StyledTextPrinter(g, 7.5 * p.HorizontalResolution) Do Until stp.EndOfText ' Fill the page with text ' at 10 inches height stp.DrawBlock(0, 0, 10 * p.VerticalResolution) If Not stp.EndOfText Then ' There is more text, so add a page g.NextPage End If Loop End If End If In order to support styled printing, the Text Area must have both its Multiline and Styled properties ON (True). If the text to print is larger than what will fit in the specified block, then you can iterate through the text until it is all printed. You do this by checking the EOF property of the StyledTextPrinter class after each call to DrawBlock. This code prints the contents of a Text Area into two columns with a quarter inch spacing between the columns: .. code:: xojo Var g As Graphics Var p As New PrinterSetup If p.ShowPageSetupDialog Then g = ps.ShowPrinterDialog ' 1 inch margin Var leftRightMargin As Double = 1 * p.HorizontalResolution Var topBottomMargin As Double = 1 * p.VerticalResolution ' Page width after accounting for margins Var pageWidth As Double = p.Width - (leftRightMargin * 2) ' Column gap is 1/4 of the margin (so, 1/4 inch) Var columnGap As Double = leftRightMargin / 4 ' Calculate the column width ' Subtract column gap from page with and ' split result 2 Var columnWidth As Double = (pageWidth - columnGap) / 2 ' Page size after accounting for margins Var pageHeight As Double = p.Height - (topBottomMargin * 2) Var stp As StyledTextPrinter stp = PrintTextArea.StyledTextPrinter(g, pageWidth) stp.Width = columnWidth Var columnToPrint As Integer = 1 Do Until stp.EndOfText stp.DrawBlock(leftRightMargin + (columnWidth + columnGap) * (columnToPrint - 1), _ topBottomMargin, pageHeight) If columnToPrint = 2 Then ' printing last column If Not stp.EndOfText Then ' more text to print g.NextPage columnToPrint = 1 End If Else ' more columns to print on this page columnToPrint = columnToPrint + 1 End If Loop End If .. _/topics/printing/sending_data_to_the_printer/html_printing: HTML printing ************* Another technique you can use is to create what you want to print using HTML, display it in an HTMLViewer and then call its print method. This can sometimes be useful for simple reports. As a simple example, this code loads an HTMLViewer with some HTML: .. code:: xojo Var html As String = "Hello, World!" HTMLViewer1.LoadPage(html, FolderItem.TemporaryFile) This code then displays the print dialog for the user to print the HTML: .. code:: xojo HTMLViewer1.Print(True) .. _/topics/printing/sending_data_to_the_printer/web_printing: Web printing ------------ Web printing is considerably simpler than desktop printing because it is much more restricted. Your web application runs in a web browser, so you are limited by what a web browser can print. Web browsers generally do a good job of printing HTML, so in order to generate something to print, you want to first render it as HTML (either to a string or a file) and use an HTML Viewer control to display the HTML. Once you have what you want displayed in an HTML Viewer, you can have a button that calls the Print method of the HTML Viewer to ask the browser to print its contents: .. code:: xojo HTMLViewer1.Print .. _/topics/printing/sending_data_to_the_printer/see_also: .. seealso:: :doc:`Graphics`, :doc:`PrinterSetup` classes; :doc:`Getting Started with Graphics` topic The Report Editor ================= .. toctree:: :maxdepth: 1 :name: sec-the_report_editor Displaying Reports Report Editor Controls ================== Displaying reports ================== Once you have designed your report with the :doc:`Report Layout Editor`, you have to provide data for it to display. This is called the data set. Databases are commonly used as data sets for reports, but you can also use any data you want by using the Reports.DataSet interface. .. _/topics/printing/the_report_editor/displaying_reports/using_a_database_as_a_data_set: Using a database as a data set ------------------------------ It is simple to use a database as the data set. You get the data you want into a RowSet. Then you can pass the RowSet to the report for it to use as its data set. Here is an example: .. code:: xojo ' teamRowSet comes from a database ' SELECT statement Var ps As New PrinterSetup Var rpt As New TeamReport If rpt.Run(teamRowSet, ps) Then If rpt.Document <> Nil Then ' Save the document for ' the Canvas to display mReportDocument = rpt.Document End If End If .. _/topics/printing/the_report_editor/displaying_reports/using_text_as_a_data_set: Using text as a data set ------------------------ To use anything else as a data set for a report, such a text file, you create a custom class that implements the :doc:`Reports.DataSet` interface. This interface specifies these methods: EOF As Boolean, Field(Integer) As Variant, Field(String) As Variant, NextRecord As Boolean, Run, Type(String) As Integer. In these methods, you fetch the specific data you need from your actual data source (perhaps a text or XML file) and then return the result or do the expected action. As a simple example, you can put the team names in a string array and then create a class to work with this array. You have to supply the code for each method in the interface. To start with, create a class and call it TeamDataSet. Add the Reports.DataSet interface to it. Now add the array to contain the team names as a property of this class: .. code:: xojo mTeams() As String Create a Constructor method for the class and initialize the array there: .. code:: xojo mTeams = Array("Seagulls", "Pigeons", "Crows") Now you can begin implementing the interface methods. Start with NextRecord as it is the simplest. The NextRecord method is used to move the current array position, but before that can be implemented, another property to track the current array position is needed: .. code:: xojo mArrayPosition As Integer Now you can add the code to move the array position to the NextRecord method: .. code:: xojo mArrayPosition = mArrayPosition + 1 EOF is next. EOF should return True when there is no more data in the array and False if there is still data. .. code:: xojo If mArrayPosition > mTeams.LastIndex Then Return True Else Return False End If The above code checks if the current array position will be beyond the size of the array. Moving along, the Type method is used to return the type of a specified field name. The array only has one field, the team name, so this method should check for the field name of “TeamName” and return its type, which is String. The type is indicated by the same integer value that specifies types when working with databases. For String, this is 5. Note: You can find the complete table of data type values in the :ref:`Database Operations` topic. .. code:: xojo If fieldName = "TeamName" Then Return 5 Else Return 0 End If The two Field methods are used to get the value for the specified field in the current array position. One Field method uses an integer field position and the other uses the field name. There is only one field, so that simplifies things. For Field(String) As Variant: .. code:: xojo If name = "TeamName" Then Return mTeams(mArrayPosition) Else Return "" End If The name is matched to what is specified for the DataField property for each ReportField in the Report Layout. For Field(Integer) As Variant, have it use the previous code: .. code:: xojo If idx = 0 Then Return Field("TeamName") Else Return "" End If Lastly, you need to implement the Run method. This method is called to start the process of getting the data. It is also simple because all you need to do for this example is initialize the array position: .. code:: xojo mArrayPosition = 0 With this class in place, you can now use it with the Report.Run method to populate the report with your data: .. code:: xojo Var teamData As New TeamDataSet Var ps As New PrinterSetup Var rpt As New TeamReport If rpt.Run(teamData, ps) Then If rpt.Document <> Nil Then ' Save the document for ' the Canvas to display mReportDocument = rpt.Document End If End If .. _/topics/printing/the_report_editor/displaying_reports/displaying_the_report: Displaying the report --------------------- The Report Layout Editor lets you design your reports, but there is no way to preview them without running your app. Reports are generated as pictures, so you can display them by running the report with the data set and then displaying the generated report in a Canvas. Of course since the report is a Picture, you can do anything you can do with a Picture as well, such as save it directly to a FolderItem or database. The above examples show how to run the report and have a comment that the document is saved for the Canvas to display. In order to display the report, the Document for the report is saved to a property of the window: .. code:: xojo mReportDocument As Reports.RBReportDocument The RBReportDocument class has a property to tell you how many pages are in the report (PageCount) and methods that are used to display, print and save the report (Page, Print, Save). .. image:: https://documentation.xojo.com/topics/printing/the_report_editor/images/displaying_reports_list_of_products_report.png To display the report, you get one page at a time using the Page method (which returns a Picture containing the page of the report). You then use a Canvas to display the picture. For example, this code in the Paint event of a Canvas displays page 1 of a report: .. code:: xojo If mReportDocument <> Nil Then g.DrawPicture(mReportDocument.Page(1), 0, 0) End If This code always only shows page 1 of the report. For an actual report preview, you usually display one page at a time and have Back/Next buttons to move between pages. The Back/Next buttons could alter a mCurrentDisplayPage property that you would use here in the Paint event in place of the “1” for the page number: .. code:: xojo If mReportDocument <> Nil Then g.DrawPicture(mReportDocument.Page(mCurrentDisplayPage), 0, 0) End If The PageCount property tells you how many pages there are in total. .. _/topics/printing/the_report_editor/displaying_reports/printing_the_report: Printing the report ------------------- Printing a report works similarly to printing in general. You use PrinterSetup and OpenPrinter (or OpenPrinterDialog) to get a Graphics object. You then call the Print method of the Report Document to print it: .. code:: xojo ' teamRowSet comes from a database SELECT statement Var ps As New PrinterSetup Var rpt As New TeamReport If ps.ShowPageSetupDialog Then Var g As Graphics g = ps.ShowPrinterDialog If g <> Nil Then If rpt.Run(teamRowSet, ps) Then If rpt.Document <> Nil Then ' Print the report rpt.Document.Print(g) End If End If End If End If .. _/topics/printing/the_report_editor/displaying_reports/saving_the_report: Saving the report ----------------- To save your report, you have a couple options. If you are using macOS, you can let the user take advantage of its ability to print directly to a PDF file. The user can use the macOS print dialog to select to save the report to a PDF file rather than sending it to the printer. Your other choice is to save the Pictures to disk rather than drawing them on screen as this code shows: .. code:: xojo Var teamData As New TeamDataSet Var ps As New PrinterSetup Var rpt As New TeamReport If rpt.Run(teamData, ps) Then If rpt.Document <> Nil Then Var saveFolder As FolderItem saveFolder = FolderItem.ShowSelectFolderDialog If saveFolder <> Nil Then Var saveFile As FolderItem For i As Integer = 1 To rpt.Document.PageCount saveFile = saveFolder.Child("TeamReport" + i.ToString + ".png") rpt.Document.Page(i).Save(saveFile, Picture.SaveAsPNG) Next End If End If End If .. _/topics/printing/the_report_editor/displaying_reports/see_also: .. seealso:: :doc:`Reports Module` module; :doc:`Reports.DataSet` interface; :doc:`Report Layout Editor` topic ====================== Report Editor controls ====================== These are the controls that are used with the Report Layout Editor. They are in the Reports group of the Library. For information on how to use the Report Editor to create your own reports, refer here: * :doc:`Report Layout Editor` * :doc:`Displaying Reports` .. image:: https://documentation.xojo.com/topics/printing/the_report_editor/images/report_editor_controls_report_layout_editor.png These are the controls that can be used on report layouts. .. _/topics/printing/the_report_editor/report_editor_controls/report_field: Report Field ------------ A Report Field maps to the data that you are displaying in the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_label: Report Label ------------ A simple static label to display on the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_picture: Report Picture -------------- A picture (from the project) to display on the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_line: Report Line ----------- Draws a line on the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_oval: Report Oval ----------- Draws an oval on the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_rectangle: Report Rectangle ---------------- Draws a rectangle on the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_round_rectangle: Report Round Rectangle ---------------------- Draws a round rectangle on the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_date: Report Date ----------- Used to display the current date on the report. .. _/topics/printing/the_report_editor/report_editor_controls/report_page_number: Report Page Number ------------------ Used to display the report page number. .. _/topics/printing/the_report_editor/report_editor_controls/see_also: .. seealso:: :doc:`Report Layout Editor`, :doc:`Displaying Reports` topics Raspberry Pi ============ .. toctree:: :maxdepth: 1 :name: sec-raspberry_pi Xojo and Raspberry Pi Xojo Dojo Making a LED light blink part I <_making_a_led_light_blink_part_i> Making a LED light blink part II <_making_a_led_light_blink_part_ii> Working with a LCD character display Controlling a servo GPIO RGB LED Remote debugging Tips Using a buzzer ====================== Xojo and Raspberry Pi ====================== .. _/getting_started/creating_more_apps/raspberry_pi/introduction_to_xojo_pi/what_is_the_raspberry_pi?: What is the Raspberry Pi? ------------------------- `Raspberry Pi` is essentially a tiny, inexpensive computer (about US$35). Because it is tiny, it can be used in all kinds of things that a typical computer does not work well with, such as robotics and embedded systems. Since it is also inexpensive, it functions as a great learning tool, making it possible for anyone to have their own computer. Additionally, because a `Raspberry Pi` is a fully functional computer, with input/output, storage and wifi capabilities, it can also be used to interface and control other things. This makes the `Raspberry Pi` a favorite amongst tinkerers, Makers, electronics hobbyists and anyone else with a cool idea for a project. The `Raspberry Pi` and other similar computers are also referred to as "single-board" computers. What makes the `Raspberry Pi` somewhat unique is that it uses an ARM processor (similar to what you see on cell phones and tablets and Mac computers), rather than an Intel or AMD CPU that is in traditional PCs. Still, a `Raspberry Pi` is a full computer. You can connect a keyboard, mouse and display to it. You can plug in storage and install an operating system on it (typically Linux). Now it is not a powerful computer, to be sure, but it is still powerful enough for many tasks. Xojo Pi licenses let you build console and desktop apps for the `Raspberry Pi` for free. Log into your account at xojo.com and go to the `Xojo Pi Store `_ page to get yours. .. _/getting_started/creating_more_apps/raspberry_pi/introduction_to_xojo_pi/the_xojo_ide_and_raspberry_pi: The Xojo IDE and Raspberry Pi ------------------------- Xojo itself does not run on the `Raspberry Pi`, but you can run Xojo on a Windows, Mac or Linux computer and use it to make apps for the `Raspberry Pi`. Specifically Xojo can build console, desktop and web apps for the `Raspberry Pi` 2, 3 or later. You can use the Remote Debugger to write your code on Windows/Mac/Linux and run it for testing on the `Raspberry Pi`. .. youtube:: U2SOGkmEwqE Xojo is also able to build apps for other similar "single-board" computers that use the ARMv7 or later CPU. Download the `ARMTest console app `_ to run on a device to see if it will be compatible with Xojo apps. .. warning:: The original `Raspberry Pi` and the `Raspberry Pi` Zero are not compatible with Xojo apps because they both use the older ARMv6. .. _/getting_started/creating_more_apps/raspberry_pi/introduction_to_xojo_pi/creating_a_raspberry_pi_app: Creating a Raspberry Pi app *************************** Creating a `Raspberry Pi` app is no different than creating an app for other targets (Windows, Mac or Linux). Select the type of project you want from the Project Chooser and then when you are ready to build for `Raspberry Pi`, click on Linux in Build Settings (because the `Raspberry Pi` runs a version of Linux) and change the Architecture property in the Inspector to "ARM 32-bit". Build your app and then transfer the built folder to the `Raspberry Pi`. You can also use the Remote Debugger to write your code on Windows/Mac/Linux and run it for testing on the `Raspberry Pi`. .. _/getting_started/creating_more_apps/raspberry_pi/introduction_to_xojo_pi/library_requirements: Library requirements ******************** Starting with 2018r1 you will need to install the libunwind8 library on your Pi in order to run Xojo apps. You can install the library from Terminal with this command: .. code:: xojo sudo apt-get install libunwind8 .. note:: To use an app you have built with Xojo with a Raspberry Pi equipped with a touchscreen, you must login with Xorg rather than Wayland. .. _/getting_started/creating_more_apps/raspberry_pi/introduction_to_xojo_pi/useful_raspberry_pi_links: Useful Raspberry Pi links ------------------------- * `RaspberryPi.org `_ * `CanaKit `_ * `AdaFruit `_ * `SparkFun `_ * `MagPi Magazine `_ * `I Wish I Knew How to … Program Raspberry Pi 2 B and Raspberry Pi 3 B Electronics with Xojo `_ book * :doc:`Programming Raspberry Pi with Xojo` textbook by Xojo .. _/getting_started/creating_more_apps/raspberry_pi/introduction_to_xojo_pi/3rd_party_tutorials_and_projects: 3rd party tutorials and projects -------------------------------- * Einhugur: `Guides for GPIO in Xojo `_ * Lennard Electronics: `Writing to an I2C to parallel port chip `_ * Lennard Electronics: `Reading from an I2C to parallel port chip `_ .. _/getting_started/creating_more_apps/raspberry_pi/introduction_to_xojo_pi/see_also: .. seealso:: :doc:`Raspberry Pi Tips` ========= Xojo Dojo ========= Xojo Dojo is a free and easy way to get started with programming on the Raspberry Pi. With Xojo Dojo you can use the Xojo programming language, along with a few other special commands to make simple Pi programs directly on your Raspberry Pi. These programs can use text, graphics and access the GPIO port. `Download Xojo Dojo for free `_ .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/xojo_dojo_xojo_dojo.png Visit the open-source `Xojo Dojo project on GitLab `_. Xojo Dojo also works on Mac and Windows: * `Download Xojo Dojo for Mac `_ * `Download Xojo Dojo for Windows `_ .. _/getting_started/creating_more_apps/raspberry_pi/xojo_dojo/get_started: Get started ----------- Download Xojo Dojo to your Raspberry Pi and unzip it. Go to the XojoDojo folder and double-click the XojoDojo executable to start it. If the Xojo Dojo app file is not marked as an executable file, you may have to manually do this by right-clicking on it, and selecting "Properties". Switch to the Permissions tab and in the Access Control section make sure Executable is set to "Anyone". Click OK. When Xojo Dojo starts it displays an empty window that is the code editor. You can type some code to get started. For example, this simple code displays text: .. code:: xojo Print "Hello, World!" Type that code and click Run in the toolbar. Xojo Dojo will switch to the Text tab and you'll see the output "Hello, World!". Xojo Dojo includes several sample projects to show you some of its commands and capabilities. To look at an example project, click Open in the File menu and go to the Scripts folder in the XojoDojo folder. These are the included examples: * Bounce: A graphics demo that draws a ball that leaves a multi-colored trail as it bounces around the screen. Press the Stop button on the toolbar to stop the demo. * Boxes: A graphics demo that draws 1000 multi-colored boxes of varying sizes on the screen. * BubbleSort: An implemention of the Bubble Sort sorting algorithm. * DiceRoll: Simulates the rolling of two dice a certain number of times and then tells you how often each dice total amount occurred. * DragonSweep: A graphics demo that draws a fractal "dragon". Based on an IBM PCjr BASIC program from Compute! magazine in March 1986. * GPIOAlarm: With a piezo speaker wired to your Pi using the GPIO, this program makes it sound like an alarm. View the wiring diagram. * GPIOButtonLED: With a a button and LED wired to your Pi using the GPIO, this program lights the LED when the button is pressed. View the wiring diagram. * GPIOLEDBlink: With an LED wired to your Pi using GPIO, this program makes the LED blink. View the wiring diagram. * GPIOTune: With a piezo speaker wired to your Pi using the GPIO, this program plays a short tune. View the wiring diagram. * GuessTheNumber: Try to guess a number picked by the program. * HelloWorld: Simple display of text. * LongLoop: A long-running loop that outputs text. * MazePattern: Displays a text output that looks like a maze (but is not a maze). Based on a Commodore 64 BASIC program from 10 PRINT. * Olympic: Draws the Olympic rings. * ShowPicture: Prompts you to choose a picture on your drive and displays it. * Sieve: Calculates the Sieve of Eratosthenes, popular benchmarking program in the 1980s. * SineWave: Displays a text-based sine wave. .. _/getting_started/creating_more_apps/raspberry_pi/xojo_dojo/xojo_dojo_commands: Xojo dojo commands ------------------ Xojo Dojo uses XojoScript and can thus use all the language commands that are part of Xojo. Xojo Dojo cannot use any of the Xojo frameworks. Xojo Dojo has its own small set of custom commands it can use, which are described below. **ClearGraphicsOutput** Clears the Graphics output screen. **ClearTextOutput** Clears the Text output screen. **DrawLine(x1 As Integer, y1 As Integer, x2 As Integer, y2 As Integer)** Draws a line from position x1, y1 to position x2, y2. **DrawOval(x As Integer, y As Integer, w As Integer, h As Integer)** Draws an oval at position x, y and using with w and height h. To draw a circle, make w and h the same. **DrawPicture(x As Integer, y As Integer, picturePath As String)** Draw the picture at the specified path at position x, y. **DrawRect(x As Integer, y As Integer, w As Integer, h As Integer)** Draws a rectangle at position x, y using width w and height h. **DrawString(s As String, x As Integer, y As Integer, w As Integer = 0, condense As Boolean = False)** Draws a string s on the graphics screen at position x, y. Optionally specify a width and whether the string is condensed if it cannot fit. **EndOfLine As String** The end of line character to use with strings. **EndTimer As Double** Stops a timer started with StartTimer, returning the milliseconds of the elapsed time. **FillOval(x As Integer, y As Integer, w As Integer, h As Integer)** Draws a filled oval at position x, y and using with w and height h. To draw a circle, make w and h the same. Change the fill color using the ForeColor command. **FillRect(x As Integer, y As Integer, w As Integer, h As Integer)** Draws a filled rectangle rectangle at position x, y using width w and height h. Change the fill color using the ForeColor command. **ForeColor(Assigns c As Color)** Sets the color to use for drawing and fill commands. **GPIODigitalRead(pin As Integer) as Integer** Read a value from a GPIO pin. **GPIODigitalWrite(pin As Integer, mode As Integer)** Write a value to a GPIO pin. **GPIOPinMode(pin As Integer, mode As Integer)** Set a GPIO pin as either input or output. **GPIOSetupGPIO** Initialize GPIO. WiringPi library must be installed on the Pi. **GPIOSoftToneCreate(pin As Integer) as Integer** Sets up tones for a pin. **GPIOSoftToneWrite(pin As Integer, freq As Integer)** Sends a tone to a pin. **GPIO_INPUT** Used with GPIO DigitalRead and DigitalWrite commands to indicate a pin is for input. **GPIO_OFF** Used with GPIO DigitalRead and DigitalWrite commands to indicate a pin is off. **GPIO_ON** Used with GPIO DigitalRead and DigitalWrite commands to indicate a pin is on. **GPIO_OUTPUT** Used with GPIO DigitalRead and DigitalWrite commands to indicate a pin is for output. **RandomInt(startValue As Integer, endValue As Integer) as Integer** Generates a random integer between startValue and endValue (inclusive). **ShowGraphicsOutput** Switches to the graphics output screen. **StartTimer** Starts a timer. **UserCancelled As Boolean** Returns True when the user has clicked the Stop button on the toolbar. Use it to end your programs that run in a long loop. **Wait(ms As Double = 10)** Pauses your program execution. The default is 10 milliseconds, but you can specify your own value. .. _/getting_started/creating_more_apps/raspberry_pi/xojo_dojo/learn_more_about_xojo_and_raspberry_pi: Learn more about Xojo and raspberry pi -------------------------------------- If you want to make full-featured apps for the Raspberry Pi you can try the full version of Xojo. You can also check out our free book: :doc:`Programming the Raspberry Pi with Xojo`. ================================ Making a LED light blink part I ================================ In this tutorial, you will create a simple circuit with an LED and will then make the LED blink using a simple Xojo app. .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/parts: Parts ----- In order to build the circuit, you'll need some parts (which are available on `AdaFruit `_): * 1 ribbon cable with Raspberry Pi GPIO connectors * 1 cobbler * 1 breadboard * 3 jumper wires * 1 LED * 1 10k resistor In order to make the LED blink with Xojo, you'll need to install the wiringPi library and grab the WiringPiXojo module, located here: Example Projects/Platform-Specific/RaspberryPi Or you can `download it from GitHub `_. .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/connect_ribon_cable_to_pi: Connect ribbon cable to Pi -------------------------- Connect the ribbon cable to the Raspberry Pi. The white/black side is typically facing the side of the Pi that does not have connectors. There is not a lot of room to work so take your time and make sure the pins are all aligned before you push it down. .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/_making_a_led_light_blink_part_i_pi_to_ribbon_cable.jpg .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/setup_the_breadboard: Setup the breadboard -------------------- A breadboard is the place where you will wire your circuit. The ribbon cable and cobbler are essentially used to extend the GPIO port pins to the breadboard. On the breadboard you can wire everything without soldering. You just plug things into the holes on the breadboard. First, you'll want to plug your cobbler into the breadboard. The cobbler has to be in the center so that the left and right pins are separated by the center channel of the breadboard. In this picture, a "t-cobbler" is used and it is plugged near the top of the board to maximize available space on the board for wiring. .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/_making_a_led_light_blink_part_i_cobbler_on_breadboard.jpg Carefully make sure all the pins are lined up with the holes on the breadboard and push it down. It may take a bit of force, but you want to get the cobbler to be flush with the breadboard. The last step is to connect the cobbler to the Raspberry Pi. Plug the other end of the ribbon cable into the cobbler. It's likely the cobbler will have a slot in it so that the ribbon cable only fits in one direction. With the breadboard connected, you now have access to the pins on the GPIO port. As you can see in the photo above, this cobbler has each of the pins labelled so you can easily tell what they are for without having to count them. Each row on the breadboard that matches up to a pin on the cobbler is "connected" to the pin on the cobbler. Any wires you connect on that row will act as if they are connected to pin on the GPIO port. For example, looking at the cobbler, you can see that the pin marked "#17" is aligned with row 6 on the cobbler. .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/wire_the_circuit: Wire the circuit ---------------- Now that you have the breadboard hooked up to the Raspberry Pi, you can start on building the blinking LED circuit. In this step you'll use 3 wires, 1 LED and a resistor. 1. Connect a wire to the pin marked "#4". Yellow is used in this example. |images/_making_a_led_light_blink_part_i_led_blinker_step_1.jpg| .. |images/_making_a_led_light_blink_part_i_led_blinker_step_1.jpg| image:: images/_making_a_led_light_blink_part_i_led_blinker_step_1.jpg #. Connect the other end of the wire to an open spot on the board away from the cobbler. Here row 24 is used. |images/_making_a_led_light_blink_part_i_led_blinker_step_2.jpg| .. |images/_making_a_led_light_blink_part_i_led_blinker_step_2.jpg| image:: images/_making_a_led_light_blink_part_i_led_blinker_step_2.jpg #. Grab the LED. Note that one of the wires coming from the LED is longer than the other. The longer end is the positive (+) connector. Plug the long end into a hole on the breadboard adjacent to the wire (yellow) and the short end to an open row on the breadboard. |images/_making_a_led_light_blink_part_i_led_blinker_step_3.jpg| .. |images/_making_a_led_light_blink_part_i_led_blinker_step_3.jpg| image:: images/_making_a_led_light_blink_part_i_led_blinker_step_3.jpg #. A resistor is needed to prevent the power provided by the Pi from blowing out the LED. Take the 10k resistor and connect one end so that it is adjacent to the negative wire of the LED. Connect the other end of the resistor to an open spot on the board. If you cross over the center of the board to the other side, you can take advantage of the same row, which is what is done here. |images/_making_a_led_light_blink_part_i_led_blinker_step_4.jpg| .. |images/_making_a_led_light_blink_part_i_led_blinker_step_4.jpg| image:: images/_making_a_led_light_blink_part_i_led_blinker_step_4.jpg #. Now connect another wire adjacent to the unconnected resistor wire. Connect this wire to the negative (-) column on the breadboard. This is usually the column on the outer edge. |images/_making_a_led_light_blink_part_i_led_blinker_step_5.jpg| .. |images/_making_a_led_light_blink_part_i_led_blinker_step_5.jpg| image:: images/_making_a_led_light_blink_part_i_led_blinker_step_5.jpg #. Lastly, connect a wire from the negative column of the breadboard to a GND pin on GPIO. There are several GND pins; you can use any one that is easily reachable. |images/_making_a_led_light_blink_part_i_led_blinker_step_6.jpg| .. |images/_making_a_led_light_blink_part_i_led_blinker_step_6.jpg| image:: images/_making_a_led_light_blink_part_i_led_blinker_step_6.jpg #. This completes the circuit. You can quickly test the circuit by moving the yellow wire from pin #4 to pin 3v3. This directly connects the circuit to 3.3 volt power. The LED should immediately light up. Switch it back to the #4 pin before moving on to creating the Xojo app. .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/create_the_xojo_app: Create the Xojo app ------------------- To test your circuit, you'll use a simple Xojo app that will alternate between ON (HIGH) and OFF (LOW) on pin #4. When the pin is ON (HIGH), the LED will illuminate. 1. Create a new Console project and call it LEDBlinker. #. Add the GPIO module to the project. #. Add this code to the App Run event handler: .. code:: xojo GPIO.SetupGPIO Const kLEDPin = 4 ' "#4" on the pinout ' Set the pin to accept output GPIO.PinMode(kLEDPin, GPIO.OUTPUT) ' Blink LED every 1/2 second While True ' Turn the pin on (give it power) GPIO.DigitalWrite(kLEDPin, GPIO.ON) App.DoEvents(500) ' Turn the pin off (no power) GPIO.DigitalWrite(kLEDPin, GPIO.OFF) App.DoEvents(500) Wend Make sure you've selected Linux in Build Settings and its architecture to ARM 32-bit in the Inspector and then Build the app. .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/transfer_and_run_the_xojo_app: Transfer and run the Xojo app ----------------------------- Start your SFTP app and connect to the Raspberry Pi. Transfer the LEDBlinker folder from the Linux build folder to the Pi. On the Pi, open the Terminal (or ssh to it from your development machine). In the terminal, navigate to the location of the LEDBlinker folder and then CD to the folder: .. code:: xojo cd LEDBlinker Run the app: .. code:: xojo sudo ./LEDBlinker Normally sudo is needed to access GPIO. The GPIO.SetupGPIOSys method does provide access to some GPIO functionality without requiring sudo. The LED should start blinking. To stop the app, press :kbd:`Control C`. Note that if you press :kbd:`Control C` while the LED is on, it will remain on. Starting with Raspbian Jessie, you no longer have to use sudo to access GPIO. In order to not require sudo, you have to first set an environment variable before you run the app: .. code:: xojo export WIRINGPI_GPIOMEM=1 The Xojo project is included with the Xojo examples and is located here: Example Projects/Platform-Specific/RaspberryPi .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/video: `Video` ------- Watch the companion step-by-step `video `_. .. _/topics/raspberry_pi/_making_a_led_light_blink_part_i/circuit_diagram: Circuit diagram --------------- Here is a circuit diagram for reference: .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/_making_a_led_light_blink_part_i_led_blinker_circuit.png ================================= Making a LED light blink part II ================================= In this tutorial you will extend the :doc:`Making a LED Light Blink Part I` to add a button to the breadboard. With Xojo code you will turn on the LED when the button is pressed and turn off the LED when the button is not pressed. .. _/topics/raspberry_pi/_making_a_led_light_blink_part_ii/parts: Parts ----- In order to build this circuit, you'll need some additional parts: * A digital button * 2 male-to-male jumper wires * 1 10k resistor .. _/topics/raspberry_pi/_making_a_led_light_blink_part_ii/wire_the_button_circuit: Wire the button circuit ----------------------- 1. Plug the button into the breadboard so that it straddles the center channel. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_1.jpg| .. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_1.jpg| image:: images/_making_a_led_light_blink_part_ii_led_blinker_button_step_1.jpg #. Connect a wire from the top of the button (either side) to pin #24. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_2.jpg| .. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_2.jpg| image:: images/_making_a_led_light_blink_part_ii_led_blinker_button_step_2.jpg #. Connect a 10k resistor from the top of the button to the same negative (-) column on the breadboard that you used in the Blinking LED Tutorial. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_3.jpg| .. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_3.jpg| image:: images/_making_a_led_light_blink_part_ii_led_blinker_button_step_3.jpg #. Connect a wire from the 3v3 pin to a bottom pin on the button. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_4.jpg| .. |images/_making_a_led_light_blink_part_ii_led_blinker_button_step_4.jpg| image:: images/_making_a_led_light_blink_part_ii_led_blinker_button_step_4.jpg #. This completes the circuit. |images/_making_a_led_light_blink_part_ii_led_blinker_button_finished.jpg| .. |images/_making_a_led_light_blink_part_ii_led_blinker_button_finished.jpg| image:: images/_making_a_led_light_blink_part_ii_led_blinker_button_finished.jpg .. _/topics/raspberry_pi/_making_a_led_light_blink_part_ii/create_the_xojo_app: Create the Xojo app ------------------- To test your circuit, you'll use a simple Xojo app that will check the state of the button. If it is pressed (ON), then the LED will illuminate. When the button is not pressed (OFF), the the LED will be off. 1. Create a new Console project and call it LEDButton. #. Add the GPIO module to the project. #. Add this code to the App Run event handler: .. code:: xojo GPIO.SetupGPIO Const kLEDPin = 4 ' "#4" on the pinout Const kButtonPin = 24 ' "#24" on the pinout ' Set the LED pin to accept output GPIO.PinMode(kLEDPin, GPIO.OUTPUT) ' Set the button pin to accept input GPIO.PinMode(kButtonPin, GPIO.INPUT) ' Blink LED when button is pressed While True ' If the Button Pin is ON then turn on the LED If GPIO.DigitalRead(kButtonPin) = GPIO.ON Then ' Turn the pin on (give it power) GPIO.DigitalWrite(kLEDPin, GPIO.ON) Else GPIO.DigitalWrite(kLEDPin, GPIO.OFF) End If App.DoEvents(50) ' Small delay helps with CPU usage Wend .. _/topics/raspberry_pi/_making_a_led_light_blink_part_ii/transfer_and_run_the_xojo_app: Transfer and run the Xojo app ----------------------------- Start your SFTP app and connect to the Raspberry Pi. Transfer the LEDButton folder from the Linux build folder to the Pi. On the Pi, open the Terminal (or ssh to it from your development machine). In the terminal, navigate to the location of the LEDButton folder and then CD to the folder: .. code:: xojo cd LEDButton Run the app: .. code:: xojo sudo ./LEDButton Normally sudo is needed to access GPIO. The GPIO.SetupGPIOSys method does provide access to some GPIO functionality without requiring sudo. The LED should light up when you press the button. To stop the app, press :kbd:`Control C`. Starting with Raspbian Jessie, you no longer have to use sudo to access GPIO. In order to not require sudo, you have to first set an environment variable before you run the app: .. code:: xojo export WIRINGPI_GPIOMEM=1 The Xojo project is included with the Xojo examples and is located here: Example Projects/Platform-Specific/RaspberryPi .. _/topics/raspberry_pi/_making_a_led_light_blink_part_ii/video: `Video` ------- Watch the step-by-step `video`: https://youtu.be/SdNztRZ1thI .. _/topics/raspberry_pi/_making_a_led_light_blink_part_ii/circuit_diagram: Circuit diagram --------------- Here is a circuit diagram for reference: .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/_making_a_led_light_blink_part_ii_led_button_circuit.png ==================================== Working with a LCD character display ==================================== In this tutorial you will learn how to hook up a HD44780-based LCD character display. This tutorial uses a 20 character by 4 line display, but most of these types of displays are hooked up the same and will work with the provided code. .. _/topics/raspberry_pi/working_with_a_lcd_character_display/parts: Parts ----- * LCD character display (HD44780-based) * Potentiometer * 16 jumper wires .. _/topics/raspberry_pi/working_with_a_lcd_character_display/wire_the_circuit: Wire the circuit ---------------- The LCD consists of 16 pins, of which 12 are used. The potentiometer is used to control the display's contrast. These are the overall steps: 1. Plug the LCD into your breadboard. Make sure you have plenty of space to work with. The pins must not overlap with any of the pins in the GPIO cobbler connector. #. Wire up power and ground. The LCD uses 5v power. There are several power and ground connections so you'll want to use the power and ground rail on the side of the breadboard. #. Connect the potentiometer. You can plug this in anywhere on the board with space. #. Wire the LCD to the GPIO pins. The specific pins you use does not matter, but if you use different pins than described here, you'll need to update the code to use the pins you connected. #. Wire the LCD to the potentiometer. #. With this all hooked up, you should see the LCD light up and ought to be able to turn the potentiometer to adjust the contrast so that you can see the boxes where the characters are displayed. The LCD pins are numbered left to right when looking at the LCD with the pins at the top. These are the specific pin connections: .. csv-table:: :header: "LCD Pin", "GPIO Pin" :widths: auto "#1 (GND)","GND" "#2 (VCC)","5.0v" "#3 (Vo)","To potentiometer (see below)" "#4 (RS)","#25" "#5 (RW)","GND" "#6 (EN)","#24" "#7, #8, #9, #10","Not used" "#11 (D4)","#23" "#12 (D5)","#17" "#13 (D6)","#21" "#14 (D7)","#22" "#15 (LED+)","5.0v" "#16 (LED-)","GND" .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/working_with_a_lcd_character_display_lcd_wiring.jpg A potentiometer is used to adjust the contrast of the LCD. It is connected like this: .. csv-table:: :header: "Pot Pin", "LCD Pin / GPIO Pin" :widths: auto "#1","GPIO GND" "#2","LCD #3 (Vo)" "#3","GPIO 5.0v" When everything is connected correctly you will see the LCD light up and you'll be able to adjust the contrast with the potentiometer. You will not see any characters, although you ought to be able to see the individual character boxes. .. _/topics/raspberry_pi/working_with_a_lcd_character_display/create_the_xojo_app: Create the Xojo app ------------------- .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/working_with_a_lcd_character_display_lcd_character_display.jpg You'll use the Xojo GPIO library to display text on the LCD. This library has the GPIO.LCD module which contains methods you can call to easily initialize and display text on the LCD. .. code:: xojo This code displays text on the LCD: ' Display text on 4-line LCD GPIO.SetupGPIO Const kRSPin = 25 Const kEPin = 24 Const kD4Pin = 23 Const kD5Pin = 17 Const kD6Pin = 21 Const kD7Pin = 22 Var lcd As New GPIO.LCD(kRSPin, kEPin, kD4Pin, kD5Pin, _ kD6Pin, kD7Pin) lcd.Clear lcd.Home lcd.SetMessage("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1) lcd.SetMessage("abcdefghijklmnopqrstuvwxyz", 2) lcd.SetMessage("1234567890!@#$%^&*()", 3) lcd.SetMessage("-=[];',./_+{}:""<>?", 4) This code displays and scrolls text on the LCD: .. code:: xojo GPIO.SetupGPIO Const kRSPin = 25 Const kEPin = 24 Const kD4Pin = 23 Const kD5Pin = 17 Const kD6Pin = 21 Const kD7Pin = 22 Var lcd As New GPIO.LCD(kRSPin, kEPin, kD4Pin, kD5Pin, _ kD6Pin, kD7Pin) lcd.Clear lcd.Home lcd.SetMessage("This text is scrolling to the right.", 1) For i As Integer = 1 To 20 lcd.ScrollDisplayRight App.DoEvents(300) Next lcd.SetMessage("This text is scrolling to the left.", 1) For i As Integer = 1 To 20 lcd.ScrollDisplayLeft App.DoEvents(300) Next =================== Controlling a servo =================== A servo is a small rotary motor. From a neutral (0) position it can rotate +90 and -90 degrees. You can connect things to the servo in order to make them move. .. _/topics/raspberry_pi/controlling_a_servo/parts: Parts ----- * SG90 Micro-Servo * 3 jumper wires .. _/topics/raspberry_pi/controlling_a_servo/wire_the_circuit: Wire the circuit ---------------- The servo has a connector to which you will connect three jumper wires. The connectors are for power, ground and signal. This chart shows how the wires on the servo connector map to GPIO pins: .. _/topics/raspberry_pi/controlling_a_servo/create_the_xojo_app: Create the Xojo app ------------------- You'll use the open-source Xojo GPIO library to control the servo. This library has the GPIO.Servo module which contains methods you can call to easily set the servo positions. This code moves the servo to its neutral and the left (-90) and right (90) positions: .. code:: xojo GPIO.SetupGPIO Const kServoPin = 6 ' Change to the pin you used Var servo As New GPIO.Servo(kServoPin) If servo.ErrorCode = 0 Then servo.Neutral App.DoEvents(500) servo.Left App.DoEvents(500) servo.Right App.DoEvents(500) servo.Cleanup End If Transfer the built app to the Pi to test it out. ==== GPIO ==== The GPIO (General Purpose Input/Output Port) is a special port on the Pi that you can use to connect your own hardware, devices and circuits. To use GPIO with Xojo you will need to install the `wiringPi library `_. After doing so you can use the `WiringPiXojo module `_ to communicate with the GPIO port on the Raspberry Pi. This code in the Run event handler of a Console app flashes an LED: .. code:: xojo GPIO.SetupGPIO Const kLEDPin = 4 ' "#4" on the pinout ' Set the pin to accept output GPIO.PinMode(kLEDPin, GPIO.OUTPUT) ' Blink LED every 1/2 second While True ' Turn the pin on (give it power) GPIO.DigitalWrite(kLEDPin, GPIO.ON) App.DoEvents(500) ' Turn the pin off (no power) GPIO.DigitalWrite(kLEDPin, GPIO.OFF) App.DoEvents(500) Wend GPIO apps usually have to be started with sudo, so if you built a console app called LEDBlink you would start it like this from terminal: .. code:: xojo sudo ./LEDBlink You can also create a desktop app using GPIO. To start a desktop app as sudo, you have to use the gksudo command from terminal: .. code:: xojo gksudo ./LEDBlinkUI Starting with Raspbian Jessie, you no longer have to use sudo to access GPIO. In order to not require sudo, you have to first set an environment variable before you run the app: .. code:: xojo export WIRINGPI_GPIOMEM=1 .. _/topics/raspberry_pi/gpio/wiringpi_gpio_module: WiringPi GPIO Module -------------------- The Xojo GPIO module maps the wiringPi library to a set of methods that you can call in your Raspberry Pi Xojo apps. In order to use the GPIO module, you'll need to install the wiringPi library on your Raspberry Pi. The GPIO module is included with the Xojo examples and is available on GitHub: https://github.com/xojo/GPIO The official wiringPi docs will not be replicated here, but links are provided for your reference: * `Setup methods `_ * `Core Functions `_ * `Raspberry Pi Specifics `_ * `Timing `_ * `Priority-Interrupts and Threads `_ * Serial (not yet implemented in Xojo GPIO module; use the Serial class instead) * `SPI Library `_ * `I2C Library `_ * `Shift Library `_ * `Software PWM Library `_ * `Software Tone Library `_ .. _/topics/raspberry_pi/gpio/gpio_in_desktop_apps: GPIO in desktop apps -------------------- You can also create desktop apps that can control GPIO using the same techniques as above. The primary difference is how you launch the app so that it has sudo access. Use gksudo to start a desktop app from the command line with sudo: .. code:: xojo gksudo ./WiringPi-UI Or set the environment variable before starting the app from the command line: .. code:: xojo export WIRINGPI_GPIOMEM=1 ./WiringPi-UI Refer to the desktop example project that uses the circuits created in the :doc:`Making a LED Light Blink Part I` and the :doc:`Making a LED Light Blink Part II`. Example Projects/Platform-Specific/RaspberryPi ======= RGB LED ======= An RGB LED is an LED that lets you independently control its red, green and blue elements. .. _/topics/raspberry_pi/rgb_led/parts: Parts ----- * RGB LED * 4 jumper wires * 3 10k resistors .. _/topics/raspberry_pi/rgb_led/wire_the_circuit: Wire the circuit ---------------- An RGB LED has 4 pins. The longest pin (#3 on the LED) is the "cathode" pin that should be wired to ground. The other pins are as follows: * Pin #1: Green * Pin #2: Blue * Pin #3: Cathode/Gnd * Pin #4: Red You will need to put resistors between the RGB LED pins and the wires that connect to the GPIO pins. The specific resistor you'll need varies depending on the type of RGB LED and its power requirements. 10k resistors seemed to work fine with the RGB LED used in this example. This chart shows the RGB LED pin to GPIO pin mappings: .. _/topics/raspberry_pi/rgb_led/create_the_xojo_app: Create the Xojo app ------------------- You'll use the open-source Xojo GPIO library to control the RGB LED. This code will randomly change the color of the LED: .. code:: xojo GPIO.SetupGPIO ' These are the pins to which you wired the RGB LED Const kRedPin = 21 Const kBluePin = 20 Const kGreenPin = 16 Var led As New GPIO.RGBLED(kRedPin, kGreenPin, kBluePin) ' Display random colors For i As Integer = 1 To 100 led.SetColor(System.Random.InRange(0, 255), _ System.Random.InRange(0, 255), _ System.Random.InRange(0, 255)) App.DoEvents(100) Next ' turn everything off led.Off Build the app for Linux ARM and transfer it to the Raspberry Pi to run it. ================ Remote debugging ================ You can use the Remote Debugger on Raspberry Pi to make it even easier to make Raspberry Pi apps with Xojo. The Remote Debugger is described in full in the Remote Debugging but this brief overview should allow you to get up and running on the Pi quickly. .. _/topics/raspberry_pi/remote_debugging/set_up_the_remote_debugger_on_the_pi: Set up the Remote Debugger on the Pi ------------------------------------ The Remote Debugger is included in your Xojo installation. Go the the installation folder and then find this file: Extras/Remote Debugger Desktop/Linux ARM.zip 1. Copy this file to your Raspberry Pi and unzip it. Use SFTP to copy the file as described in Getting Started. #. On the Pi, find the ZIP file you copied over, right-click on it in the File Manager and select "Extract Here". #. This creates a folder called "Remote Debugger Desktop". Open this folder and then double click the "Remote Debugger Desktop" file to start the Remote Debugger. #. In the Remote Debugger, go to the Edit menu and select Options. Enter a name for your Pi in the "Name:" field. #. In the Download Location section click Choose and choose a location for your debug apps to be copied. Using the Desktop works for most people. #. Click OK to save these changes. .. _/topics/raspberry_pi/remote_debugging/tell_xojo_that_raspberry_pi_is_a_remote_debugging_host: Tell Xojo that Raspberry Pi is a remote debugging host ------------------------------------------------------ .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/remote_debugging_raspberry_pi_debugging_preferences.png Now you can start Xojo on your development machine and add the Pi as a Remote Debugging host. 1. Open the Xojo Preferences and click the Debugging panel. #. In the Remote Debugging Hosts section, click the Add button to add your Pi. It's likely your Pi shows up as an auto-discovered remote machine. If it does, just select it and click OK. #. If it does not show up you'll need to manually add it by entering a name and the IP address of the Pi (which is displayed in the Remote Debugger running on the Pi if you need to go back to find it). #. Click OK to save the changes. .. _/topics/raspberry_pi/remote_debugging/debug_run_an_app_on_the_raspberry_pi: Debug run an app on the Raspberry Pi ------------------------------------ .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/remote_debugging_raspberry_pi_remote_debugging.png It's now time to try running a Xojo app on the Pi. 1. Open a Xojo example project. A good one to try is: Examples/Sample Apps/XojoNotes/XojoNotes - Desktop #. In the Build Settings section of the Navigator, click on Linux. #. Finally, to run the project on the Pi, click the Project menu, go to "Run Remotely" and choose your Pi that is listed there. This builds your app for Pi, transfers it to the Pi and then launches it in debug mode. While your app is running on the Pi, you can use Xojo to debug it. You app will stop at breakpoints allowing you to view variables and other information just as you can when you are debugging normally. ==== Tips ==== .. _/topics/raspberry_pi/tips/desktop_apps: Desktop apps ------------ .. _/topics/raspberry_pi/tips/playing_sound: Playing Sound ************* The Raspberry Pi can at least play WAV and mp3 sound files. By default sound output is sent out the HDMI port. There is also a headphone port that can be used for sound output. To switch between the sound outputs, you can use this command: .. code:: xojo sudo amixer cset numid=3 where n is 0=auto, 1=headphones, 2=HDMI. In your code you can run this command from a Shell. This code switches to the headphone port: .. code:: xojo #If TargetARM Then ' Switch to headphone port Var sh As New Shell sh.Execute("amixer cset numid=3 1") #Endif .. _/topics/raspberry_pi/tips/general_tips: General tips ------------ If you have the default installation of Raspbian (with the desktop), you'll be able to connect your Pi to any display (including a TV) using HDMI. Plug in a USB keyboard and mouse and you can use it as a real computer. .. _/topics/raspberry_pi/tips/updating_raspbian_os: Updating Raspbian OS ******************** To update Raspbian, you can run these two commands from Terminal: .. code:: xojo sudo apt-get update sudo apt-get dist-upgrade .. _/topics/raspberry_pi/tips/transferring_files_to_raspberry_pi: Transferring files to Raspberry Pi ********************************** By default, there is an SFTP server installed on the Pi but you'll have to enable it using the Pi Configuration screen to enable SSH. Once enabled, you can connect to it using any SFTP client. You'll need to know the IP address of the Raspberry Pi on your network and the username and password that you created when you initially installed Raspbian (by default, it's 'pi' and 'raspberry' respectively). .. _/topics/raspberry_pi/tips/connecting_to_raspberry_pi_via_shell: Connecting to Raspberry Pi via the Shell **************************************** You can also use ssh without any Pi configuration. Start ssh with a command such as this: .. code:: xojo ssh pi@10.0.1.194 and then enter your password ('raspberry' by default). You'll now be connected to the Pi in a terminal window. Here you can easily run Xojo console apps that you've transferred using SFTP. *Note: Windows doesn't have ssh already available. You'll want to `install PuTTY `_ or on newer versions of Windows 10 `enable its SSH Client `_.* .. _/topics/raspberry_pi/tips/connecting_to_raspberry_pi_via_vnc: Connecting to Raspberry Pi via VNC ********************************** .. image:: https://documentation.xojo.com/topics/raspberry_pi/images/tips_raspberry_pi_configuration_window.png VNC (aka Remote Desktop) allows you to view the desktop of the Pi and interact with it using your mouse. This is useful for testing Xojo desktop apps, but it can also be helpful to more easily access configuration screens or use the Pi in some other fashion. The Pi has a VNC server built-in but you may need to enable it. Go to the Pi Configuration (Pi menu->Preferences->Raspberry Pi Configuration) where you should see a radio button to enable VNC. You can connect to the Pi using any VNC client. Here are some suggestions: * RealVNC * Mac Screen Sharing * Finder, Go > Connect to Server, vnc://10.0.1.194:5901 (substitute your own IP address and port) * Tight VNC ============== Using a buzzer ============== A simple piezo buzzer can be used to make beeps and even play simple tunes. .. _/topics/raspberry_pi/using_a_buzzer/parts: Parts ----- * Piezo buzzer * 2 jumper wires .. _/topics/raspberry_pi/using_a_buzzer/wire_the_circuit: Wire the circuit ---------------- The buzzer requires only a simple circuit. One lead of the buzzer is longer than the other. The longer lead is the positive connector. Using a wire, connect this to a GPIO port. Then connect the other lead to ground. .. _/topics/raspberry_pi/using_a_buzzer/create_the_xojo_app: Create the Xojo app ------------------- You'll use the open-source Xojo GPIO library to control the buzzer. For advanced control of the buzzer, you can use the GPIO.SoftToneCreate and SoftToneWrite methods. .. _/topics/raspberry_pi/using_a_buzzer/alarm: Alarm ***** This code sends a simple ON/OFF signal to the buzzer to make it act like an alarm: .. code:: xojo GPIO.SetupGPIO Const kBuzzerPin = 5 GPIO.PinMode(kBuzzerPin, GPIO.OUTPUT) For i As Integer = 1 To 10 GPIO.DigitalWrite(kBuzzerPin, GPIO.LOW) App.DoEvents(100) GPIO.DigitalWrite(kBuzzerPin, GPIO.HIGH) App.DoEvents(100) Next GPIO.Cleanup .. _/topics/raspberry_pi/using_a_buzzer/tones: Tones ----- This code uses the SoftToneCreate and SoftToneWrite methods to play a simple Super Mario Brothers tune: .. code:: xojo Const kBuzzerPin = 5 ' Simple Super Mario Brothers theme Var scales() As Integer = Array(659, 659, 0, 659, 0, 523, 659, 0, _ 784, 0, 0, 0, 392, 0, 0, 0, 523, 0, 0, 392, 0, 0, 330) GPIO.SetupGPIO If GPIO.SoftToneCreate(kBuzzerPin) = 0 Then For i As Integer = 0 To scales.LastIndex GPIO.SoftToneWrite(kBuzzerPin, scales(i)) App.DoEvents(200) Next End If GPIO.PinMode(kBuzzerPin, GPIO.INPUT) Text handling ============= .. toctree:: :maxdepth: 1 :name: sec-text_handling Accessing the clipboard Accessing the selected text Comparing text values Creating styled text Encrypting and decrypting data Formatting values Right-to-left text support Searching text using the SoundEx algorithm Understanding text encodings Using Regular Expressions Working with fonts ======================= Accessing the Clipboard ======================= The :doc:`Clipboard` class is used to get or add data to the system-wide clipboard. The properties and methods let you determine what kind of data is available on the Clipboard, get data from the Clipboard, and send data to the Clipboard. The Clipboard class supports three kinds of data: text, picture, and binary/raw data. Raw data is represented in string form and is marked with a type you specify so you can tell what the raw binary data represents. .. _/topics/text_handling/accessing_the_clipboard/text: Text ---- .. _/topics/text_handling/accessing_the_clipboard/getting_text_from_the_clipboard: Getting text from the Clipboard ******************************* Since the Clipboard can contain text, picture and binary data, you should ask it what type of data it contains before you attempt to use it. To check for text, you use the TextAvailable method: .. code:: xojo Var c As New Clipboard Var clipText As String If c.TextAvailable Then clipText = c.Text End If c.Close .. _/topics/text_handling/accessing_the_clipboard/adding_text_to_the_clipboard: Adding text to the Clipboard **************************** This code adds text to the clipboard: .. code:: xojo Var c As New Clipboard c.Text = "Hello!" c.Close .. _/topics/text_handling/accessing_the_clipboard/pictures: Pictures -------- The clipboard can contain any picture that can be stored in the :doc:`Picture` class. .. _/topics/text_handling/accessing_the_clipboard/getting_a_picture_from_the_clipboard: Getting a picture from the Clipboard ************************************ Since the Clipboard can contain text, picture and binary data, you should ask it what type of data it contains before you attempt to use it. To check for a picture, you use the PictureAvailable method: .. code:: xojo Var c As New Clipboard Var clipPic As Picture If c.PictureAvailable Then clipPic = c.Picture End If c.Close .. _/topics/text_handling/accessing_the_clipboard/adding_a_picture_to_the_clipboard: Adding a picture to the Clipboard ********************************* This code adds a picture to the clipboard: .. code:: xojo Var c As New Clipboard c.Picture = ImageWell.Image c.Close .. _/topics/text_handling/accessing_the_clipboard/raw_data: Raw data -------- Raw data refers to any data that is not text or is not a picture. This allows you to put any type of data you want in the clipboard. Only your own apps will be able to parse custom data you put here, however. When you put data in the clipboard you can specify the data type using a UTI. Standard UTIs, such as "public.rtf" may be readable by other apps if your data is formatted properly. .. _/topics/text_handling/accessing_the_clipboard/getting_raw_data_from_the_clipboard: Getting raw data from the Clipboard *********************************** Use the RawData method to get raw data from the Clipboard. The raw data is in string format and you supply a type indicator used to identify the type of data being fetched. This code gets RTF data from the Clipboard and stores it in a Text Area: .. code:: xojo Var c As New Clipboard If c.RawDataAvailable("public.rtf") Then TextArea1.StyledText.RTFData = c.RawData("public.rtf") End If c.Close .. _/topics/text_handling/accessing_the_clipboard/adding_raw_data_from_the_clipboard: Adding raw data from the Clipboard ********************************** Use the AddRawData method to add raw data to the clipboard, specifying the type. This code stores the RTF data from a styled Text Area in the Clipboard so that other apps should be able to read it: .. code:: xojo Var c As New Clipboard c.RawData("public.rtf") = TextArea1.StyledText.RTFData c.Close .. _/topics/text_handling/accessing_the_clipboard/see_also: .. seealso:: :doc:`Clipboard`, :doc:`Picture` classes =========================== Accessing the selected text =========================== A text selection is text that is highlighted, either by the user or by using the appropriate properties of the control. You can manage text selection in the Desktop Text Field and Text Area controls using several properties and methods. The SelectionChanged event is called when the user selects text in the control. You can use the SelectAll method in your code to select all the text in a control. .. code:: xojo TextField1.SelectAll These properties can be used to view or change the selected text: .. csv-table:: :header: "Name", "Description" :widths: 10, 50 "SelectionLength","The number of characters currently selected. You can change the selected text by changing this number. Setting this value to 0 (zero) will position the insertion point based on the value in the SelectionStart property rather than selecting any text." "SelectionStart","The number of the character just before the selected text. For example, if the fifth character in a Text Field was selected, this property would be 4. Setting this value to 0 (zero) will start the selection at the first character of the Text Field." "SelectionText","A string containing all of the selected text. Changing this value will replace the selected text with the SelectionText value. If no text is selected, the SelectionText value will be inserted at the insertion point (the value in SelectionStart)." .. _/topics/text_handling/accessing_the_selected_text/usage: Usage ----- Assume you have a Text Area with the text "A quick brown fox." This code selects "quick": .. code:: xojo TextArea1.SelectionStart = 2 TextArea1.SelectionLength = 5 This code changes "quick" to "speedy": .. code:: xojo TextArea1.SelectionStart = 2 TextArea1.SelectionLength = 5 TextArea1.SelectedText = "speedy" This code in the SelectionChanged event change text that the user selects to all uppercase: .. code:: xojo Me.SelectedText = Me.SelectedText.Uppercase .. _/topics/text_handling/accessing_the_selected_text/see_also: .. seealso:: :doc:`Text Field`, :doc:`Text Area` topics ===================== Comparing text values ===================== .. _/topics/text_handling/comparing_text_values/case_insensitive: Case-insensitive comparison --------------------------- All text comparison is case-insensitive by default. This means that text such as “Hello”, “HELLO”, “hello” and “hELLO” are all treated the same when you compare using the comparison operators (=, <, >, <>, <=, >=). .. code:: xojo If "Hello" = "hello" Then MessageBox("They match!") ' Displayed End If If "Hello" <> "hello" Then ' Not displayed MessageBox("They do not match.") End If .. _/topics/text_handling/comparing_text_values/case_sensitive_comparisons: Case-sensitive comparison ------------------------- When you need to do case-sensitive text comparisons on String data, then use the :ref:`String.Compare` function to test the text values. Compare takes three parameters (other, compare and locale) and returns an Integer value indicating the result of the comparison. The compare parameter indicates the type of comparison to do and uses the :doc:`ComparisonOptions` enumeration. When the value CaseInsensitive is used, the comparison is done without considering the case of the letters. When the value CaseSensitive is used, the comparison is done using the case of the letters. For example, “Hello” and “HELLO” would be different strings because they are the same other than the case. However, it does mean that text such as “hello” and “Today” may not compare exactly how you would expect. In the strictest sense, “hello” is greater than “Today” because “h” has a higher character code than “T”. But with CaseSensitive, that detail is not used because the two strings are not the same. Here are some examples: .. code:: xojo Var source As String = "Hello" If source.Compare("hello", ComparisonOptions.CaseInsensitive) = 0 Then MessageBox("They match!") End If If source.Compare("hello", ComparisonOptions.CaseSensitive) <> 0 Then MessageBox("They do not match.") End If .. _/topics/text_handling/comparing_text_values/see_also: .. seealso:: :doc:`String` data type; :ref:`String.Compare` function ==================== Creating Styled Text ==================== Styled Text refers to text than can have a variety of fonts, font sizes and other font styles. Styled Text can be used with the desktop Text Area control when its Styled property is ON or True (which is the default). Additionally, on Mac you can use the :doc:`StyledTextPrinter` class to print styled text. This is discussed in the :doc:`Sending data to the printer` topic. .. _/topics/text_handling/creating_styled_text/determining_font,_size_and_style_of_text: Determining font, size and style of text ---------------------------------------- TextAreas have properties that make it easy to determine the font, font size, and font style of the selected text. The :ref:`SelectionFontName` property can be used to determine the font of the selected text. If the selected text has only one font, the :ref:`SelectionFontName` property contains the name of that font. If the selected text uses more than one font, the :ref:`SelectionFontName` property is empty. This function returns the names of fonts for the selected text of the TextArea passed: .. code:: xojo Function Fonts(item As DesktopTextArea) As String Var fonts, theFont As String Var i, Start, Length As Integer If item.SelectionFontName.IsEmpty Then Start = item.SelectionStart Length = item.SelectionLength For i = Start To Start + Length Field.SelectionStart = i Field.SelectionLength = 1 If fonts.IndexOf(Field.SelectionFontName) = 0 Then If fonts.IsEmpty Then fonts = Field.SelectionFontName Else fonts = fonts + ", " + Field.SelectionFontName End If End If Next Return fonts Else Return Field.SelectionFontName End If End Function The :ref:`SelectionFontSize` property is used to determine the font size of the selected text and works the same way as the :ref:`SelectionFontName` property. If all characters of the selected text are the same font size, the :ref:`SelectionFontSize` property will contain that size. If different sizes are used, the :ref:`SelectionFontSize` property will be 0. There are also boolean properties for determining if all of the characters in the selected text are the same font style. Since text can have multiple styles applied to it, these properties determine if all of the characters in the selected text have a particular font style applied to them. For example, if all of the characters in the selected text are bold but some are also italic, a test for bold returns :doc:`True`. On the other hand, a test for italic returns False since some of the selected text is not in the italic font style. For all of these properties, you test to see if the property is True or False. If the test returns True, then all of the characters in the selected text have that font style. If it returns False, the selected text contains more than one font style. If you want to determine which styles are in use, you can programmatically select each character in the selected text and then test the style properties. This is an operation similar to the sample Font function that determines which fonts are in use in the selected text. In this example, if the selected text of the TextArea is bold, then the Bold menu item is checked: .. code:: xojo StyleBold.Checked = TextArea1.SelectionBold If all of the characters in the selected text are not bold then TextArea1.SelectionBold returns False which will then be assigned to the Checked property of the StyleBold menu item. .. _/topics/text_handling/creating_styled_text/setting_the_font,_size,_and_style_of_text_in_desktop_app: Setting the font, size, and style of text in desktop app -------------------------------------------------------- For desktop apps, the properties used to check the font, font size, and font styles of the selected text are also used to set these values. A :doc:`DesktopTextArea` can support multiple fonts, font sizes, and styles. A :doc:`DesktopTextField` can support one font, one font size, and the plain style. A :doc:`DesktopTextField` can also support the Bold, Underline, and Italic styles for all the text in the :doc:`DesktopTextField`. For example, to set the font of the selected text to Helvetica, you do the following: .. code:: xojo TextArea1.SelectionFontName = "Helvetica" Keep in mind when setting fonts that the font must be installed on the user's computer or the assignment will have no effect. You can use the FontAvailable function mentioned earlier in this chapter to determine if a particular font is installed. You can set the FontSize property of a control to zero to have your application use the font size that looks best for the platform on which the application is running. You can set the font size of the selected text using the SelectionFontSize property. For example, the following code sets the font size of TextArea1 to 12 point: .. code:: xojo TextArea1.SelectionFontSize = 12 To apply a particular font style to the selected text, set the appropriate style property to :doc:`True`. For example, the following code applies the Bold style to the selected text in TextArea1: .. code:: xojo TextArea1.SelectionBold = True TextAreas also have built-in methods for toggling the font styles on and off. *Toggling* in this case means applying the style if some of the selected text doesn't have the style already applied or removing the style from any of the selected text that already has it applied. The following code toggles the bold style of the selected text in TextArea1: .. code:: xojo TextArea1.ToggleSelectionBold These methods are used to toggle the selection style: * ToggleSelectionBold * ToggleSelectionItalic * ToggleSelectionUnderline .. _/topics/text_handling/creating_styled_text/styled_text_objects_for_desktoptextarea: Styled Text objects for DesktopTextArea --------------------------------------- When you are working with styled text that is displayed in a TextArea, you can work with the properties of TextAreas that get and set style attributes as described in the previous section to manage styled text. However, there are also classes for opening, saving, and managing styled text separately from a TextArea or any other control. In fact, the styled text doesn't even have to be displayed at all. This set of techniques uses the properties and methods of the StyledText class. Its Text property contains the styled text that is managed by the StyledText object. Each method takes parameters for the starting position and length of the text for which the attribute applies. These numbers are zero-based. For example, a call to the Bold property would look like this: .. code:: xojo Var st As New StyledText st.Text = "How now Brown Cow." st.Bold(0, 3) = True This sets the first word of the text, “How,” to bold. Each contiguous set of characters that has the identical set of style attributes makes up a :doc:`StyleRun` object. In the above example, the first three characters make up one StyleRun. The remaining text is the second StyleRun. In the language of a word processor, each StyleRun is an instance of a character style. The entire Text property is made up of a sequence of StyleRuns. The StyledText class has these methods for managing StyleRuns: .. csv-table:: :header: "Name", "Description" :widths: 10, 50 "Bold","Gets or sets the Bold style to the selected text in Text." "FontName","Gets or sets the font for the selected text in Text." "Italic","Gets or sets the Italic style to the selected text in Text." "FontSize","Gets or sets the font size to the selected text in Text." "TextColor","Gets or sets the color of the selected text in Text." "Underline","Gets or sets the Underline style to the selected text in Text." The StyledText class has these methods for managing StyleRuns: .. csv-table:: :header: "Name", "Description" :widths: 10, 50 "AddStyleRun","Appends a StyleRun to the end of Text." "AddStyleRunAt","Inserts a StyleRun at a specified position." "RemoveStyleRunAt","Removes a specified StyleRun from Text." "StyleRun","Provides access to a particular StyleRun in Text. The StyleRun class has its own properties that describe the style that's applied to all the characters in the StyleRun." "StyleRunCount","Returns the number of StyleRuns that make up Text." "StyleRunRange","Accesses the starting position, length, and end position of the StyleRun." "Text","The text that is managed by the StyledText object. Technically, Text is a method, but you can get and set its value as if it were a property." The Text method of a StyledText object can have multiple paragraphs. A paragraph is the text between two end-of-line characters. A paragraph can be defined either with the EndOfLine function or the end-of-line character for the platform the application is running on. A paragraph can be made up of multiple StyleRuns. It has only one style property of its own, paragraph alignment (Left, Centered, or Right). Although you can work with a StyledText object entirely in code — without ever displaying it — the TextArea control is “hooked up” to the StyledText class in the sense that you can access all the methods and properties of the StyledText class via the StyledText property of the TextArea. These methods can be used to work with Paragraphs: .. csv-table:: :header: "Name", "Description" :widths: 10, 50 "Paragraph","Provides access to a particular Paragraph in Text. The Paragraph class has its own properties that return the start position, length, end position, and alignment of the paragraph." "ParagraphCount","Returns the number of Paragraphs that make up Text." "ParagraphTextAlignment","Sets the alignment of the specified paragraph (Default, Left, Centered, or Right). The ParagraphAlignment method takes one parameter, the number of the paragraph to be aligned (starting at zero). You assign it a :doc:`TextAlignments` enumeration value. For example, to right align the first paragraph, you would use code like this: :: StyledText1.ParagraphTextAlignment(0) = TextAlignments.Right " In order to work with a StyledText object in a TextArea, both the MultiLine and Styled properties of the TextArea must be ON or True (which is the default). You can do this using the Inspector. Suppose the styled TextArea already has the text that you want to manipulate using the StyledText class. The following code loads the text into the StyledText object: .. code:: xojo Var st As New StyledText st = TextArea1.StyledText TextArea1.AddText("This is the added text.") st.Bold(0, 4) = True The StyledText object is actually an alias to the TextArea's text, not a static copy. This means that the third line of code changes the contents of the TextArea and the last line sets the first four characters of the TextArea to bold. The following code sets the Text property of the StyledText object and displays it in the TextArea: .. code:: xojo TextArea1.StyledText.Text = "Here is my styled text." + EndOfLine + "Impressive. Most impressive." From there, you can go ahead and assign style properties to the text. The changes reformat the contents of the TextArea. Here is code that works with these two paragraphs: .. code:: xojo Var st, ln As Integer Var Text As String Text = "Here is my styled text." + EndOfLine + "Aren't you really impressed?" TextArea1.StyledText.Text = Text ' Assign Font and Size to entire text TextArea1.StyledText.Font(0, Text.Length) = "Arial" TextArea1.StyledText.Size(0, Text.Length) = 14 ' Apply character highlights to 'my// in first paragraph TextArea1.StyledText.Bold(8, 2) = True TextArea1.StyledText.TextColor(8, 2) = &cff0000 ' Get positions of second paragraph (0-based) st = TextArea1.StyledText.Paragraph(1).StartPosition - 1 ln = TextArea1.StyledText.Paragraph(1).Length ' Second paragraph in Bold TextArea1.StyledText.Bold(st, ln) = True ' Second paragraph Centered TextArea1.StyledText.ParagraphAlignment(1) = Paragraph.AlignCenter This above code works with the StyledText object "hooked up" to the TextArea, but you can also work with styled text "offline." You declare a StyledText object in a Var statement and operate on it without reference to any control. When you 're ready to display it, you can assign it to the StyledText property of a TextArea. You would do this with code such as: .. code:: xojo Var st As New StyledText ' do whatever you want right here; when you 're done, just write... TextArea1.StyledText = st You can also export the styled text as a series of StyleRuns and read them back in and reconstruct the StyledText object using the AddStyleRun method. See :doc:`StyleRun` and :doc:`StyledText` for more information. .. _/topics/text_handling/creating_styled_text/see_also: .. seealso:: :doc:`StyledText`, :doc:`StyleRun`, :doc:`Paragraph`, :doc:`DesktopTextArea` classes; :doc:`Text Area`, :doc:`Sending data to the printer` topics ============================== Encrypting and decrypting data ============================== Using the cryptography functions, you can hash and/or encrypt your text for security purposes. .. _/topics/text_handling/encrypting_and_decrypting_data/hashing_methods: Hashing methods --------------- * :ref:`Hash` * :ref:`HMAC` * :ref:`MD5` * :ref:`PBKDF2` These methods support the following algorithms are supported via the the :ref:`Crypto.HashAlgorithms` enumeration: .. csv-table:: :header: "Algorithm", "Hash", "HMAC", "PBKDF2" :widths: auto "CRC32", ✓, "", "" "MD5", ✓, ✓, ✓ "SHA1", ✓, ✓, "" "SHA256", ✓, ✓, ✓ "SHA512", ✓, "", ✓ "SHA2_256", ✓, ✓, ✓ "SHA2_512", ✓, ✓, ✓ "SHA3_256", ✓, "", "" "SH3_512", ✓, "", "" .. note:: There are also :ref:`MD5`, :ref:`SHA1`, :ref:`SHA2_256`, :ref:`SHA2_512`, :ref:`SHA3_256`, :ref:`SHA3_512` functions methods but these methods produce different results than using the :ref:`Hash`, :ref:`HMAC` or :ref:`PBKDF2` with the algorithms of the same names. .. _/topics/text_handling/encrypting_and_decrypting_data/encryption_and_decryption_methods: Encryption and decryption methods --------------------------------- There are also methods for public/private key encryption: RSA *** * :ref:`RSADecrypt` * :ref:`RSAEncrypt` * :ref:`RSAGenerateKeyPair` * :ref:`RSASign` * :ref:`RSAVerifyKey` * :ref:`RSAVerifySignature` AES *** * :ref:`AESDecrypt` * :ref:`AESEncrypt` BlowFish ******** * :ref:`BlowFishDecrypt` * :ref:`BlowFishEncrypt` TwoFish ******* * :ref:`TwoFishDecrypt` * :ref:`TwoFishEncrypt` DER *** Used for interoperability with other libraries: * :ref:`DEREncodePrivateKey` * :ref:`DEREncodePublicKey` .. tip:: There is also a :ref:`GenerateRandomBytes` method that generates a random number of bytes in a way that is even more random than traditional means. .. _/topics/text_handling/encrypting_and_decrypting_data/usage: Usage ----- This code calculates the hash of the supplied text using SHA256: .. code:: xojo Var value As String value = Crypto.SHA256("DataToEncrypt") .. _/topics/text_handling/encrypting_and_decrypting_data/rsa_public_key_encryption: RSA Public key encryption ------------------------- With Public Key Cryptography there are two keys: a public key and a private key. The person who wants to receive an encrypted message generate both of these keys. This can be done in Xojo using the Crypto.RSAGenerateKeyPair function: .. code:: xojo Var privateKey As String Var publicKey As String If Crypto.RSAGenerateKeyPair(1024, privateKey, publicKey) Then ' 1024-bit private and public keys ' were generated End If The private key is not shared with anyone. The public key can be shared with anyone. To make the public key more presentable, converting it to Base64 is a good idea: .. code:: xojo viewablePublicKey = EncodeBase64(publicKey) So if you created both a private and public key and shared the public key, others can now create encrypted messages that only you will be able to decrypt. These people create the encrypted message for you by encrypting it using the public key: .. code:: xojo Var publicKey As String = DecodeBase64(PublicKeyArea.Text) Var textMessage As String = "Top-secret message." Var msg As MemoryBlock msg = textMessage ' Encrypt msg using the publicKey Var encryptedData As MemoryBlock = Crypto.RSAEncrypt(msg, publicKey) If encryptedData <> Nil Then MessageBox("Successfully encrypted.") End If This encrypted message can be sent to you, although again converting it to Base64 can make it simpler to send: .. code:: xojo Var msgToSend As String = EncodeBase64(encryptedData) When you receive the message, you can decrypt it using your private key: .. code:: xojo encryptedData = DecodeBase64(encryptedMsg) Var decryptedData As MemoryBlock = Crypto.RSADecrypt(encryptedData, privateKey) Var msg As String = decryptedData MessageBox(msg) Keep in mind that these “messages” that are being encrypted have to be pretty short (usually just a couple hundred characters, but it depends on the number of bits you use to create the keys). So typically you use the messages to communicate a “secret key” of some kind that can be used to decrypt an actual message that was encrypted using some other technique (such as AES). As an example, here is how two people might send a large amount of encrypted data using an encrypted database: 1. Julie creates a SQLite database, adds data to it and encrypts it using a secret password. #. Paul creates an RSA Public/Private key pair and gets the Public key to Julie. #. Julie encrypts the secret password using the Public Key from Paul to get an encrypted message that she sends to Paul. #. Paul can decrypt the message from Julie using his Private Key to get the secret password. #. Julie sends the encrypted database to Paul. #. Paul accesses the database using the secret password he previously decrypted. This is secure because the database cannot be accessed by anyone that does not have the secret password and only the person with the RSA Private Key pair for the Public Key used to encrypt the secret password will be able to decrypt it to access the database. There is more to RSA encryption, including padding techniques that further improve security. You can learn more about RSA from its Wikipedia topics. .. _/topics/text_handling/encrypting_and_decrypting_data/see_also: .. seealso:: :doc:`Crypto` module ================= Formatting values ================= There are many ways to control the display and formatting of numbers, dates, and times. .. _/topics/text_handling/formatting_values/numbers: Numbers ------- Numbers are stored unformatted. Fortunately, all of the numeric data types (:doc:`Double`, :doc:`Currency`, :doc:`Integer` and :doc:`Single`) include a ToString function that can convert them to a formatting string value. The ToString function of :doc:`Double`, :doc:`Integer` and :doc:`Single` all take a format specification (see below). :doc:`Currency` does not. It respects the user's locale and system settings regarding currency formatting. For example, this code converts a double to a string formatting for US dollars then assigns it to a label: .. code:: xojo Var money As Double = 34.56 Label1.Text = money.ToString("$###.00") An alternative to ToString is the Format function makes providing formatting to numbers easy. The resulting number is formatted to use the number formatting of your OS settings. To use this function, pass it a format specification and the number you wish formatted. The Format function then returns a string that represents the number with the formatting applied to it. The syntax for the Format function is: .. code:: xojo result = Format(Number, FormatSpec) Another alternative is the Str function works similarly to Format, but it does not use the OS settings to format the number. .. code:: xojo result = Str(Number, FormatSpec) For example, the decimal “.” is always used as the decimal separator when Str is used to format the number regardless of what the OS number format setting specifies. Use the Format function for numbers that should be displayed to the user. Use Str function for numbers that should be stored (perhaps in a database or XML file). .. _/topics/text_handling/formatting_values/formatspec: FormatSpec ********** The FormatSpec is a string made up of one or more characters that control how the number will be formatted. For example, the format spec “$###,##0.00” applies the dollars and cents formatting used in the United States. On Windows, the character that is used as the Decimal and Thousands separator is specified by the user in the Regional Settings Control Panel. On Mac, these characters are specified on the Formats panel of the International system preference. By default, the FormatSpec applies to all numbers. If you want to specify different FormatSpecs for positive numbers, negative numbers, and zero, simply separate the formats with semi-colons within the FormatSpec. The order in which you supply FormatSpecs is: positive, negative, zero. These characters are used in a FormatSpec: .. csv-table:: :header: "Format Character", "Description" :widths: auto "#","Placeholder that displays the digit from the value if it's present. If no digit is present, 0 (zero) is displayed in its place." "0","Placeholder that displays the digit from the value if it's present. If no digit is present, 0 (zero) is displayed in its place." ".","Placeholder for the position of the decimal character." ",","Placeholder that indicates that the number should be formatted with thousands separators." "%","Displays the number multiplied by 100 (or as a percent)." "(","Displays an open parenthesis." ")","Displays a closing parenthesis." "+","Displays a plus sign to the left of the number if the number is positive or a minus sign if the number is negative." "-","Displays a minus sign to the left of the number if the number is negative. Nothing is displayed for positive numbers." "E or e","Displays the number in scientific notation." "\\","Displays the character that follows the backslash. Use this to display other characters in the output." Here are some example FormatSpecs and their corresponding output (on a US-based system): .. csv-table:: :header: "Format Syntax", "Output" :widths: auto "Format(1.784, *#.##*)","1.78" "Format(1.3, *#.0000*)","1.3000" "Format(5, *0000*)","0005" "Format(0.25, *#%*)","25%" "Format(145678.5, *###,###.##*)","145,678.5" "Format(145678.5, *#.##e+*)","1.46E+05" "Format(-3.7, *-#.##*)""-3.7" "Format(3.7, *+#.##*)","+3.7" "Format(3.7, *#.##; (#.##); \\z\\e\\r\\o*)","3.7" "Format(0, *#.##; (#.##); \\z\\e\\r\\o*)","zero" .. _/topics/text_handling/formatting_values/dates: Dates ----- Dates are stored using as objects via the :doc:`DateTime` class and have properties that hold the date in various different formats. To get a date as a string formatted in a specific way, you simply access the appropriate property. .. csv-table:: :header: "Property", "Default Output (on US-based systems)" :widths: auto "Full","Tuesday, September 1, 2020" "Long","September 1, 2020" "Medium","Sep 1, 2020" "Short","9/1/20" "None","" Date formats are controlled by the user's Date Properties (Windows) or Date Formats (Mac) system settings. On Mac, the Date Formats dialog is accessed from the Formats panel of the “Languages & Text” system preference (Click Customize... in the Date area in the Formats panel). On Windows, Date Properties is a screen in the Regional Options control panel. Users can choose the order of the day, month, year, as well as the separators. The :ref:`DateTime.FormatStyles` use whatever format that the user has set in these system settings and may not match what is in the table. To get the current date in any of these formats, simply create and instantiate a DateTime object and then call the ToString method and pass it the format style in which you want the DateTime. In this example, the current date formatted as a long date, is assigned to a variable: .. code:: xojo Var now As DateTime = DateTime.Now Var theDate As String theDate = now.FormatStyles.Long The :ref:`DateTime.SecondsFrom1970` property is the “master” property that stores the date/time associated with the object. .. _/topics/text_handling/formatting_values/times: Times ----- Like dates, time values are stored as part of a :doc:`DateTime`. Time values have five different formats: .. csv-table:: :header: "Property", "Default Output (on US-based systems)" :widths: auto "Full","3:09:52 PM Central Daylight Time" "Long","3:09:52 PM CDT" "Medium","3:09:52 PM" "Short","3:09 PM" "None","" As is the case with date formats, several aspects of these formats are controlled by the user via the Time screen in the Regional Settings Control panel on Windows or the Time Formats panel in “Languages & Text” preferences (Mac). .. _/topics/text_handling/formatting_values/see_also: .. seealso:: :doc:`Format`, :ref:`DateTime.ToString` functions; :doc:`DateTime` class; :doc:`Introduction to app localization` topic ========================== Right-to-left text support ========================== Xojo primarily uses native controls when building your apps. Because of this, with a few noted exceptions, right-to-left text support is supported for desktop, mobile and web without you having to do anything special. The exceptions for desktop apps are :doc:`DesktopBevelButton` and :doc:`DesktopListBox`. For :doc:`DesktopListBox` you would use the CellBackgroundPaint event handler to draw your text appropriately. The exception for web apps is :doc:`WebListBox`. Use :doc:`WebListBoxStyleRenderer` when you need right-to-left text support for a :doc:`WebListBox`. Right-to-left layout changes are not automatically supported by Xojo. However, you can use the Container to contain sections of your layouts and put code in the Container to manually adjust the layout as necessary. To learn how you can adjust a Container layout dynamically at run time, refer to: * Examples/Desktop/Custom Controls/OKCancelContainer * :doc:`DesktopContainer` .. _/topics/text_handling/right-to-left_text_support/see_also: .. seealso:: :doc:`DesktopContainer`, :doc:`WebContainer`, :doc:`MobileContainer` classes ========================================== Searching text using the SoundEx algorithm ========================================== Soundex is a phonetic algorithm for indexing names by sound, as pronounced in English. It can be a useful way to store names for searching so that similar names can be found. As defined by Wikipedia: The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling. The algorithm mainly encodes consonants; a vowel will not be encoded unless it is the first letter. Soundex is the most widely known of all phonetic algorithms (in part because it is a standard feature of popular database software). .. important:: Soundex is not currently supported for :doc:`SQLiteDatabase` on Android. SoundEx has these rules: 1. Retain the first letter of the name and drop all other occurrences of a, e, i, o, u, y, h, w. #. Replace consonants with digits as follows (after the first letter): * b, f, p, v → 1 * c, g, j, k, q, s, x, z → 2 * d, t → 3 * l → 4 * m, n → 5 * r → 6 #. If two or more letters with the same number are adjacent in the original name (before step 1), only retain the first letter; also two letters with the same number separated by 'h' or 'w' are coded as a single number, whereas such letters separated by a vowel are coded twice. This rule also applies to the first letter. #. If you have too few letters in your word that you can't assign three numbers, append with zeros until there are three numbers. If you have more than 3 letters, just retain the first 3 numbers. This is a Xojo SoundEx function: .. code:: xojo Public Function SoundEx(word As String) As String Const kLength As Integer = 4 Var value As String Var size As Integer = word.Length ' Make sure the word is at least two characters in length If (size > 1) Then word = word.Uppercase ' Convert the word to a character array for faster processing Var chars() As String = word.ToArray("") ' For storing the SoundEx character codes Var code() As String ' The current and previous character codes Var prevCode As Integer = 0 Var currCode As Integer = 0 ' Add the first character code.Add(chars(0)) Var loopLimit As Integer = size - 1 ' Loop through all the characters and convert them to the proper character code For i As Integer = 0 To loopLimit Select Case chars(i) Case "H", "W" currCode = -1 Case "A", "E", "I", "O", "U", "Y" currCode = 0 Case "B", "F", "P", "V" currCode = 1 Case "C", "G", "J", "K", "Q", "S", "X", "Z" currCode = 2 Case "D", "T" currCode = 3 Case "L" currCode = 4 Case "M", "N" currCode = 5 Case "R" currCode = 6 End Select If i > 0 Then ' two letters With the same number separated by 'h' or 'w' are coded as a single number If currCode = -1 Then currCode = prevCode ' Check to see if the current code is the same as the last one If currCode <> prevCode Then ' Check to see if the current code is 0 (a vowel); do not proceed If currCode <> 0 Then code.Add(currCode.ToString) End If End If End If prevCode = currCode ' If the buffer size meets the length limit, then exit the loop If (code.LastIndex = kLength - 1) Then Exit For End If Next ' Pad the code if required size = code.LastIndex + 1 For j As Integer = size To kLength - 1 code.Add("0") Next ' Set the return value value = String.FromArray(code, "") End If ' Return the computed soundex Return value End Function You call the SoundEx function like this: .. code:: xojo Var result As String result = SoundEx("Robert") ' R163 result = SoundEx("Rupert") ' R163 result = SoundEx("Rubin") ' R150 result = SoundEx("Ashcraft") ' A261 result = SoundEx("Ashcroft") ' A261 result = SoundEx("Tymczak") ' T522 result = SoundEx("Pfister") ' P236 .. _/topics/text_handling/searching_text_using_the_soundex_algorithm/see_also: .. seealso:: :doc:`Full Text Searching` topic ============================ Understanding text encodings ============================ All computers use encoding systems to store character strings as a series of bytes. The oldest and most familiar encoding scheme is the ASCII encoding which defines 128 character codes (using Integer values 0-127). These characters include only the upper and lowercase English alphabet, numbers, some symbols, and invisible control codes used in early computers. You can use the :ref:`String.ChrByte` function to get the character that corresponds to a particular ASCII code value. Over the years, ASCII was extended and other encodings were created to handle more and more characters and languages. In particular, the Unicode standard was designed to handle the encoding of any language and a mixture of languages in the same string. In your Xojo projects, any Strings you create in code (as constants, variables, or literals) use the UTF-8 encoding, which is what is most commonly used today. If the strings you work with are created, saved, and read only within your own apps, you shouldn't have to worry about encoding issues because the encoding used is stored along with the content of the string. If you are creating apps that open, create, or modify text files or data that are created outside of your app, then it's possible that the text was encoded using something other than UTF-8. For these situations you need to understand how text encodings work and what changes you may need to make to your code to make sure it recognizes the text as it was encoded. If your app assumes the text was encoded as UTF-8 but it was in fact encoded as WindowsLatin1, then you may find that some characters do not display properly. .. note:: Strings in Structures do not contain encoding information because they are just a series of bytes. .. _/topics/text_handling/understanding_text_encodings/from_ascii_to_unicode: From ASCII to Unicode --------------------- As you know, computers don't really store or understand characters. They store each character as a numeric code. For example, "A" is ASCII character number 65. When the computer industry was in its infancy, each computer maker came up with their own numbering scheme. A numbering scheme is sometimes called a character set. It is a mapping of letters, numbers, symbols, and invisible codes (like the carriage return or line feed) to numbers. With a character set, information can be exchanged between computers made by different manufacturers. In 1963 the American Standards Association (which later changed its name to the American National Standards Institute) announced the American Standard Code for Information Interchange (ASCII) which was based on the character set available on an English language typewriter. Over the years, computers became more and more popular outside of the United States and ASCII started to show its weaknesses. The ASCII character set defines only 128 characters. That covers what is available on an English-language typewriter, plus some special “control“ characters that can be used on computers to control output. It doesn't include special characters that are commonly used in typeset books such as curved quotes or the curved apostrophe, bullet characters, and long dashes—like this one. Also, many languages (like French and German) use accented characters that are not defined as part of the ASCII specification. When the Macintosh and Windows operating systems were introduced, each OS defined extensions to standard ASCII by defining codes from 128-255. This enabled both operating systems to handle accented characters and other symbols that are not supported by the ASCII standard. However, the Macintosh and Windows extensions do not agree with one another. Cross-platform applications have to build in some way of managing text that uses characters in the 128-255 range. The problem is even worse for users of languages that don't use the standard Roman alphabetic characters at all — like Japanese, Chinese, or Hebrew. Because there are so many characters, the character sets devised to support some of these languages use two bytes of data per character (rather than one byte per character, as in ASCII). Apple eventually created various text encodings to make it easier to manage data. MacRoman is a text encoding for files that use ASCII. MacJapanese is a text encoding for files that store Japanese characters. There are others as well. But these encodings were Mac-specific. They didn't make exchanging data with other operating systems any easier and mixing data with different encodings (typing a sentence in Japanese in the middle of an English-Language document, for example) was problematic. In 1986, people working at Xerox and Apple both had different problems to solve that required the same solution. Before long, the concept of a universal character encoding that contained all the characters for all languages, became the obvious solution. The universal encoding was dubbed “Unicode” by one of the people at Xerox that helped to create it. Unicode solves all of these problems. Any character you need from any language is supported and will be the same character on any computer that supports Unicode. And as a bonus, you can mix characters from different languages together in one document since all are defined in Unicode. Unicode support began appearing on the Macintosh with System 7.6 and on Windows with Windows 95. You could translate files between other text encodings and Unicode but Unicode was still the exception and not the rule. It wasn't until Mac OS X and Windows 2000 that Unicode became the standard. Computer users are now in a transition. There are some using older systems where Unicode is not the standard. All new systems that are running macOS, Windows, or Linux use Unicode as the standard encoding. As a result, you may have to deal with text files of different encodings for a while. That means you may need to modify your code to handle this. At some point in the future, it may be so rare that you can assume all files are in Unicode format but until then, you may need to make some modifications to your code so that your application operates properly when it encounters text with different types of encoding. .. _/topics/text_handling/understanding_text_encodings/handling_text_encodings: Handling text encodings ----------------------- Unfortunately, there is no perfectly accurate way to determine the encoding of a file. You have to know what encoding the file is using. For example, if it is coming from an English-speaking user of Windows, it's probably Windows ANSI. If the encoding of a string is defined, you can use the Encoding function to get its encoding, like this: .. code:: xojo Var theEncoding As TextEncoding theEncoding = Encoding(myString) where the variable myString contains the string whose encoding is to be determined and theEncoding is a TextEncoding object. If the encoding is not defined, the Encoding function returns :doc:`Nil`. Any String you create in Xojo has the UTF-8 encoding by default: .. code:: xojo Var testString As String = "Hello" Var theEncoding As TextEncoding theEncoding = Encoding(testString) ' theEncoding.Name = "UTF-8" When you get text from another source, such as an external file, database or the internet it is possible it has a different encoding. As mentioned above, there's no way to look at text and know what it's encoding is, but you should be able to determine from the originator of the text what the encoding is. If you get text in a different encoding, you'll want to tell Xojo what the encoding is. You can use the DefineEncoding function for this. So if you get text from a file that is in MacRoman encoding, then you can tell Xojo its encoding like this: .. code:: xojo ' InputText is the text from the file InputText = InputText.DefineEncoding(Encodings.MacRoman) With the correct encoding specified, the text will be properly displayed in your app. In other cases you may want to covert the encoding of the text to another encoding. For example, if the incoming text is MacRoman but you really want to work in UTF-8 you can convert the encoding. .. code:: xojo ' Define the encoding as MacRoman InputText = InputText.DefineEncoding(Encodings.MacRoman) ' Convert the encoding to UTF-8 InputText = InputText.ConvertEncoding(Encodings.UTF8) You may also want to convert the encoding if you app needs to output text in a specific encoding for use by another app. If you wanted to output your text as MacRoman you would also convert its encoding: .. code:: xojo OutputText = OutputText.ConvertEncoding(Encodings.MacRoman) .. _/topics/text_handling/understanding_text_encodings/text_encoding_and_files: Text encoding and files ----------------------- When dealing with text data in files, it is particularly important to handle encodings properly. Refer to :doc:`Accessing Text Files` for information about encoding with text files. .. _/topics/text_handling/understanding_text_encodings/getting_individual_characters: Getting individual characters ----------------------------- As was mentioned earlier, when you need to obtain an individual ASCII character, you can use the :ref:`TextEncoding.Chr` function by passing it the ASCII code for the character you want. It requires that you specify both the encoding and the character value. For example, the following returns the ÷ sign in the variable, s: .. code:: xojo Var divChar As String = Encodings.UTF8.Chr(247) .. _/topics/text_handling/understanding_text_encodings/see_also: .. seealso:: :ref:`TextEncoding.Chr`, :ref:`String.ChrByte`, :doc:`ConvertEncoding`, :doc:`DefineEncoding` functions; :doc:`TextEncoding` class; :doc:`Encodings` module ========================= Using Regular Expressions ========================= Regular Expressions (RegEx) are patterns that describe text. You can use regular expressions to find substrings in text and to make replacements within text. These are common characters used to create RegEx patterns: .. csv-table:: :header: "Matching Character", "Description" :widths: auto ".","wildcard, matches any character except /r and /n" "\\r","return" "\\n","newline" "\\d","Matches a string of digits" "\\w","Matches a string of word characters" "\\s","Matches whitespace" "*","Repeat pattern zero or more times" "+","Repeat pattern one or more times" .. note:: Regular Expressions are not currently supported for Android. .. _/topics/text_handling/using_regular_expressions/searching: Searching --------- Your apps can search and replace text using regular expressions (often called RegEx), a pattern describes specific text to find in a string. You use the properties of the :doc:`RegEx`, :doc:`RegExOptions` and :doc:`RegExMatch` classes to define a regular expression and search or replace text using regular expressions. Regular Expressions can be a bit tricky to get the hang of, but they are fast and efficient ways to process text. This code finds the first word in the text “Software development made easy”, returning “Software”: .. code:: xojo Var re As New RegEx Var match As RegExMatch re.SearchPattern = "\\w*" match = re.Search("Software development made easy") If match <> Nil Then MessageBox(match.SubExpressionString(0)) End If Here are some examples of other search patterns on the same text "Software development made easy": .. csv-table:: :header: "Pattern", "Result" :widths: auto "made","made" "simple","Text Not Found" "^.","S" "[wb]are","ware" "..sy$","easy" Remember that a RegEx result (RegExMatch) may return more than one match. You should check RegExMatch.SubExpressionCount to see how many matches were returned. .. _/topics/text_handling/using_regular_expressions/replacement: Replacement ----------- Regular Expressions can also be used to replace strings in text. This code replaces “Software development” with “Programming”: .. code:: xojo Var re As New RegEx re.SearchPattern = "Software development" re.ReplacementPattern = "Programming" Var result As String result = re.Replace("Software development made easy") MessageBox(result) The result variable now contains “Programming made easy”. This removes HTML tags from source HTML: .. code:: xojo Var re As New RegEx re.SearchPattern = "<[^<>]+>" re.ReplacementPattern = "" Var html As String = "

Hello,

There

" Var plain As String = re.Replace(html) While html.Compare(plain) <> 0 html = plain plain = re.Replace Wend MessageBox(plain) .. _/topics/text_handling/using_regular_expressions/see_also: .. seealso:: :doc:`RegEx`, :doc:`RegExOptions`, :doc:`RegExMatch` classes ================== Working with Fonts ================== A font defines how text is displayed on the screen. Typically a font has a family, such as "Helvetica, Times, Courier, etc." and a corresponding size that is usually measured in points. For most of your apps you will want to use the system standard font size and point size so that things are as readable as possible and so that any changes the user makes to accessibility settings (to alter font readability) are properly used by your app. Still, there may be cases when you need to choose a specific font for your text display. This topic describes how that works for desktop, web and iOS projects. .. _/topics/text_handling/working_with_fonts/desktop_fonts: Desktop Fonts ------------- You have the ability to set the font, font size, and font style of many of the objects and controls in your app. Text Areas support multiple fonts, styles, and sizes (collectively referred to as styled text) and List Boxes support multiple styles as well. Desktop controls that use a single font have a TextFont property that you can set by assigning it the name of the font you want used to display text for the control. Additionally, many controls also have checkboxes to specify if the font should be Bold, Italic or Underlined. Text Areas have a TextFont property but they can also display multiple fonts and all kinds of formatted text. For information on this, refer to the :doc:`Creating Styled Text` topic. .. _/topics/text_handling/working_with_fonts/system_and_smallsystem_fonts: System and SmallSystem Fonts **************************** The System font is the font used by the system software as its default font. It's the font used for the menus as well. This font varies by operating system and the user's preferences. .. image:: https://documentation.xojo.com/topics/text_handling/images/working_with_fonts_desktop_text_font_inspector.png If you want text to be shown using the user's System font, use the name "System" as the font when you assign it. You can enter it as the TextFont property in the Inspector. If you also enter zero (0) as the font size, the font size that works best for the platform on which the app is running is used. Because of differences in screen resolutions, different font sizes are often required for each operating system platform. This feature enables you to use different font sizes on different platforms without having to create separate windows for each platform. Use the Inspector to set the font name to “System” and the font size to zero (0). If the system software supports both a large and small System font, you can also specify the "SmallSystem" font as the font name. This option selects the small system font on the user's computer, if there is one. If there is no small system font, the System font is used. .. image:: https://documentation.xojo.com/topics/text_handling/images/working_with_fonts_desktop_system_and_smallsystem_fonts.png On Mac, both System font and the Small System font are supported. .. _/topics/text_handling/working_with_fonts/available_fonts: Available Fonts *************** You may want to use fonts other than the System font. In this case you will need to determine if a particular font is installed on the user's computer. There are two global functions, :ref:`System.FontCount` and :doc:`Font`, that make determining available fonts easy. The following function, when passed a font name, will return True or False to inform you if the font is installed: .. code:: xojo Function FontAvailable(FontName As String) As Boolean Var i As Integer For i = 0 To System.FontCount - 1 If System.FontAt(i) = FontName Then Return True End If Next Return False End Function .. _/topics/text_handling/working_with_fonts/building_a_font_menu_dynamically: Building a font menu dynamically ******************************** Suppose you want to create a Fonts menu that will display all the fonts on the user's computer. You don't know which fonts are installed in advance, you need to create the menu dynamically when the app starts. To do so, you create a instance of the MenuItem class and instantiate it for each font. The Action event for the class instance handles the menu selection. The :doc:`Creating Menus` topic shows you how to do this. .. _/topics/text_handling/working_with_fonts/macos_fonts: macOS Fonts *********** With Mac apps, not all fonts can appear as Bold, Italic or Underlined even if those properties are selected. Because of the way Mac draws fonts, Bold, Italic and Underline are only displayed for fonts that include it as part of the font definition. These settings cannot be applied to just any font as they can be on other platforms. Instead, on Mac Xojo has to look up the specific font to use based on what you set for font properties. If you are not seeing the font setting you expect, be sure to verify that the font itself supports it, which you can do using TextEdit or the Font Book app. On newer version of MacOS (starting with Sierra) your apps may perform slower if you are relying on Xojo to do font lookups. Here is an example of what happens behind the scenes. Say you want "Arial" in Bold to draw on a Graphics surface of a Canvas with code like this: .. code:: xojo g.FontName = "Arial" g.Bold = True Xojo takes the font family name (Arial) and the fact that you enabled the Bold property and finds the correct font variant in the "ArialMT" font, which is has this PostScript name: "Arial-BoldMT". It then uses that font to do the actual drawing. You can look up these names yourself using the Mac Font Book app. If this causes performance issues in your app, you can avoid the lookup by directly specifying the PostScript font name (and not using the Bold or Italic properties). For example instead of the code above, you can do this: .. code:: xojo g.FontName = "Arial-BoldMT" .. _/topics/text_handling/working_with_fonts/web_fonts: Web Fonts --------- For web apps, you use the control's :doc:`Style` property to set the font or do it through the Inspector. You can add any of :doc:`Google's web fonts` to your web app as well. .. _/topics/text_handling/working_with_fonts/ios_fonts: Android/iOS Fonts ----------------- .. image:: https://documentation.xojo.com/topics/text_handling/images/working_with_fonts_ios_font_inspector.png For Android & iOS apps, you use the :doc:`Font` class to specify font settings for display. It has a constructor that takes a parameter for the font's postscript name and point size. For example, this code can set a button text to use Arial at 18 point: .. code:: xojo Var courier As New Font("ArialMT", 18) Button1.CaptionFont = courier To use the system font, you can use the shared methods like this: .. code:: xojo Label1.TextFont = Font.SystemFont(0) You can also set fonts for the Text Field and Text Area controls using the advanced tab of the Inspector. .. _/topics/text_handling/working_with_fonts/see_also: .. seealso:: :doc:`Font` class; :ref:`System.FontAt`, :ref:`System.FontCount` methods; :doc:`Changing the Appearance of Web Controls`, :doc:`Creating Styled Text` topics Threading ========= .. toctree:: :maxdepth: 1 :name: sec-threading Creating Helper Apps Running Code in a Thread Running Code periodically with a Timer ==================== Creating Helper Apps ==================== There are times when you may want to take advantage of multiple CPU cores, something that you cannot do with :doc:`Threads`. With a multi-core CPU, your computer can literally do multiple things at one time, which is called multiprocessing. With a little careful planning, your Xojo apps can use multiprocessing for significant performance improvements. You may be thinking, isn't this what threads are for? Sometimes, although not with Xojo. As noted in the previous section, Threads in Xojo use co-operative threading, which is simpler and more efficient but means that all the threads run on a single CPU core. .. _/topics/threading/creating_helper_apps/the_worker_class: The Worker class ---------------- If you are intending to create a helper app for data processing, the first thing you should do is check out the :doc:`Worker` class as it's designed to do just that and takes care of all the overhead of having a helper app for you. .. _/topics/threading/creating_helper_apps/console_app_helpers: Console App helpers ------------------- With Xojo, separate processes using console apps is the way to take advantage of multiple CPU cores. The technique is relatively simple. You create a console app that does the processing you need and returns a result. You then have your main (GUI) app start one or more of these console apps and supply them with the data they need to process. Each console app goes off on its own (in its own memory space and using its own CPU core -- if one is available) to do the processing. When one finishes, it returns the result, which your app can then use. .. _/topics/threading/creating_helper_apps/communication_techniques: Communication techniques ************************ There are two common ways for your main app to communicate with the console apps: :doc:`Shell` or :doc:`IPCSocket`. Using a Shell is simplest. You create a Shell instance for each console app and launch the console app from the Shell. You can get the return result using the Shell methods and properties. An IPCSocket is more useful when the console apps need to continuously communicate with the main app, but it is also a more advanced technique. .. _/topics/threading/creating_helper_apps/word_counter_example: Word counter example -------------------- Imagine you are responsible for receiving documents to publish on the company newsletter and need to know the word counts. You used Xojo and created a quick app to add a bunch of documents to a window and go through and count all the words in each document, one by one. The WordCounter example does this simple task of counting words in text files. The word count algorithm is not very speedy, but that is OK because the example also shows you how you use multiple CPU cores to speed it up. The WordCounterGUI app displays the UI and can count the words. The WordCounter console app is a helper that is used to count the words using multiprocessing. The example can count words using three different techniques: * Sequential: This simply counts the words in each file, one after the other in the main thread. This locks up the UI while the words are being counted. * Threaded: This creates a separate thread for each file and the words are counted using the thread. The UI is reponsive while the words are being counted. * Processes: This launches the WordCounter console app for each file and the words are counted by the console app. The UI is responsive while the words are being counted. The four sample documents used to test the different techniques are chapters from *A Princess of Mars* by Edgar Rice Burroughs. As you might expect, the sequential method is pretty slow, taking about 44 seconds on the test machine to count all the words in the four documents. The thread method is slightly slower at 46 seconds. This may surprise you, but it shouldn't. As covered in :doc:`Running code in a thread`, threads in Xojo are co-operative. This means they still run on just a single CPU core. Add in the extra overhead for managing the threads and you get a slightly slower run time. But the threaded version is much nicer to use. The word counts appear in the order they are completed (shorter files to longer files) and the ProgressWheel spins while it is counting words. The processes method is fastest. On the test computer it finished in about 21 seconds. Four separate console apps were launched, one for each document, and each quickly took over its own CPU. As each console app finished, the results were returned back to the GUI and displayed. Of course you will get different times on your system depending on the OS and number of CPU cores. To try the app, run the built version for your OS platform in the WordCounterGUI Build folder. This folder has the console app set up properly for each platform. If you want to run WordCounterGUI directly from Xojo, you will need to make sure you copy the corresponding console app so that it is alongside the debug app (or change the code that finds the console app). To use the app, click "Add File" to add one or more files (you can multi-select in the file selector dialog). You can then choose from Sequential, Threaded or Processes to choose the processing technique. Then press "Count Words" to count the words. .. _/topics/threading/creating_helper_apps/how_does_it_work?: How does it work? ***************** The WordCounterGUI app has four main project items: MainWindow, WordCounter, WordCountThread and WordCountShell. **MainWindow** is the UI. It does not do any processing. **WordCounter** is a simple class that take a file (in the constructor) and counts the words by calling the Words method. It is called by all three of the processing methods. **WordCountThread** is a Thread subclass that uses WordCounter to count the words in a file. A separate instance of WordCountThread is created for each file added. MainWindow has a Timer to watch the progress of these threads so that the word counts can be updated in the ListBox and the elapsed time calculated. **WordCountShell** is a Shell subclass (actually, an asynchronous shell) that starts a console app and waits for the word count result. A separate instance of WordCountShell is created for each file added. MainWindow has a Timer to watch the progress of these shells so that the word counts can be updated in the ListBox and the elapsed time calculated. The console app is event simpler. It contains the same WordCounter class from above. In the Run event handler, it gets the file path (that was passed as a parameter) and sends it to a WordCounter instance to count the words in the file. The results are printed to the output and the console app quits. .. _/topics/threading/creating_helper_apps/specifics_about_the_processes_technique: Specifics about the processes technique *************************************** Look at the code in MainWindow.CountWordsInFilesUsingProcesses. The code is pretty simple: it loops through each file added to the ListBox and creates a new WordCountShell, passing in the file. Then the WordCountShell.CountWords method is called to count the words. When the console app outputs the word count, the DataAvailable event handler is called and the word count is saved in a property. The shell then stops running. ShellTimer on MainWindow periodically checks to see if a shell has stopped running. If it has, then it gets its word count and updates the appropriate file in the ListBox. WordCountShell.CountWords has the code that starts the console app using the shell and passes in the file name as a parameter. The shell runs asyncrhonously so that other processing can continue. That is pretty much it. Overall it is a simple technique, but you can certainly make it more sophisticated. For example, an :doc:`IPCSocket` could be used in place of the shell for communicating with the console app. And this might make more sense if you want to have a fixed number of console apps continuously running in the background, waiting to be assigned work. .. _/topics/threading/creating_helper_apps/see_also: .. seealso:: :doc:`ConsoleApplication` class; :doc:`Running code in a thread` topic ======================== Running Code in a Thread ======================== Threads provide a way for you to run code separately from the main thread that is also used by the user interface. They are particularly useful for long-running tasks that might otherwise make your application appear as if it is frozen because no user interface updates can occur. Threads can be cooperative or preemptive: .. csv-table:: :header: "Type", "Description" :widths: auto "Cooperative", "The default type. A cooperative thread shares time with your app (the main thread) and any other cooperative threads you have running. It can access the entire Xojo framework." "Preemptive", "A preemptive thread runs on its own CPU core independently of your app. Only parts of the Xojo framework that are not user interface can be safely accessed. Accessing an unsafe framework object (such as a :doc:`Window`) will cause the app to crash." .. note:: Pre-emptive threading can be complex. If you need to run processes on multiple cores, another consideration is using separate helper console applications and communicate with them. One technique is shown in the :doc:`Creating Helper Apps` topic. .. _/topics/threading/running_code_in_a_thread/creating_a_thread: Creating a Thread ----------------- To create a thread, first you need to add a :doc:`Thread` object to your project (iOS projects use :doc:`Thread`). You can do this by dragging a Thread from the Library onto a window, web page or to the Navigator. The code that you want to run in the thread is placed in the Run event handler. Anything you call from the thread is considered part of the thread and also runs in the background. To start a thread you call its :ref:`Run` method, which calls the code in the Run event handler. If you want the thread to be preemptive, make sure you set the :ref:`Type` property before you call the :ref:`Run` method. Once the thread is running, the :ref:`Type` of the thread can only be changed from within the thread itself. .. tip:: If you want to know how many `logical cores `_ there are on the device upon which your app is running you can call the :ref:`System.CoreCount` function. .. _/topics/threading/running_code_in_a_thread/how_cpu_time_is_shared: How CPU time is shared ********************** Cooperative threads can yield time to other threads each time they execute a looping construct such as For, While, and Do. However, a thread does not necessarily yield time at every loop boundary even though it has an opportunity to do so. A thread actually yields to another thread when the Thread Scheduler decides that its timeslice has expired. Context switches are expensive, so the Thread Scheduler always tries to avoid them. You can also force context switches by calling Application.YieldToNextThread or by calling Application.SleepCurrentThread. Each preemptive thread is like a separate application in that they share time with other processes running on the logical core. .. _/topics/threading/running_code_in_a_thread/cooperative_thread_priority: Cooperative thread priority *************************** As the name suggests, the Priority property of the Thread class dictates how much priority that thread should have in terms of time allocated to it to execute. This applies to cooperative threads only. By default a thread has a priority of 5. This is the same priority as the main application thread, so if you leave your thread at 5 it will have the same amount of time allocated to it as the main thread. For example, presume there are 100 "units" of thread time available. If both the main thread and your thread have a priority of 5 then the time unit split is calculated like this: Total Priority = 5 (main) + 5 (your thread) = 10 Time Units (your thread) = (5/10) * 100 = 50 Time Units (main thread) = (5/10) * 100 = 50 This means that the main thread runs 50 times and your thread runs 50 times. But what if you want your thread to run more often because it is doing some heavy processing? In this case you would increase its priority. If you change your thread's priority to 15 then the time unit split is calculated the same, but results in more time units for your thread: Total Priority = 5 (main) + 15 (your thread) = 20 Time Units (your thread) = (15/20) * 100 = 75 Time Units (main thread) = (5/20) * 100 = 25 This means your thread will get 75 of the 100 time units and the main thread will get only 25. So your thread is running 3 times more often than the main thread. .. youtube:: IeMrEK__wGo .. _/topics/threading/running_code_in_a_thread/thread_control: Thread control ************** Threads can be slept, suspended, resumed and killed. When you sleep a thread, you specify the amount of time (in milliseconds) for the thread to sleep. It will automatically wake itself when the time has elapsed. If you suspend a thread, it stays suspended until you specifically resume it. Finally you can kill a thread, which terminates it. Each of these actions changes the state of the thread. You can check the thread state any time using the State property. A thread can be Running (0), Waiting (1), Suspended (2), Sleeping (3) or NotRunning (4). Overflowing the stack ********************* No stack overflow checking is done on preemptive threads. If your stack gets too deep, your app will crash. .. _/topics/threading/running_code_in_a_thread/sharing_resources: Sharing resources ***************** Sometimes you may have a resource (data or a file, for example) that needs to be used by multiple threads that are all currently running. An example would be that a thread tries to open a file for writing that another thread has already opened for writing. This can cause issues and unwanted exceptions. You can manage this by using a :doc:`CriticalSection`, :doc:`Semaphore` or :doc:`Mutex` to prevent multiple threads from trying to access the same shared resource. This is even more important for a preemptive thread as it can be interrupted at any time. When used in a preemptive thread, both :doc:`CriticalSection` and :doc:`Semaphore` have a Type property that must be set to *preemptive* to work properly. .. _/topics/threading/running_code_in_a_thread/communicating_with_the_user_interface: Communicating with the user interface ------------------------------------- Because of operating system restrictions, threads can not directly access or manipulate any user interface element. Should a thread access a UI element your app will raise a :doc:`ThreadAccessingUIException`. If you have a thread that needs to update user interface in some way, such as updating a Progress Bar, you should instead use a Timer as an intermediary. Rather than having your thread update a Progress Bar directly, a Timer periodically get the progress value from the thread and then the Timer (which runs on the main application UI thread) updates the Progress Bar. Here is an example of the Run event handler of a thread that was added to a window. The thread loops through an array (with a pause in the middle). The position in the array is stored as a property of the window (ArrayPosition) as is the maximum value of the array (ArraySize): .. code:: xojo Var arrayValues() As Integer arrayValues = Array(1, 2, 3, 4, 5, 6, 7, 9, 10) ArraySize = arrayValues.LastIndex For i As Integer = 0 To ArraySize ArrayPosition = i Thread.SleepCurrent(1000) ' Pause for 1 second Next A separate timer can check the value for ArrayPosition and ArraySize and use them to update the Progress Bar. This code is in the Action event handler of a Timer on the window: .. code:: xojo If CountThread.ThreadState = Thread.ThreadStates.NotRunning Then Me.Enabled = False MessageBox("Finished!") Else ThreadProgress.Value = ArrayPosition ThreadProgress.Maximum = ArraySize End If In the Pressed event handler of a button on the window, this code starts the thread and the timer: .. code:: xojo If CountThread.ThreadState = Thread.ThreadStates.NotRunning Then CountThread.Run CountTimer.Period = 500 CountTimer.RunMode = Timer.RunModes.Multiple CountTimer.Enabled = True End If As the Thread runs, it updates the *ArrayPosition* property on the window. The Timer periodically updates (about every 1/2 a second) the Progress Bar on the window with the value of the property. This two-step process prevents the thread from directly updating the user interface. .. _/topics/threading/running_code_in_a_thread/using_the_task_class: Using the Task class ******************** Included with Xojo is an example of a Task class that can be used to do this as well (Desktop/UpdatingUIFromThread/UIThreadingWithTask). Task is a Thread subclass that has an UpdateUI event handler and method. Use it in place of a Thread when you want your thread to be able to update the user interface. In the Task's Run event handler, you call the UpdateUI method when you want to update any UI. Then in the UpdateUI event handler, you can have code that directly accesses the UI. When you call UpdateUI, you can pass either a list of values (using a series of :doc:`Pair`s) or you can pass a :doc:`Dictionary` of values. Regardless, in the UpdateUI event handler, you get a dictionary of the values. You can then check the values in the dictionary to determine what to update in the UI. .. _/topics/threading/running_code_in_a_thread/see_also: .. seealso:: :doc:`Thread`, :doc:`CriticalSection`, :doc:`Semaphore`, :doc:`Mutex`, :doc:`ThreadAccessingUIException` classes; :doc:`Running code periodically with a Timer` topic ====================================== Running code periodically with a Timer ====================================== A Timer is a class that runs code periodically on some sort of interval (or period). They provide a great way to your application to be doing some processing while it might otherwise be idle, which can sometimes be a simple way to give the illusion of concurrency. Timers are often used in conjunction with threads to provide UI updates for background processing. .. _/topics/threading/running_code_periodically_with_a_timer/types_of_timers: Types of Timers --------------- Desktop and iOS projects can use the Timer class to create timers. Web projects can use the WebTimer or Timer class to create timers. In most case you will want to use the WebTimer class which creates a Timer that runs in the browser. Timers created using Timerr run on the server. Timers on the Desktop have an Action event handler (on Mobile and Web it's called Run) where you can put the code you want to run along with Period and Mode properties that let you specify how often the Action/Run event handler is called. In all cases, **code run from a Timer runs in the main (UI) thread** so it is best to not have long-running code called by a timer as it will block the rest of your app from being able to do anything. This also means that if your app is busy doing processing it will be unable to call the Timer when its period is reached. If you need more precise control of your processing you will want to use Threading. Avoid having long-running code called by a Timer as it will block the rest of your app. .. _/topics/threading/running_code_periodically_with_a_timer/using_a_timer_in_a_desktop_project: Using a Timer in a desktop project ---------------------------------- For desktop projects, you use the Timer class to create a Timer. The code that you want to run periodically goes in the Action event. You specify the period in milliseconds using the Period property. It tells the Timer how frequently the Action event is called. The RunMode property is used in conjunction with this to specify if the Timer is set to run multiple times, just a single time or is off completely. You can drag a Timer from the Library onto your layout to create a Timer subclass that you can use. When you do this you get the classic Timer class. You put the code you want to run when the timer period is reached in its Action event handler. For a Timer that is set to run multiple times (``RunMode = Timer.RunModes.Multiple``) and with a Period of 500 (1/2 second). The following code in a Timer will increase a counter displayed in a Label about every 1/2 second: .. code:: xojo Var value As Integer value = CounterLabel.Text.ToInteger value = value + 1 CounterLabel.Text = value.ToString You can set a Tolerance to tell the OS how precise you need the Timer to be. A less precise Timer makes better use of system resources and will not impact the battery on portable devices as much. Timer also has CallLater methods that are handy for when you need a one-time timer (for example to update the UI after a short period of time) and don't want to create a subclass. For example, suppose you want to display some help text for a few seconds and then hide it. You can do this by creating a method to clear a Label (ClearLabel): .. code:: xojo Sub ClearLabel MyLabel.Text = "" End Sub In another method, you set the Label help text and then use CallLater to tell the Timer to call the Clear method after 2 seconds: MyLabel.Text = "Help text goes here" .. code:: xojo Timer.CallLater(2000, AddressOf ClearLabel) .. _/topics/threading/running_code_periodically_with_a_timer/using_a_timer_in_a_web_project: Using a Timer in a web project ------------------------------ Web projects can use the WebTimer class to create a Timer that runs in the web browser. This is useful for creating a session-specific Timer that updates the UI. The simplest way to do this is to drag a Timer from the Library onto your layout, set the Period and RunMode properties and add the Action event. The same code from above also works with a web project increase a counter displayed in a Label every 1/2 second: .. code:: xojo Var value As Integer value = CounterLabel.Text.ToInteger value = value + 1 CounterLabel.Text = value.ToString You can also use Timer in a web app but these will run on the server and will not be session-specific so they are far less useful. .. _/topics/threading/running_code_periodically_with_a_timer/using_a_timer_in_an_ios_project: Using a Timer in Mobile projects -------------------------------_ Mobile projects also use the Timer class to create a Timer. You can drag the Timer from the Library onto your layout to add a Timer. Set its RunMode and Period properties and add the Run event for your code. With a Period of 500 (milliseconds) and a RunMode of Multiple, this code will increment a counter in a Label: .. code:: xojo Var value As Integer value = Integer.FromString(CounterLabel.Text) value = value + 1 CounterLabel.Text = value.ToString .. _/topics/threading/running_code_periodically_with_a_timer/using_addhandler_with_a_timer: Using AddHandler with a Timer ----------------------------- You can also use a Timer without adding it to the layout, at least in desktop and Mobile projects. In this case you would create a Timer instance in a property, create a method to serve as its Action/Run event and then use the AddHandler command to tell the Timer that the method will be called in place of the Action/Run event. Below is how you would do it for an Mobile project. Start by adding a property to the layout: .. code:: xojo MyTimer As Timer Next, add a ProgressBar to the layout (this example code uses MobileProgressBar). The Timer will simply update the ProgressBar. In the Opening event handler of the layout, instantiate the Timer and indicate that its RUn event handler should be handled by a method, TimerRun, that you will add to the layout: .. code:: xojo MyTimer = New Timer MyTimer.Period = 1000 MyTimer.RunMode = Timer.RunModes.Multiple AddHandler MyTimer.Run, AddressOf TimerRun Now add the TimerRun method to the layout: .. code:: xojo Sub TimerRun(sender As Timer) ' This code works on mobile projects and uses MobileProgressBar. ' Change the properties as appropriate for desktop apps. If ProgressBar1.Value < ProgressBar1.MaximumValue Then ProgressBar1.Value = ProgressBar1.Value + 1 Else ' Stop Timer and Remove the handler sender.RunMode = Timer.RunModes.Off RemoveHandler MyTimer.Run, AddressOf TimerRun End If End Sub It is important that the first parameter to TimerRun must be of the type of the original object, in this case Timer. When you run the project, the ProgressBar is updated once per second. .. _/topics/threading/running_code_periodically_with_a_timer/using_timer_in_a_console_project: Using Timer in a console project -------------------------------- To use Timer in a Console project you have to either subclass the Timer or use AddHandler to map the Action event to a method as shown above. In addition you have to add an event loop to your Console project using DoEvents. As an example, the following code uses a Timer with AddHandler to display output to the terminal about once a second. Create a new Console project and add a property to the App object: .. code:: xojo MyTimer As Timer Add the Action event to the App object and put this code to initialize the Timer and map its Action event to the TimerAction method: .. code:: xojo MyTimer = New Timer MyTimer.Period = 1000 MyTimer.RunMode = Timer.RunModes.Multiple AddHandler MyTimer.Action, AddressOf TimerAction While True App.DoEvents Wend Now add the TimerAction method: .. code:: xojo Public Sub TimerAction(sender As Timer) Print("Timer called.") End Sub Run the project and you'll see "Timer called." output to the Terminal (or Command shell) about once a second. Press Control-C to quit the console app. .. _/topics/threading/running_code_periodically_with_a_timer/see_also: .. seealso:: :doc:`Timer`, :doc:`WebTimer` classes User interface ============== .. toctree:: :maxdepth: 1 :name: sec-user_interface Creating your own controls Design tips Desktop Dynamically adding and removing controls iOS Playing movies and sound Sharing event handlers with Control Sets Supporting Dark Mode Web Windows UI guidelines ========================== Creating your own controls ========================== In this tutorial you'll learn how to create your own controls for your desktop and web projects. This tutorial assumes you have gone through the :doc:`Desktop QuickStart` and :doc:`Desktop Tutorial` and thus have a basic understanding of how to navigate around a simple project, get to the Inspector, the Library, the Code Editor, etc. If you haven't gone through the Desktop QuickStart and Desktop Tutorial yet, you should do so first. Xojo has many built-in controls each with lots of functionality. Having said that it can't account for everything so sometimes you need to add your own custom logic. I 'm going to use one of the built-in controls, the TextField, as a starting point then I 'll show you how to create completely new controls using the Canvas control. .. _/topics/user_interface/creating_your_own_controls/custom_version_of_built-in_controls: Custom version of built-in controls ----------------------------------- Say you are creating data entry layouts with lots of TextFields and you want to make sure that the user doesn't accidentally add spaces at the beginning or end of the entries. The TextField control doesn't do that by default. The good news is that the Xojo framework has a Trim function that does this. Add this line of code: .. code:: xojo Me.Text = Me.Text.Trim to the FocusLost event of a TextField and you 'll have just that. If the user leans on the spacebar, when they leave the field, your code will remove any spaces at the beginning or end of the entry. This is great, but it what if you have a lot of TextFields over many, many layouts? Adding this bit of code over and over to 20 or more TextFields would get tedious, not to mention the work entailed if you ever decided to update that code in the future. The solution is to create your own TextField control that has this trimming functionality built-in to its FocusLost event. Then you would just use your custom TextField everywhere in your app that needs it. As it turns out, this is easy to do with subclassing. With subclassing you create a new class that is based upon an existing class. The new class has all the same properties, methods and events as the original along with any new or changed functionality you add. Here's how you create a subclass of the Textfield that has trimming code built-in to it: 1. Launch Xojo and create a new Desktop project. A web project would also work. #. Add a new class to the project by choosing New Class from the Insert menu or Toolbar item. #. In the Inspector, change the name from Class1 to DataEntryField. #. Change the Super in the Inspector to TextField. It is this that makes our new DataEntryField class a subclass of TextField. It will have all the features of the regular TextField but we will also be able to add our own custom features. For a web project, change the Super to WebTextField. #. Now add the FocusLost event to the subclass. Right-click on DataEntryField in the Navigator, choose Add to DataEntryField and from the menu that appears choose Event Handler... #. In the Add Event Handler dialog box, choose FocusLost and click OK. #. In the FocusLost event, add the following code: .. code:: xojo Self.Text = Self.Text.Trim 8. In the Navigator, click on Window1 to select it. For a web project click on WebPage1. #. Drag DataEntryField from the Navigator and drop it somewhere on Window1 (or WebPage1). This creates an instance of it. #. The code runs when the control loses focus but since this control is the only control on the window, the FocusLost event won't run or at least not until you close the window or quit the app while the window is open. Thats isn't very effective for a demonstration, so drag DataEntyField from the Navigator to Window1 to create another control. Now the FocusLost event can fire when the user tabs or clicks out of the field since there's another control on screen that can receive the focus. #. Run your project. #. Try entering spaces at the beginning, in the middle and at the end of an entry in either TextField. You 'll notice that when you exit the field the spaces at the beginning and end are removed. #. Quit your app to go back to the IDE. Now when you want this functionality in a field, you can drag a DataEntryField to the layout or select an existing TextField and change its Super to DataEntryField. No more copying and pasting code. Also, should you ever want to make this functionality more sophisticated, you only need update the DataEntryField class in your project and all instances of it on all the different layouts you have in your project will automatically use the new version because the code only exists in one place. There is, however, a downside. Say you had a DataEntryField control on one of your layouts and needed to add some additional logic to the FocusLost event but just for a specific instance of it and not any of the others. Naturally, you 'd just go to the Add Event Handler dialog box for that control to add the FocusLost event only to discover it's not there. It's not there because your DataEntryField class is handling it. But, fear not. You can add back the FocusLost event. Here's how: 1. Right-click on the DataEntryField in the Navigator and choose Add to DataEntryField then choose Event Definition from the menu. #. An Event Definition, as the name implies, defines an event. In this case, you will define one called FocusLost. In the Inspector, type FocusLost as the name for the new event definition. #. In order for you to be able to use this event for any instance of DataEntryField, you need to call it. In the Navigator, click on the FocusLost event handler of DataEntryField. You should see your line of code there from before. #. The FocusLost event you are adding back can run at any time. You might want the instance event to run before the class event or after. For this example, make the instance event run after the trim code so type FocusLost on a new line below the line that is already there. Your code should now look like this: .. code:: xojo Self.Text = Self.Text.Trim FocusLost Now if you add some code to a specific instance of the DataEntryField, the DataEntryField's implementation of the FocusLost event will run first (with the trim code) followed by the local, instance-specific FocusLost code. You can add some code to an instance of the DataEntryField class to see the effect. 5. Go to Window1 and double-click on the first DataEntryField control. #. Notice that the FocusLost event you just added appears in the Add Event Handler dialog. Select it and click OK. #. As an example, you can add code that prevents this particular field from being left blank. Add the following code: .. code:: xojo If Me.Text.IsEmpty Then System.Beep MessageBox("This field is mandatory. It cannot be left blank.") Me.SetFocus End If 8. Run your project and try it out by entering spaces at the beginning and end of the field. #. When you 're done testing, quit your app and return to Xojo. The event could have been called anything you like and you could then call it anywhere in your DataEntryField's code. In this particular case it makes sense to call it FocusLost and to call it from the DataEntryField's FocusLost event because you are giving the user an option to add code to the FocusLost event of any particular instance of the DataEntryField on any particular layout. You could do the same with just about any event of any control. This gives you a lot of flexibility to create your own controls based upon the existing built-in controls. You can also create a subclass of a control in the Library by dragging the control from the Library and dropping it into the Navigator. .. _/topics/user_interface/creating_your_own_controls/what_about_ios?: What about iOS? --------------- You may have noticed that the example above doesn't mention iOS. This is because the iOSTextField control does not have a FocusLost event at this time. However, once it has one, the instructions for doing the same task would be identical. Thus the line of code for the FocusLost event would be: .. code:: xojo Self.Text = Self.Text.Trim In the meantime, for iOS you can do this kind of trimming or checking for mandatory fields, in a toolbar button the user taps when they finish editing. .. _/topics/user_interface/creating_your_own_controls/custom_drawn_controls: Custom drawn controls --------------------- What if you need a control that doesn't exist amongst Xojo's built-in controls? Perhaps io's not even a control that the OS provides, but something entirely custom directly from your own imagination. Consider a simple control called TrafficLight. It's a circle that when clicked, changes from green to yellow to red. Xojo doesn't have this built-in nor does any OS provide it. The good news is that this is not a problem because you can create custom controls with the Canvas control. The Canvas control is nothing more than a blank area where you do all the drawing and respond to all of the events. You control everything. .. image:: https://documentation.xojo.com/topics/user_interface/images/creating_your_own_controls_customcontrols_tutorial_ovals.png Here's how to create the TrafficLight control. Like the DataEntryField control, you could recreate or copy and paste this each time you needed one but that would be tedious to maintain so instead you will start off with a new class that is a subclass of the Canvas control. 1. Choose New Class from the Insert menu or from the Insert toolbar button. #. In the Inspector, change the name of the class to TrafficLight. #. Change the Super to Canvas. You will need to store the color that was last used when drawing the control so you know which color to use next. Add a property to store that. 4. Right-click on the TrafficLight class in the Navigator, choose “Add to TrafficLight” and then choose Property from the menu. #. In the Inspector, change the name of the new property to LightColor. #. Change the property type to Color. #. Set the Default value to Color.Green. Xojo has some basic colors built-in so you don't have to figure the proper red, green and blue (or RGB) values to create them but you can create any color you want. Next you need to add the Paint event. This event is called each time the canvas needs to be redrawn. This can happen for a number of reasons. For example, your code might redraw the canvas in response to a user's click (or tap on a mobile device). If the layout has just been opened, the canvas will need to be drawn or redrawn if ir's exposed when a user drags away a window that was obscuring it. The Paint event is passed a Graphics object. This represents the graphics that will be drawn into the canvas when the Paint event completes. Your code call methods of this graphics object in order to draw whatever it is you want to appear in the canvas. To add the Paint event: 1. Right-click on the TrafficLight class in the Navigator, choose “Add to TrafficLight” and then choose Event Handler from the menu. #. Select the Paint event handler and click OK. #. Add a line of code that sets the color used for drawing to the new property. Add the following line of code: .. code:: xojo g.DrawingColor = LightColor Now draw the circle using the FillOval function. This function takes the X and Y coordinates where you want to draw the upper-left position (within the canvas) as well as the width and height of the oval you want to draw. You will start drawing at 0, 0 and pass the canvas control's width and height so the circle fills the canvas. 4. Add the following line of code to draw the circle: .. code:: xojo g.FillOval(0, 0, width, height) 5. Click on Window1 to select it. #. Drag the TrafficLight class from the Navigator to Window1. #. Run your project to see the result. You now have a control that draws a green circle. Granted, it doesn't do a whole lot just yet but keep going. Next you need to add some code that sets the LightColor property to the next color in the series (green, yellow, red) when the user clicks on the control. For that, you need to add the MouseDown even to detect a mouse click: 1. Right-click on TrafficLight in the Navigator, choose “Add to TrafficLight” and then choose Event Handler. #. Choose the MouseDown event and click OK. This event is called when the user clicks the control. At that point you want to change the color and then redraw the circle in the canvas with the new color. #. Add the follow code to the MouseDown event: .. code:: xojo Select Case LightColor Case Color.Green LightColor = Color.Yellow Case Color.Yellow LightColor = Color.Red Case Color.Red LightColor = Color.Green End Select Refresh Just as you 'd expect, this Select Case statement looks at the color then changes it to the next color in the series. If it's green, it changes it to yellow. If it's yellow, it changes it to red and if it's red, it changes it back to green. The Refresh method at the end causes the control to be redrawn and thus fire the Paint event which will draw the circle using the color that was set in the MouseDown event. 4. Run your project to see the results. Now you have a custom control you can use as many times as you want across many different layouts. If you need to change the logic of the control, you can change it one place (the TrafficLight class). The last thing you 'll want to decide is whether or not you want to be able to add code to the MouseDown and Paint events on an instance of the TrafficLight control on any particular layout. If you do, you 'll need to add these events back just as you did with the FocusLost event of the DataEntryField control. Adding event definitions for these two events is a little trickier than adding the FocusLost event was because both the MouseDown and Paint events have parameters passed to them. That means you will need to define the same parameters as part of the event definition and pass those parameters through when you call the event definitions from the original events. 1. Add a new Event Definition to the TrafficLight class in your project. #. Name this new Event Definition MouseDown. #. Enter as the Parameters: .. code:: xojo x As Integer, y As Integer 4. Now set the Return Type to Boolean. #. Lastly, click on the MouseDown event handler and add the following line of code to the end: .. code:: xojo Return MouseDown(x, y) Now if you want to add code to the MouseDown event of a specific instance of your TrafficLight control, you can. Your code will be called after the TrafficLight control sets the color and tells the control to refresh. Do the same thing for the Paint event. 1. Add a new Event Definition to the TrafficLight class in your project. #. Name this new Event Definition: Paint. #. Enter as the Parameters: .. code:: xojo g As Graphics, areas() As Rect 1. Lastly, click on the Paint event handler and add the following line of code to the end: .. code:: xojo Paint(g, areas) 5. Run your project to see that it still works properly. You shouldn't see any change (unless of course you typed something wrong). Now you should feel comfortable being able to subclass existing controls to make your own custom versions of them as well as using the Canvas control to create completely new controls. =========== Design tips =========== When designing your applications to be cross-platform, you should keep in mind the differences in user interface across the different platforms. An application that is well designed for Windows may look drastically out of place on Mac or Linux. And vice versa. .. _/topics/user_interface/design_tips/menus: Menus ----- .. _/topics/user_interface/design_tips/the_application_menu: The Application menu ******************** Desktop applications usually have an About menu that displays an About window with the name of the application and its copyright information. On Windows and Linux, this About menu appears in a Help menu. You can add a help menu by clicking the “Add Menu” button in the Menu Editor to add a new top-level menu. Name this menu "HelpMenu" and drag it so that it is rightmost. Now you can add the About menu item to the help menu. On Mac, the About menu instead should appear in the Application menu rather than the help menu. To make it automatically move to the Application menu, you add your About menu to the Help menu so that it appears as expected for Windows and Linux. And then change its Super property in the Inspector from :doc:`DesktopMenuItem` to :doc:`DesktopApplicationMenuItem`. Any menu that uses this class will be automatically moved to the Application menu when it is run on Mac. .. _/topics/user_interface/design_tips/the_preferences_menu_item: The Preferences menu item ------------------------- Similarly, the Preferences menu is also located in the Application menu on Mac. On Windows and Linux, the Preferences menu is often located in the Edit menu and is instead called “Options”. There is always a fixed Preferences menu in the Application menu on Mac, but is is disabled by default. To attach your Preferences menu to it, you set its Super property to :doc:`DesktopPreferencesMenuItem`. Only one menu in your project should be set to DesktopPreferencesMenuItem. To change the name of the Preferences menu for Mac and Windows/Linux, you use a constant. By default there are several constants on the App class that control the text for Edit > Clear/Delete and File > Quit/Exit. You can add another to handle preferences. To do so, add a new constant and call it kPreferences, setting its default value to "&Options...". In the Constant Editor, click the “+” to add a new entry and select “macOS” as the Platform. For the Value, enter "Preferences..." This creates a constant that uses the value "&Options..." on Windows and Linux, but the value "Preferences..." on Mac. Now that you have created the constant, you can use it as the text of the menu. Add a new MenuItem to the Edit menu and set its Text property to “#App.kPreferences”. This tells it to use the value of the constant. Also set its Super to “DesktopPreferencesMenuItem”. You can use the preview buttons in the Menu Editor toolbar to see the text change between Mac, Windows and Linux. In addition, when you run the application on Mac, the Preferences menu appears in the Application menu instead of the Edit menu. .. _/topics/user_interface/design_tips/dialog_buttons: Dialog buttons -------------- Perhaps you have never noticed it, but when you use a dialog box on Windows and Linux, the buttons are in a different order than they are on Mac. On Mac, the default OK/Cancel buttons display as Cancel followed by OK. On Windows and Linux they appear as OK followed by Cancel. For your applications to look proper on each platform, the buttons should appear in the appropriate positions. The easiest way to do this is to us a :doc:`DesktopContainer` to swap the buttons for you at runtime. The example project OKCancelContainer in the Examples/Topics/User Interface/Desktop/Custom Controls folder demonstrates how to do this. * Examples/Platforms/Desktop/Custom Controls/OKCancelContainer .. _/topics/user_interface/design_tips/fonts: Fonts ----- Normally you will use the System font as the Font for your controls. This tells your application to use the default system font for each OS as the font for the control. As you might expect, the default system font varies by platform and sometimes even by updates within a platform. This means that some controls may end up being too small on some platforms to fit the text you provided. It is important that you run your project on each platform and review the layout to make sure that everything is readable and fits as you expect. You may find that you need to increase the size of some controls so that they display properly on all platforms. You can do this in the Layout Editor by increasing the size of a control. Or you can do it at runtime by increasing the size of the control in its Opening event depending on the platform being used (using Conditional Compilation). For example, this code in the Opening event handler of a DesktopButton increases its size when running on Linux: .. code:: xojo #If TargetLinux Then Me.Height = Me.Height + 20 #Endif .. _/topics/user_interface/design_tips/windows-specific_tips: Windows-specific tips --------------------- Linux and Mac use a technique called double-buffering for all window drawing. This means that updates to a window are done offscreen and then shown on the actual screen in one update. This results in stable, flicker-free windows and graphics updates. Windows, however, does not use this technique. The Xojo desktop framework attempts to minimize this for you. Below are some general tips that will help your Windows apps look their best. .. _/topics/user_interface/design_tips/do_not_overlap_controls: Do not overlap controls ----------------------- The easiest thing you ensure a rock-solid UI is to not overlap any controls. Overlapped controls result in more requests to redraw the controls which will be slower and can sometimes result in flickering. .. _/topics/user_interface/design_tips/use_a_canvas: Use a Canvas ------------ For best results, display any graphics using the Paint event of a :doc:`DesktopCanvas` control. Stay away from using the Window Backdrop property, the Window Paint event, the Canvas.Backdrop property or the :doc:`DesktopImageViewer` control. Although those techniques work fine in certain situations, in more complex window layouts a Canvas gives you more control. You can have separate methods that update the graphics, but they need to be called from the Paint event with the graphics object supplied to the methods as a parameter. Another technique is to have a Picture property that you use to draw you graphics to and then in the Paint event handler you draw the Picture to the Canvas to display it. When you want the Canvas to update itself, redrawing any changes to your graphics, you call the :ref:`Refresh` method: .. code:: xojo Canvas1.Refresh or .. code:: xojo Canvas1.Refresh(True) Passing :doc:`True` tells the Canvas to update itself immediately. Otherwise, the Canvas will update itself when it gets a redraw request from the operating system. Normally you want to use Refresh without the :doc:`True` parameter as it results in fewer draw requests, improving performance and reducing any flicker. By using these techniques, you can create a stable UI and graphics in your Windows applications. Desktop ======= .. toctree:: :maxdepth: 1 :name: sec-desktop Controls without user interface Creating dialog boxes Creating menus Custom controls Everything about desktop menus Linux macOS Supporting drag and drop The desktop control hierarchy Windows =============================== Controls without user interface =============================== These are the controls and classes in the Controllers group of the Library. The controls in this section are all non-visual. They do not display on the Window in the built apps and the user cannot see or interact with them. When added to a Layout, they appear on the Shelf area. Adding these controls to the Layout is merely a convenience to give you easy access to their event handlers. Although you can also create these controls in using the New operator you will not have access to the event handlers unless you first subclass the control (and access the event handlers there) or use the :doc:`AddHandler` command to map event handlers to methods on the window. You can also add just about any non-visual class to a layout by dragging the Object control onto the layout and changing its Super to the class you want. .. _/topics/user_interface/desktop/controls_without_user_interface/ipc_socket: IPCSocket --------- The IPCSocket performs interprocess communications between two applications running on the same computer. Use it to send and receive messages. Like other sockets, the IPC Socket control displays an icon when placed in a window in the Window Editor but has no interface. .. _/topics/user_interface/desktop/controls_without_user_interface/ipcsocket/see_also: See also ******** :doc:`IPCSocket` class .. _/topics/user_interface/desktop/controls_without_user_interface/note_player: NotePlayer ---------- Plays musical notes. Refer to :doc:`DesktopNotePlayer` for a list of available instruments. .. _/topics/user_interface/desktop/controls_without_user_interface/properties: Properties ********** Instrument: The number of the musical instrument to play. Refer to :doc:`DesktopNotePlayer` for a list of the available instruments and their values. .. _/topics/user_interface/desktop/controls_without_user_interface/methods: Methods ******* PlayNote: Plays a note using the supplied pitch and velocity. .. _/topics/user_interface/desktop/controls_without_user_interface/usage: Usage ***** This example: .. code:: xojo NotePlayer1.Instrument = 20 ' Church Organ NotePlayer1.PlayNote(60, 60) .. _/topics/user_interface/desktop/controls_without_user_interface/shell: Shell ----- Provides a quick way to run Unix or DOS shell commands. .. _/topics/user_interface/desktop/controls_without_user_interface/shell/see_also: See also ******** :doc:`Shell` class .. _/topics/user_interface/desktop/controls_without_user_interface/serialconnection: SerialConnection ---------------- Although the SerialConnection control displays an icon when placed in a window in the Window Editor, it is not visible in the built application. It is designed only for executing code to communicate via the serial port. For more information, refer to the :doc:`Communicating with Serial Devices` topic. .. _/topics/user_interface/desktop/controls_without_user_interface/serialconnection/see_also: See also ******** :doc:`SerialConnection` class, :doc:`Serial Device Tutorial` topic .. _/topics/user_interface/desktop/controls_without_user_interface/server_socket: ServerSocket ------------- The ServerSocket class enables you to support multiple TCP/IP connections on the same port. When a connection is made on that port, the ServerSocket hands the connection off to another socket, and continues listening on the same port. It includes the ability to replenish its supply of TCPSockets as connections are made. Without the ServerSocket, it is difficult to implement this functionality due to the latency between a connection coming in, being handed off, creating a new listening socket, and restarting the listening process. If you had two connections coming in at about the same time, one of the connections may be dropped because there was no listening socket available on that port. .. _/topics/user_interface/desktop/controls_without_user_interface/serversocket/see_also: See also ******** :doc:`ServerSocket` class; :doc:`Building a Server into your app` topic .. _/topics/user_interface/desktop/controls_without_user_interface/spotlight_query: SpotlightQuery -------------- Used to perform Spotlight searches on Mac. It does nothing on other operating systems. .. _/topics/user_interface/desktop/controls_without_user_interface/spotlightquery/see_also: See also ******** :doc:`SpotlightQuery` class .. _/topics/user_interface/desktop/controls_without_user_interface/tcp_socket: TCPSocket --------- Used for executing code to communicate with other computers on the Intranet or Internet using TCP/IP. .. _/topics/user_interface/desktop/controls_without_user_interface/tcpsocket/see_also: See also ******** :doc:`TCPSocket` class; :doc:`Communicating via TCP/IP` topic .. _/topics/user_interface/desktop/controls_without_user_interface/thread: Thread ------ The Thread class is used to have processing run in the background, which is very useful way to keep the user interface responsive while a process is running. .. _/topics/user_interface/desktop/controls_without_user_interface/thread/see_also: See also ******** :doc:`Thread` class; :doc:`Running code in a thread` topic .. _/topics/user_interface/desktop/controls_without_user_interface/timer: Timer ----- The Timer executes some code once or repeatedly after a period of time has passed. .. _/topics/user_interface/desktop/controls_without_user_interface/timer/see_also: See also ******** :doc:`Timer` class; :doc:`Running code periodically with a Timer` topic .. _/topics/user_interface/desktop/controls_without_user_interface/url_connection: URLConnection ------------- A URLConnections supports web communications to URLs. You can use this control to send and receive requests to web services and other web-based APIs. .. _/topics/user_interface/desktop/controls_without_user_interface/urlconnection/see_also: See also ******** :doc:`URLConnection` class; :doc:`Accessing Web Services` topic .. _/topics/user_interface/desktop/controls_without_user_interface/udp_socket: UDPSocket --------- The UDPSocket supports communications via a UDP (User Datagram Protocol) connection. UDP is the basis for most high speed, highly distributed network traffic. It is a connectionless protocol that has very low overhead, but is not as secure as TCP. Since there is no connection, you do not need to take nearly as many steps to prepare when you wish to use a UDPSocket. UDP sockets can operate in various modes, which are all very similar, but have vastly different uses. Perhaps the most common use is “multicasting”. Multicasting is a lot like a chat room: you enter the chatroom, and are able to hold conversations with everyone else in the chatroom. .. _/topics/user_interface/desktop/controls_without_user_interface/udpsocket/see_also: See also ******** :doc:`UDPSocket` class; :doc:`Communicating via UDP` topic .. _/topics/user_interface/desktop/controls_without_user_interface/object: Object ------ Use this generic object to add any class to the layout so it can work as a non-visual control, giving you easy access to all its event handlers. Drag the Object to the layout and then change its Super property to your actual class name. .. _/topics/user_interface/desktop/controls_without_user_interface/xojo_script: XojoScript ---------- The XojoScript control allows the end user to write and execute Xojo code within a compiled application. Scripts are compiled into machine code. You pass the code that you want to run via the Source property and execute it by issuing the Run method. .. _/topics/user_interface/desktop/controls_without_user_interface/see_also: .. seealso:: :doc:`IPCSocket`, :doc:`DesktopNotePlayer`, :doc:`Shell`, :doc:`SerialConnection`, :doc:`ServerSocket`, :doc:`SpotlightQuery`, :doc:`TCPSocket`, :doc:`Thread`, :doc:`Timer`, :doc:`UDPSocket`, :doc:`XojoScript` classes ===================== Creating dialog boxes ===================== There are several ways you can create dialog boxes in your apps. The easiest way is to use the MessageBox method. The MessageDialog class provides a bit more features. Lastly, you can create your own dialog with its own layout by creating a custom dialog box using a Window. .. _/topics/user_interface/desktop/creating_dialog_boxes/a_simple_message_box: A simple message box -------------------- You can display a simple message box to the user with the :doc:`MessageBox` method. This is useful for displaying a brief message to the user in the form of a modal dialog box. Pass the text that you want to display to the MessageBox method. For example, the following message could be shown when a file upload has completed successfully: .. code:: xojo MessageBox("File transfer complete!") This line of code displays a message box with the message and one button that the user can click to dismiss the dialog. .. note:: Visual Basic's MsgBox method is also supported for compatibility and is identical in syntax and functionality to :doc:`MessageBox`. .. list-table:: * - MessageBox on Windows - .. figure:: images/creating_dialog_boxes_msgboxwindows.png * - MessageBox on macOS - .. figure:: images/creating_dialog_boxes_msgboxmacos.png * - MessageBox on Linux - .. figure:: images/creating_dialog_boxes_msgboxlinux.png If the string that you pass to MessageBox contains a null character or unprintable characters, you should filter them out before passing them to MessageBox. The null character will terminate the string, no matter where it appears. Use MessageBox when you need a simple message dialog box. The :doc:`MessageDialog` class itself supports more configuration options should you need them such as additional buttons, icons, and more. .. _/topics/user_interface/desktop/creating_dialog_boxes/message_dialog: Message Dialog -------------- A Message Dialog is a much more flexible dialog that you can display to the user. By taking advantage of the :doc:`MessageDialog` class you can have more sophisticated dialog boxes than are possible with the MessageBox function. With the MessageDialog class, you can present up to three buttons and control their text and functionality. You can also present subordinate explanatory text below the main message. However, since MessageDialog is a class, you cannot accomplish all of this with a one line function call. You need to declare a variable as type MessageDialog, instantiate it, set its properties, and handle the result returned, which tells you which button the user pressed. This is a little bit more work, but the results are worth it. A MessageDialog can have up to three buttons: ActionButton, CancelButton, and AlternateActionButton, which each have these properties: .. csv-table:: :header: "Property", "Description" :widths: auto "Caption","The text displayed in the button." "Visible","Indicates if the button is displayed. Set to True to show the button." By default, only the ActionButton is shown, but you can show the others simply by setting their Visible properties to :doc:`True`. .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/creating_dialog_boxes_messagedialog_on_macos.png In addition, you can set the text of the message, the subordinate explanation, the type of icon shown in the dialog (no icon, Note, Warning, Stop, or Question), and the title. Not all of the icons are displayed on MacOS. You present the customized dialog by calling the ShowModal method of the MessageDialog class. After the user clicks a button, MessageDialog returns a :doc:`MessageDialogButton` object, which is either an ActionButton, CancelButton, or AlternateActionButton. By determining the type of object that was returned, you learn which button the user pressed. You can also examine the returned object's properties, if necessary. Here is an example: .. code:: xojo Var dialog As New MessageDialog dialog.Message = "Do you want to save changes to this document before closing?" dialog.ActionButton.Caption = "Save" dialog.CancelButton.Visible = True dialog.CancelButton.Caption = "Cancel" dialog.AlternateActionButton.Visible = True dialog.AlternateActionButton.Caption = "Don't Save" Var dialogButton As MessageDialogButton dialogButton = dialog.ShowModalWithin(Self) Select Case dialogButton Case dialog.ActionButton ' Save Case dialog.AlternateActionButton ' Don't save Case dialog.CancelButton ' Cancel End Select .. _/topics/user_interface/desktop/creating_dialog_boxes/custom_dialogs: Custom dialogs -------------- There will be times when you need a more advanced dialog box, perhaps with additional controls or a more sophisticated layout than what MessageBox and MessageDialog offer. In these situations you can use a Modal Window as a dialog box. To do this, add a Window to your project and set its Type property in the Inspector to one of these values: * Movable Modal: A modal dialog box with a title bar that the user can use to drag it around the screen. * Modal Dialog: A modal dialog box that cannot be moved around the screen by the user. * Sheet Window: Available only on MacOS, a sheet window drops down from the parent window. Because of this you do not see the title bar. On Windows and Linux, a Sheet Window behaves the same as a Movable Modal window. On macOS, it is important that you add a way for the dialog to close since there is no close button. Typically you use a button for this and in its Pressed event handler call Self.Close to close the dialog. To make a value available for the caller to check, create a public property on the dialog window and set it to the value before you close the dialog. This code (in the Pressed event handler of a button on a dialog) gets the selected text from a ListBox, assigns it to the SelectedName property and closes the dialog: .. code:: xojo If NameList.SelectedRowIndex >= 0 Then SelectedName = NameList.SelectedRowText Self.Close End If This is the code that calls the dialog window and assigns the SelectedName to a Label on the window: .. code:: xojo Var dialog As New DialogWindow dialog.ShowModal ResultLabel.Text = dialog.SelectedName By default dialogs display centered on the main screen. You can reposition a dialog by setting its Left and Top properties. This code centers a dialog above its parent window: .. code:: xojo Var dialog As New DialogWindow ' Calculate center of parent (Self) Var dLeft As Integer = Self.Left + (Self.Width - dialog.Width) / 2 Var dTop As Integer = Self.Top + (Self.Height - dialog.Height) / 2 ' Change position to new center coordinates and show the dialog dialog.Left = dLeft dialog.Top = dTop dialog.ShowModal .. _/topics/user_interface/desktop/creating_dialog_boxes/file_dialogs: File dialogs ------------ Use the built-in file dialogs to create file open and save dialogs. They are: * :doc:`OpenFileDialog` * :doc:`SaveFileDialog` * :doc:`SelectFolderDialog` For more information on how to use these, refer to the :doc:`Accessing the file system via the FolderItem Class` topic. .. _/topics/user_interface/desktop/creating_dialog_boxes/see_also: .. seealso:: :doc:`MessageBox` method, :doc:`MessageDialog` class, :doc:`DesktopWindow`, :doc:`OpenFileDialog`, :doc:`SaveFileDialog`, :doc:`SelectFolderDialog` classes ============== Creating menus ============== Most apps have a Menu Bar. The location of the menu bar varies by platform. On Mac, there is only a single menu bar and it appears at the top of the screen. On Windows, each window can have its own menu bar. Linux can work either way, depending on the distribution. When you create a desktop project, a default menu bar is added automatically, called MainMenuBar. For most applications this is usually sufficient. You create additional menu bars by adding them to your project using the Insert > Menu Bar command on the toolbar or menu. .. _/topics/user_interface/desktop/creating_menus/menu_editor: Menu Editor ----------- The :doc:`Menu Editor` is used to create your menu bars. A menu bar consists of top-level menus (called menus) and their items (called menu items). Regardless, both are subclasses of :doc:`DesktopMenuItem`. The default menu bar, MainMenuBar, has two menus: File and Edit, each with their own menu items. .. youtube:: 9sRyUQbdMBU Use the toolbar buttons to add menus and menu items. Menu items that are added to a menu have several properties that are useful. In particular, you can set the Text for the menu (this is what appears in the menu itself) and an icon for the menu. By default menu items have their AutoEnabled property set to ON. This means that the menu will automatically be enabled when the menu is clicked, if the menu handler (see below) contains code. Lastly, you can specify the keyboard shortcut for the menu. .. _/topics/user_interface/desktop/creating_menus/keyboard_shortcuts: Keyboard shortcuts ****************** You can assign keyboard shortcuts to menu items, but remember that the operating system looks for a shortcut starting from the leftmost menu. That means that if you assign the same keyboard shortcut to two different menu items, one of them won't work. There are also several specific keyboard shortcuts that are reserved for specific functions. You do not have to assign keyboard shortcuts to all menu items. In the Behavior section, specify the Key to use for the keyboard shortcut. You will also typically turn on the MenuModifier property which is :kbd:`⌘` on macOS and :kbd:`Ctrl` on Windows/Linux. AlternateMenuModifier is :kbd:`Shift` on all platforms. To to set a keyboard shortcut for :kbd:`⌘ S` (macOS) or :kbd:`Ctrl S` (Windows/Linux) you would set the Key to :kbd:`S` and turn the MenuModifier to ON. There are several other modifiers you can also use with the shortcut: * HasMacOptionKey: Use the :kbd:`⌥` key on macOS. Turning this on has no effect on Windows/Linux. * HasMacControlKey: Use the :kbd:`Ctrl` key on macOS. Turning this on has no effect on Windows/Linux, which use the Control key by default when MenuModifier is turned ON. * HasAltKey: Uses the Alternate (or :kbd:`Alt`) key on Windows/Linux. Turning this on has no effect on macOS. You can also use these special values in the Key property to set non-alphanumeric shortcut keys: F1-F15, Tab, Enter, Space, Del (Delete), Return, Bksp (Backspace), Esc, Clear, PageUp, PageDown, Left, Right, Up, Down, Help, and Ins (Insert) .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/creating_menus_menu_inspector.png You can also change the shortcut key in code by using the ShortcutKey property for the DesktopMenuItem. This example sets the HelpAbout DesktopMenuItem shortcut to be :kbd:`F5`: .. code:: xojo HelpAbout.ShortcutKey = "F5" If you wanted to set its shortcut to be :kbd:`Ctrl 1` you would do this: .. code:: xojo HelpAbout.ShortcutKey = "Ctrl-1" Refer to the :doc:`DesktopMenuItem` page for more details on how you can set up shortcut keys in code. .. _/topics/user_interface/desktop/creating_menus/accelerator_keys: Accelerator keys **************** Windows and Linux also have the concept of keyboard accelerators. In addition to the keyboard equivalent, which work on all platforms, you can also add a keyboard accelerator for each menu and menu item. When you designate a key as the keyboard accelerator, it is underlined in the menu name or menu item. The user can display the menu or invoke the menu item by holding down Alt and pressing the accelerator key. With a comprehensive system of accelerators, a user can use the menu system without using the mouse at all. To designate the accelerator key, precede the letter by an ampersand ("&") in the menu or DesktopMenuItem Text property. For example, if you are creating a menu named "Actions" and you want to make the keyboard accelerator the “A”, you would enter “&Actions” in the Text property. To make the "t" the accelerator key, enter "Ac&tions". To display an ampersand in the menu, you need to double it like this: "&&". Accelerators do not work on Mac and are not displayed, but you still need to use a double-ampersand “&&” to display a single ampersand in a menu. .. _/topics/user_interface/desktop/creating_menus/adding_a_submenu: Adding a submenu **************** Submenus are menu items that display an additional menu to their right. The menu item itself is not selectable, clicking it displays the submenu whose items can be selected. If you wish, you can continue to add a submenu to an existing submenu, creating a three-level hierarchical menu system. However, submenus can be difficult to navigate for most users. Deeply nested submenus are not a good design because they are hard for most users to navigate and make it harder to find menu items. .. _/topics/user_interface/desktop/creating_menus/adding_a_menu_item_to_the_macos_apple_and_application_menus: Adding a menu item to the macOS apple and Application menus *********************************************************** Mac apps add a new menu between the Apple menu and the standard File menu. When you create a Mac app, this menu gets the Mac App Name entered in the macOS Build Settings. Although both the Apple menu and the Application menu appear in the Menu Editor, you cannot directly add menu items to them. Instead you use these DesktopMenuItem subclasses so that the menu is moved at runtime: * :doc:`DesktopPreferencesMenuItem`: For your preferences menu item, put the menu item where you want it to appear under Windows and Linux and set its Super class to DesktopPreferencesMenuItem. A DesktopMenuItem based on the DesktopPreferencesMenuItem class will be moved automatically to the Application menu when the Mac app runs. * :doc:`DesktopApplicationMenuItem`: Similarly, use the DesktopApplicationMenuItem subclass for any menu items that should appear in the application menu on Mac. * Refer to Special Menus below for additional information. .. _/topics/user_interface/desktop/creating_menus/the_exit_(or_quit)_menu_item: The Exit (or Quit) menu item **************************** The :doc:`DesktopQuitMenuItem` class is intended only for the Quit (or Exit) menu item. When a DesktopQuitMenuItem is selected, your application quits. It also moves the menu item to the application menu for Mac apps. .. _/topics/user_interface/desktop/creating_menus/moving_menus_and_menu_items: Moving menus and menu items *************************** A menu item can be moved to a new position in the menu by dragging the menu item. You can also move menus within the menu bar. In a similar fashion, drag a menu in the menu bar and move it to the left or right. Drop the menu when it is between the desired menus. .. _/topics/user_interface/desktop/creating_menus/converting_a_menu_item_to_a_menu: Converting a menu item to a menu ******************************** To convert a Menu Item into a Menu, select the menu item and then click the Convert To Menu button in the Menu Editor toolbar. The menu item is then removed from its menu and appears in the menubar. From there you can drag it to another position in the menu bar if you wish. .. _/topics/user_interface/desktop/creating_menus/removing_menu_items: Removing menu items ******************* To remove a menu item from a menu, select it in the menu (not the Navigator) and press the Delete key or choose Edit->Delete from the menu. .. _/topics/user_interface/desktop/creating_menus/adding_a_menu_item_separator: Adding a menu item separator **************************** Menu item separators are lines that appear in between menu items to logically group items together. To add a menu item separator, simply select a menu item and click the Add Separator button in the Menu Editor toolbar. The separator will appear just below the selected menu item. If you wish, you can drag it vertically to a new location. .. _/topics/user_interface/desktop/creating_menus/default_menu_bar: Default menu bar ---------------- The default menu bar, MainMenuBar, is automatically set as the MenuBar for the App and for each new window that you create. The menu that appears for a window depends on the platform being used. Since each window on Windows has a menu bar, you must specify the MenuBar property on the window in order for a menu bar to appear. Linux is similar. On Mac, if the MenuBar property for a window is not specified, then the App.MenuBar property is used for the menu instead. .. _/topics/user_interface/desktop/creating_menus/implicit_instance: Implicit instance ***************** You may have noticed that you can refer to a MenuBar globally by its name. This is because an "implicit instance" is automatically created for you. If you use this global name, then you get the same MenuBar instance everywhere you use it. If you modify the MenuBar in code, the modification will appear everywhere the MenuBar is used. If you would rather have separate instances of the MenuBar, you should assign it in code manually in the Window.Opening event: .. code:: xojo Self.MenuBar = New MainMenuBar With Mac apps, if a window does not have a MenuBar specified, then the window uses the MenuBar specified on Application.MenuBar. With Windows and Linux, if a window does not have a MenuBar specified, then the window displays without a MenuBar even if one is specified in Application.MenuBar. .. _/topics/user_interface/desktop/creating_menus/menu_handlers: Menu Handlers ------------- When a user clicks a menu, a Menu Handler gets called. Menu Handlers are added to your window manually for the specific menu items that the window needs to handle. Click the Add button on the Window and select "Menu Handler". This adds an empty Menu Handler to the window. In the Inspector you can change the DesktopMenuItem Name (by using the ComboBox) to select the name of an existing Menu Item in the menu bar that is assigned to the window. In the menu handler itself, you write the code that should run when the menu is selected. If the menu item has its AutoEnabled property set to True (or ON), then the menu will automatically be enabled once you add code. This is how you will most often work with menus. You can also manually control when menu items are enabled. To do this, its AutoEnabled property must be False (or OFF). In this case, the menu item will always appear as disabled. In order to enable it, you have to add code to the MenuBarSelected event handler for the containing Window. For example, you might have code that enables a Save menu item only when the document has been changed: .. code:: xojo If Self.Changed Then SaveMenu.Enable End If To enable a menu item, you can call set its Enabled property to :doc:`True`. .. _/topics/user_interface/desktop/creating_menus/dynamic_menus: Dynamic menus ------------- Sometimes you may need to create a menu dynamically. Examples of this include a menu that shows recently opened files, a menu that shows the names of the currently open windows or a menu that shows the name of available fonts. To do this you create a DesktopMenuItem subclass and use the AddMenu and AddMenuAt methods of the DesktopMenuItem class to add instances of your subclass to the menu bar. Here is an example that adds a Font menu. Create a new DesktopMenuItem subclass (called FontMenuItem). In its MenuItemSelected event add this code: .. code:: xojo MessageBox("Selected Font: " + Self.Text) Reminder: To create a DesktopMenuItem subclass, create a new class and set its Super to DesktopMenuItem. Now you can use this subclass to create a font menu. In the Opening event of the default window, add this code: .. code:: xojo mFontMenu = New DesktopMenuItem("Font") MenuBar1.AddMenu(mFontMenu) Var fCount As Integer = System.FontCount Var fMenu As FontMenuItem For i As Integer = 0 To fCount - 1 fMenu = New FontMenuItem(System.FontAt(i)) mFontMenu.AddMenu(fMenu) Next This code creates a new top-level menu called "Font" and adds to it a menu item for each font that is installed on your system. When you run the application and click the Font menu, you will see a list of all the fonts. Click on a font and a dialog appears telling you the name of the one you clicked. .. _/topics/user_interface/desktop/creating_menus/menu_control_set: Menu Control Set ^^^^^^^^^^^^^^^^ Another way to create menus dynamically is to use a Menu Control Set, although the method described above (using Append) is preferred. To create a Menu Control Set, add an item to a menu and in the Inspector set its Index value to a 0 (normally it will be blank). When you add the Menu Handler for this menu item you will now see that it has a parameter: index As Integer. You can use this to tell which menu item was selected when there are multiple ones. When you create a new instance of this menu item it will add a new entry to the menu in which it is contained. For example, if you have called the menu item WindowItem then you can use this code to add another entry to the menu: .. code:: xojo Var m As New WindowItem m.Text = "Menu Text" .. _/topics/user_interface/desktop/creating_menus/creating_a_recent_items_menu: Creating a Recent Items menu **************************** You can use the above dynamic menus technique to create your own "Recent Items" menu. For example, the steps below describe how to create Recent Items menu for tracking recently opened files. Create a class called "OpenRecentMenuItem" and set its Super to DesktopMenuItem. In its MenuItemSelected event add some code to display the name of the file that was selected (it's stored in the Tag for the DesktopMenuItem): .. code:: xojo MessageBox("Recent Item: " + FolderItem(Me.Tag).NativePath) Return True In your MainMenuBar, add a menu to the File menu and call it "FileOpenRecent" and set its Text to "Open Recent" and its Submenu property to ON. With that in place, you can now add menu items to FileOpenRecent when a new file is open. As a test, you can add a button to a Window with code that lets the user select a file and then adds the file they selected to the FileOpenRecent menu: .. code:: xojo ' Choose a file Var file As FolderItem = FolderItem.ShowOpenFileDialog("") If file Is Nil Then Return ' Make one of our Open Recents subclasses and ' set it's text to being something (in this ' case, it's a number that we'll increment ' below) Var recentItem As New OpenRecentMenuItem(file.NativePath, file) ' Put the latest item at the top of ' of the open recents menu on ' the File menu. FileOpenRecent.AddMenuAt(0, recentItem) To remove an item from the FileOpenRecent menu, just remove the last one in the list like this: .. code:: xojo ' If the recent menu has items, then remove the last one If FileOpenRecent.Count > 0 Then FileOpenRecent.RemoveMenuAt(FileOpenRecent.Count - 1) End If You can find a working version of this in the Xojo examples here: Examples/Platforms/Desktop/Menus/Open Recent Menu .. _/topics/user_interface/desktop/creating_menus/contextual_menus: Contextual menus ---------------- Contextual menus are menus that appear when the user choose to see them. This is most often by right-clicking (or :kbd:`Ctrl`-clicking on Mac) somewhere, but contextual menus can also be displayed using a keyboard shortcut on Windows. All controls have two events that you use to create and handle contextual menus: ConstructContextualMenu and ContextualMenuItemSelected. In ConstructContextualMenu, you can dynamically create the contextual menu (by appending menu items to the base parameter). Return True from the event to display the contextual menu: .. code:: xojo base.AddMenu(New DesktopMenuItem("Test 1")) base.AddMenu(New DesktopMenuItem("Test 2")) base.AddMenu(New DesktopMenuItem("Test 3")) Return True In ContextualMenuItemSelected, you can test the selectedItem parameter to perform actions: .. code:: xojo If hitItem <> Nil Then MessageBox(hitItem.Text) End If Return True .. _/topics/user_interface/desktop/creating_menus/special_menus: Special menus ------------- There a few menu items that should have specific names in order for you to get automatic OS-provided functionality. .. _/topics/user_interface/desktop/creating_menus/help: Help **** If you add a Help menu, you should make sure its text property is set to just "Help" (or the localized equivalent) to allow Mac to automatically provide the Spotlight "Search" menu item that lets the user search all the menu items for specific text. .. _/topics/user_interface/desktop/creating_menus/edit: Edit **** A top-level menu with the text "Edit" (or the localized equivalent), automatically get "Start Dictation" and "Insert Special Characters..." menus added on Mac. Additionally, items in the EditMenu should also retain the names they are given by default if you want them to automatically work in TextFields, TextAreas and other controls. These names are: EditCut, EditCopy, EditPaste, EditClear, EditSelectAll, EditUndo and EditRedo. You can change their Text property, but do not change the Name property. .. _/topics/user_interface/desktop/creating_menus/desktopapplicationmenuitem: DesktopApplicationMenuItem ************************** Desktop applications usually have an About menu that displays an About window with the name of the application and its copyright information. On Windows and Linux, this About menu appears in a Help menu. You can add a help menu by clicking the “Add Menu” button in the Menu Editor to add a new top-level menu. Name this menu "HelpMenu" and drag it so that it is rightmost. Now you can add the About menu item to the help menu. On Mac, the About menu instead should appear in the Application menu rather than the help menu. To make it automatically move to the Application menu, you add your About menu to the Help menu so that it appears as expected for Windows and Linux. And then change its Super property in the Inspector from “DesktopMenuItem” to “DesktopApplicationMenuItem”. Any menu that uses this class will be automatically moved to the Application menu when it is run on macOS. .. _/topics/user_interface/desktop/creating_menus/preferencesmenuitem: PreferencesMenuItem ******************* Similarly, the Preferences menu is also located in the Application menu on Mac. On Windows and Linux, the Preferences menu is often located in the Edit menu and is instead called “Options”. There is always a fixed Preferences menu in the Application menu on Mac, but is is disabled by default. To attach your Preferences menu to it, you set its Super property to “DesktopPreferencesMenuItem”. Only one menu in your project should be set to DesktopPreferencesMenuItem. To change the name of the Preferences menu for Mac and Windows/Linux, you use a constant. By default there are several constants on the App class that control the text for Edit > Clear/Delete and File > Quit/Exit. You can add another to handle preferences. To do so, add a new constant and call it kPreferences, setting its default value to "&Options...". In the Constant Editor, click the “+” to add a new entry and select “macOS” as the Platform. For the Value, enter "Preferences..." This creates a constant that uses the value "&Options..." on Windows and Linux, but the value "Preferences..." when run on Mac. Now that you have created the constant, you can use it as the text of the menu. Add a new DesktopMenuItem to the Edit menu and set its Text property to “#App.kPreferences”. This tells it to use the text of the constant. Also set its Super to “DesktopPreferencesMenuItem”. You can use the preview buttons in the Menu Editor toolbar to see the text change between Mac, Windows and Linux. In addition, when you run the application on Mac, the Preferences menu appears in the Application menu instead of the Edit menu. .. _/topics/user_interface/desktop/creating_menus/see_also: .. seealso:: :doc:`DesktopMenuBar`, :doc:`DesktopMenuItem` classes; :doc:`Menu Editor` topic; `Using Menus with Desktop and Web Apps `_. `Using Menus with Desktop Apps `_ videos; :doc:`Everything About Desktop Menus` lesson =============== Custom controls =============== You can create your own custom controls by subclassing built-in controls, drawing them entirely using a :doc:`Canvas` or by combining controls using a :doc:`Container`. .. youtube:: ebfrSM200Ic .. _/topics/user_interface/desktop/custom_controls/subclassing_built-in_controls: Subclassing built-in controls ----------------------------- You can create your own custom controls by subclassing any of the built-in controls. For example, desktop projects do not have a “Link” control, which would be useful to open the default browser to the specified URL. To create such a control, you could start by subclassing the Label control. 1. Drag a Label from the Library to the Navigator. This adds a new control called “CustomLabel”. Change its name to “LinkLabel”. 2. Using the Add button on the Editor toolbar for LinkLabel, choose Property. Change the property name to “URL” with Type “String”. 3. Using the Add button on the Editor toolbar for LinkLabel, choose Event Handler and then select the MouseDown event. 4. Add this code: .. code:: xojo System.GotoURL(URL) 5. Now you can drag LinkLabel from the Navigator onto a Window and set the URL property in its Opening event: .. code:: xojo Me.URL = "https://www.wikipedia.org" You might also want to change its Text property to be “Wikipedia” so it is more obvious what will happen when it is clicked. Run the project and click on the LinkLabel. Your default browsers opens to the Wikipedia web site. Refer to :doc:`Subclassing Examples` for other examples of subclassing. .. _/topics/user_interface/desktop/custom_controls/inspector_behavior: Inspector Behavior ------------------ When creating custom controls, you may want to make some of its properties changeable at design time in the Inspector. Using the LinkLabel example above, it would be better if you could set the URL property in the Inspector for the control on the layout rather than having to put code in the Opening event. .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/custom_controls_inspector_behavior_for_linklabel_control.png You use the :doc:`Changing Properties With the Inspector` feature to control what displays in the Inspector. To use it, open the contextual menu for LinkLabel in the Navigator and select Inspector Behavior. This opens the Inspector Behavior dialog. In this dialog, you can control all the properties that appear in the Inspector, including any new ones that you add. If you scroll down, you will see the URL parameter. Check the box next to its name and click OK to have the property displayed in the Inspector for the control when it is on a layout. Now you can specify the URL in the Inspector and remove the Opening event from the LinkLabel. Here are the steps in general: 1. Create your custom control. 2. Select the proper Inspector Behavior entries to expose in the IDE. 3. Drag and drop the custom control onto your layout. 4. Scroll the inspector to the appropriate section (usually Behaviors) to see your exposed properties. .. _/topics/user_interface/desktop/custom_controls/other_features_of_the_inspector_behavior_dialog: Other features of the Inspector Behavior dialog *********************************************** The Inspector Behavior dialog allows you to control all aspects of what appears in the Inspector. You can: * Add or remove group headings: Use the “+” or “-” buttons below the list or use the contextual menu. * Change the order of the properties, including moving them to different groups: Drag properties around as needed. * Set or modify default values: Double-click in the Default Value column for the property to set or modify the default value. * Change whether a property is shown or hidden: Check the property to display it; uncheck it to hide it. * Add enumerations: Use the Enumerations control on the right to add values that the user can select from a list. .. _/topics/user_interface/desktop/custom_controls/setting_a_default_event: Setting a default event ----------------------- If your custom control has events, you may want to indicate one to be the default event. This is the event that will be selected when the user adds a new event to an instance of your custom control. You can indicate the default event by selected your custom control class in the Navigator then using the Property Inspector to add an attribute to your custom control class. Attributes are found on the Advanced tab of the Inspector. Set the name to "DefaultEvent" and the value to the name of the event you wish to be the default. .. _/topics/user_interface/desktop/custom_controls/using_subclassed_controls_in_your_projects: Using subclassed controls in your projects ------------------------------------------ When you've created subclassed controls, there are a couple ways you can add them to a layout. For smaller projects you may find it is fast and easy to select the destination layout project item and then drag the subclassed control from the Navigator onto the layout. You can also find subclassed controls in the Library. If you have "Group Banners" turned on for the Library you'll find any subclassed controls in the "Project Controls" section at the end. You can drag a subclassed control that appears here onto the layout the same as you can for any of the built-in controls. If you want to display a description for your own control in the Library, use the "LibraryDescription" attribute and its value text as a description in your class. If you have a subclassed control that you do not want to appear in the Library you can "hide" it by setting a special attribute on the subclassed control. Add the :ref:`HideFromLibrary attribute` to the subclassed control and it will not appear in the Library. .. _/topics/user_interface/desktop/custom_controls/property_value_assignment: Property value assignment ------------------------- When you create control subclasses, the values of public properties are set in this order: 1. They are assigned values set by the subclass itself, either in the Constructor or as defaults. 2. The above values are overridden by default values for properties specified in the Inspector Behavior window. 3. The above values are overridden by values changed in the Inspector. .. _/topics/user_interface/desktop/custom_controls/see_also: .. seealso:: :doc:`OOP Design Concepts`, :doc:`Subclassing Examples`, :doc:`Desktop Container` :doc:`Canvas`, :doc:`Changing Properties With the Inspector` topics ============================== Everything about desktop menus ============================== The Menu Bar is one of the main user interface elements in desktop apps (macOS, Windows and Linux). The user interface is in charge of displaying all the available options (Commands) normally grouped by subject or application area so the user can see and choose all the available actions that can be applied on the active item, Window or in the context of the App. In this tutorial, we will see how to create static menus with the help of the Menu Editor included in the Xojo IDE; how to react to the menu selected by the user; how to create the menus dynamically at runtime; and how we can clone and display complete menu hierarchies on any position within a Window or Control derived from the DesktopUIControl class. Read each section and watch the accompanying videos (in Spanish with English subtitles). In order to follow this tutorial, you should feel comfortable with the basics of Object Oriented Programming (OOP) and Event Driven Programming like the concepts of Classes, Subclasses or Delegates. If this is not the case, you can read about them in this Overview of OOP and/or watch these videos: Object-Oriented Programming Concepts, Intro to OOP 101 and Intro to OOP 201, Advanced OOP Concepts. .. _/topics/user_interface/desktop/everything_about_desktop_menus/menus_for_desktop_and_the_operating_systems: Menus for desktop and the operating systems ------------------------------------------- Every time we create a new Desktop Project, Xojo adds by default a new instance of a menu bar that includes all the essential items. We do this so if you decide to compile and run the app it will include the minimal required structure so you can deploy it. In fact, this is the menu bar that is assigned by default to the global object App and also to the Window added by default to the project: Window1. This menu bar has the particularity that is available as an implicit instance and globally for all the items of the App. This means that it is possible to refer to the default menu bar MainMenuBar and their items directly from any method or Event of the App. Of course, it is possible to change the menu bar name in the Inspector Panel if you want. Once the name is changed, it is automatically reflected in all the objects referencing the menu bar by default, as is the case of the App and Window1 objects. For macOS apps you don't need to manually assign the default or any other menu bar to each Window added to the project, these will use the default menu bar. For Windows and Linux apps, you have to explicitly assign the menu bar used by every Window from the Menus section of the Inspector Panel. Of course, you can add as many menu bars as you need to the project from the Insert > Menu Bar menu or using the Insert button available in the toolbar of the project window. Once you add new menu bars to the project, you can assign any one of them from the Menus section of the Inspector Panel or from code. .. youtube:: jnXG7kZ6fMQ .. _/topics/user_interface/desktop/everything_about_desktop_menus/the_menu_editor: The Menu Editor --------------- It is possible to create all the menu bar hierarchy from code, but it is usually easier to edit the project menu bars from the Menu Editor. In order to access the editor, select the menu bar you want to edit from the Project Browser. These are the main items available in the Menu Editor toolbar: .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/everything_about_desktop_menus_menu_editor_command_bar.png 1. Click on these icons to see the approximate appearance of the menu bar on any of the supported operating systems. Note that this is just an interpretation and not the real appearance they will show once you deploy the app. #. Click on this icon in order to add a new first level menu item to the menu bar. You can change any first level menu item position by dragging it to another place. The moved menu item will bring with it all the menu items under its hierarchy. #. Click on this icon to add a new menu item on the selected first level menu item. You can modify its position by dragging it to the new place. You can even drop it under other first level menu hierarchy. #. Click on this icon to add a new separator item on the selected first level menu hierarchy. As with the previous options, it is also possible to change its position. Optionally, you can convert any menu item into a separator selecting it and changing its Text property to a dash line ("-"). #. Click on this button to add a new submenu in the selected main menu. Optionally, you can convert any menu item into a submenu switching the Submenu option to the On position under the Behavior section of the Inspector Panel. In the same way, you can convert a submenu item into a regular menu item switching this option to the Off position. Note that if you already had added menu items to the submenu, they will be lost once you change it to a regular menu item. #. Click on this icon to convert the selected menu item to a first level menu. If the selected item is a submenu, then the available menu items under his hierarchy will be moved too. It is not possible to convert a first level menu with all the items under it to a submenu. #. Under Windows and Linux it is possible to assign accelerator keys to the menu items in order to execute the selected item. Note that Xojo will detect the accelerator keys from left to right, so if you use the same accelerator key by mistake in more than one menu item, it will be detected only for the leftmost menu. You can assign the accelerator key to a menu item, along other menu item properties, via code or using the associated Inspector. When the Menu Editor previews the Menu Bar for the macOS operating system, you will see that it adds the Apple menu and the My Application menu; however you can't add new menu items directly to this menu. Instead, you will have to add the menu item to the first level menu where you want it to show up for Windows and/or Linux, using then the Inspector Panel to change its associated Super class from :doc:`DesktopMenuItem` to AppMenuItem. This is what you probably will want to do in order to add the About My App… and the Preferences menu item for your macOS apps. In the case of the Preferences menu item for your app, you can assign the PrefsMenuItem subclass so it looks active by default and with the proper keyboard shortcut already assigned on macOS. If you want to check all the available :doc:`DesktopMenuItem` subclasses you can choose from, click on the pen icon associated with the Super field on the Inspector. This picture shows the resulting window: .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/everything_about_desktop_menus_menuitem_subclasses.png .. youtube:: kDJfKwpkMp4 .. _/topics/user_interface/desktop/everything_about_desktop_menus/menu_items_properties: Menu item properties -------------------- When designing the menu bar from the Menu Editor, you can set several of the available :doc:`DesktopMenuItem` properties using the Inspector associated with every item. In fact, you'll probably find it easier to set some of the most relevant properties using the Inspector, as is the case of setting the use of the alternate key for the keyboard shortcut, the Control key or the modifier key. In addition, it is only possible to assign an accelerator key to the menu item for Windows and/or Linux from the Menu Editor using the Inspector, and not from code. Specifically, these are the available read only properties when accessed from code:. * **AlternateMenuModifier**. Boolean value for the use of the Shift key under all the supported operating systems. * **MacControlKey**. Boolean value for the use of the Control :kbd:`Ctrl` key under macOS. * **MacOptionKey**. Boolean value for the use of the Option key :kbd:`Alt` under macOS. * **PCAltKey**. Boolean value for the use of the :kbd:`Alt` key on Windows and Linux keyboards. * **MenuModifier**. Boolean value for the use of the Command :kbd:`⌘` key under macOS, and Control :kbd:`Ctrl` on Windows and Linux. While these are read-only properties when accesed from code, it is still possible to assign the special keys for all the possible keyboard variations when setting the keyboard shortcut for a menu item. These are the values you can use followed with a dash line ("-") and the key for the shortcut. You can combine then in any order: * :kbd:`⌘` Command key on macOS. * :kbd:`⌘` Option key on macOS and :kbd:`ALT` on Windows and Linux. * :kbd:`⇧` Shift key on Windows, macOS and Linux. * :kbd:`Ctrl` Control key for macOS, Windows and Linux. For example: .. code:: xojo MyMenuItem.KeyboardShortcut = "cmd-shift-ctrl-alt-U" Will display the same as if it would be set as: .. code:: xojo MyMenuItem.KeyboardShortcut = "ctrl-shift-cmd-alt-U" The following are other interesting properties available under the Inspector (also of interest when creating menu items from code), although you will probably want to use their default values: * **Visible**. Available under the Appearance section of the Menu Editor for all the menu items. The menu item will be visible when set to On or using the True value from code. * **AutoEnabled**. Available under the Behavior section for all the menu items excepting the first level menus. This has the On value (or True value, from code) by default for all the new menu items created on the fly from code. This allows that the menu item that shows up as active under the menu always it has its handler menu implemented (or a delegate method associated to his Action event, something we will see a bit later). If we use the Off value (or the False value from code), then we will have to implement the EnableMenuItems Event Handler in the window associated with the menu bar or in the App object, and decide here if the menu item will be displayed as active or not. The app will execute the code available in this event handler every time the user clicks on the menu bar, or when using any of the assigned keyboard shortcuts. For example, we would be interested in making some menu items active only when some conditions are meet, for example, if there is an item or text selected. A typical case for this would be the Copy, Cut and Paste menu items. .. _/topics/user_interface/desktop/everything_about_desktop_menus/setting_special_keyboard_shortcuts: Setting special keyboard shortcuts ---------------------------------- Setting a regular keyboard shortcut for your menu items is really straightforward; but, what if you want to use some of the special keys or symbols as the "delete" key or the "right arrow" key? Xojo has considered these cases too, so you only have to set the following values to the Key property on the Inspector Panel or the KeyboardShortcut property when creating new menu items on the fly from code: * **F1-F15**. Function key for that range. For example: :kbd:`F1`. * **Tab**. Graphical representation of the Tab key. * **Enter**. Graphical representation of the Enter key. * **Space**. Textual representation for the Space bar key. * **Del**. Graphical representation for the Delete key. * **Return**. Graphical representation for the Return key. * **Bksp**. Graphical representation for the Backspace key. * **Esc**. Graphical representation for the Escape key. * **Clear**. Graphical representation for the Clear key. * **PageUp**. Graphical representation for the Page Up key. * **PageDown**. Graphical representation for the Page Down key. * **Left**. Graphical representation for the Left Cursor key. * **Right**. Graphical representation for the Right Cursor key. * **Up**. Graphical representation for the Up Cursor key. * **Down**. Graphical representation for the Down Cursor key. * **Help**. Graphical representation for Help (closing question mark). * **Ins**. Insert. For example, the following line will set the Command + Help keyboard shortcut to a menu item: .. code:: xojo MyMenuItem.KeyboardShortcut = "cmd-help" .. _/topics/user_interface/desktop/everything_about_desktop_menus/setting_icons_to_menu_items: Setting icons to menu items --------------------------- In addition to the text, keyboard shortcuts and accelerator keys, it is also possible to assign an icon that helps to identify the main purpose of the function or command to every menu item of the menu bar. You can use any of the pictures added to the project for that, and set the picture to the menu item through the Icon option of the Inspector Panel for the selected menu item, or using the Icon property from code. The only thing you need to be aware of is that Xojo will not automatically resize the assigned picture to an acceptable size for his use in combination with the menu item, so you should use a Graphical Editor to generate the pictures you intend to use in your menus with the following recommended sizes: * **16 x 16 pixels**. 1x size. * **32 x 32 pixels**. 2x size in HiDPI mode. * **48 x 48 pixels**. 3x size in HiDPI mode. In addition, some of the current operating systems guidelines for their UI propose the use of monochromatic icons for this purpose, but you are free to use pictures of any supported color depth. It is also preferable to use PNG images with transparent background. .. youtube:: MYo5Ii5J24M .. _/topics/user_interface/desktop/everything_about_desktop_menus/localizing_the_menu_items: Localizing the menu items ------------------------- Of course you can use localized texts for the menu items. For that, remember to set all the text constants under a Module added to the project, enabling them as Dynamic constants, and to assign the localized text values for every supported language. Then, you just have to use the #moduleName.ConstantName syntax under the Text field of the Inspector Panel for every menu item you want to localize. For example, if our project would have a LocalizedValues module with a dynamic constant in it with the name kPreferences, then we would be able to use the localized value in the Text value of a :doc:`DesktopMenuItem` using the following syntax: .. code:: xojo #LocalizedValues.kPreferences While if you want to achieve the same from code, then you should use this: .. code:: xojo MyMenuItem.Text = LocalizedValues.kPreferences .. youtube:: tlcfGLQUavA .. _/topics/user_interface/desktop/everything_about_desktop_menus/how_to_set_the_action_for_static_menus: How to set the action for static menus -------------------------------------- If you run your app after designing the menu bars and set them to your project window, then you will see that all the menu options will be grayed or inactive, even with their AutoEnabled property set to On. This is because you still need to set the action to execute for every one of the menu items in first place. To do this: * Select the window that needs to handle the menu action or the App object from the Project Browser and add a Menu Handler to it from the Insert menu. * As result of the previous action, you will see a new Menu Handlers section in the Project Browser, showing the Code Editor associated with the new menu handler. * Select the menu item you want to link with the code execution for the Menu Handler from the popup menu of the Inspector Panel. Or start writing the name of the menu item in the :doc:`DesktopMenuItem` Name field, and use the autocomplete feature to select the desired menu item. In addition to the window objects and the global App object, you can also add Menu Handlers to the DesktopContainer instances added to the project. In this case, you have to enter the exact name of the menu item in the Inspector Panel after adding the Menu Handler. .. youtube:: 0lTjY19G4EQ .. _/topics/user_interface/desktop/everything_about_desktop_menus/action_response_hierarchy_for_menus: Action response hierarchy for menus ----------------------------------- Since the windows of the project and the App object can use the same menu bars, implementing menu handlers for the same menu items in every one of these (and it is even possible that the DesktopContainers used in the Windows layout can also react to the selection of a menu item), which one is in charge of executing the code of the menu handler? This is the order that will follow the event after a menu item selection: * If the frontmost Window has a DesktopContainer that implements a Menu Handler for the selected menu, then this will be the executed code. In order to stop the event propagation, the menu handler has to return the value `True`; otherwise, the event will be propagated to the containing window. * If the frontmost Window has a Menu Handler for the selected menu item, then this will be the executed code. If not, the App object will receive the event and if it has a Menu Handler for that menu item then it will execute the code. * If there is not a DesktopContainer or Window that can catch the event of the selected menu item, then the App will execute the menu handler for the menu item, in case it has it implemented. As you can see, depending where we decide to implement the menu handler, or if we decide to stop or not the event propagation, we can execute different actions in response of the same menu item selection from the menu. For example, it is a good idea to add to the App object the menu handlers for menu items that should be available along all the app execution, no matter which window is the active window or the user interaction. .. _/topics/user_interface/desktop/everything_about_desktop_menus/creating_menu_items_from_code: Creating menu items from code ----------------------------- So far, we have seen how to add and set static menus with the help of the Menu Editor and the Menu Handlers. That is, implicit instances of the menu items with static code. However, you may need to create, display (and delete) menu items on the fly. Some practical cases for this are when you don't know, for example, the amount or type of available items to show under a menu; or the options you want to make available to the user depending a type of license. For these and other use cases, the Xojo framework provides all we need to create new menu items and even to create complete menu bars from scratch that you can assign to the app windows on the fly! All the menu items are created by default from the :doc:`DesktopMenuItem` class; so the following code in the Opening event of the App object will add a new menu item named MyMenu, displaying the text "My Menu Option" and reacting to the :kbd:`Command U` or :kbd:`⌘ U` (macOS) keyboard shortcut: .. code:: xojo Var newMenuItem As New DesktopMenuItem newMenuItem.Name = "MyMenu" newMenuItem.Text = "My Menu Option" newMenuItem.KeyboardShortcut = "cmd-U" We can also use the Class Constructor omitting the name assignation to the new instance, although you probably will want to add a name to every one of your :doc:`DesktopMenuItem` instances, more on this later. The following code shows how to use the :doc:`DesktopMenuItem` constructor: .. code:: xojo Var newMenuItem As New DesktopMenuItem("My Menu Option") newMenuItem.kKeyBoardShortcut = "cmd-u" However, creating a new menu item instance doesn't imply that it will be displayed on a menu bar as a first level menu or under the hierarchy of any of the already available menu items. You still need to add it in code. For example, if we use the default menu bar added with every new Desktop Xojo project, the following line of code will add the created menu item as a first level entry in the menu's rightmost position: .. code:: xojo Me.MenuBar.AddMenu(newMenuItem) We also can set the exact position we want to display our menu item using the `AddMenuAt` method, and passing as its first parameter the 0 based index value. For example, we can use the following line of code to display our menu item as the rightmost entry in the menu bar: .. code:: xojo Me.MenuBar.AddMenuAt(0, newMenuItem) If you run both examples on macOS you will notice that it doesn't display the set keyboard shortcut, which makes sense. Under Windows, the first level menu items of the menu bar will display their keyboard shortcut in case they have one assigned. In fact, they will react to the keyboard shortcut always they have the corresponding Menu Handler. Now try this code in both macOS and Windows and/or Linux: .. code:: xojo Var newMenuItem As New DesktopMenuItem("My Menu Option") Me.MenuBar.AddMenu(newMenuItem) Me.MenuBar.AddMenuAt(0, newMenuItem) When this code is executed on macOS the app will exit unexpectedly, displaying the following error message: .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/everything_about_desktop_menus_error_menuitem.png This is because macOS apps can't use the same menu item several times. Under Windows and Linux this is not the case and the previous code will run without problem. If you need to use the same menu item several times for macOS, Windows and/or Linux deployments, then the best solution is to use the Clone method instead. As you can expect, the Clone method clones the item menu that invokes it and this is really powerful in order to clone complete menu hierarchies from first level menus or submenus, but not for menu bars! For example, the following code will fix the previous problem; and will work just fine under macOS, Windows and Linux: .. code:: xojo Var newMenuItem As New DesktopMenuItem("My Menu Option") Me.MenuBar.AddMenu(newMenuItem) Me.MenuBar.AddMenuAt(0, newMenuItem.clone) .. youtube:: YRYFXjGKW_o .. _/topics/user_interface/desktop/everything_about_desktop_menus/how_to_create_submenus_from_code: How to create submenus from code -------------------------------- Until now we have limited ourselves to add new menu items as first level entries to the menu bar but how can you add new menu items under first level entries or in order to create a submenu? For this, the :doc:`DesktopMenuItem` class provides two methods, 1) we can get it using its object name (and not the menu item displayed text), or 2) using the zero based index as the position that such menu item has in the hierarchy it belongs to. However, if we want to add new menu items to other menu items created using the Menu Editor, then it is even easier! In this case we can use the menu item names from our code (remember, they are global objects). For example, if we use the default menu bar added to the project, then we know that we have a menu named FileMenu (you can check the menu item names in the Project Browser). We can add a new menu item "Open…" from code using this code: .. code:: xojo Var openMenu As New DesktopMenuItem("Open…") openMenu.name = "openMenu" FileMenu.AddMenu(openMenu) In addition, we also can use the before mentioned methods. These will be especially useful when creating dynamic menus from code: .. code:: xojo Var openMenu As New DesktopMenuItem("Open…") openMenu.name = "openMenu" Var sourceMenu As DesktopMenuItem = Me.MenuBar.Child("FileMenu") ' We get the DesktopMenuItem reference whose name is "FileMenu" If sourceMenu <> Nil Then sourceMenu.AddMenu(openMenu) Use the Index method for cases where you prefer to use this approximation because the menu item you want to refer to has no object name or we don't know its name in advance. For example, the menu "File" has the index zero in the default menu bar because this is the one placed in the leftmost position: .. code:: xojo Var openMenu As New DesktopMenuItem("Open…") openMenu.name = "openMenu" Var sourceMenu As DesktopMenuItem = Me.MenuBar.MenuAt(0) ' We get a reference to the DesktopMenuItem whose index is 0. If sourceMenu <> Nil Then sourceMenu.AddMenu(openMenu) As you can deduce, we can create submenus using simply the methods Append or Insert on other menu items added (or that we will add) to first level menu item entries. For example, this code will add two new entries to our "Open" menu (write this code in the Opening event of the App object): .. code:: xojo Var openMenu As New DesktopMenuItem("Open") openMenu.Name = "OpenMenu" Var firstSubmenuItem As New DesktopMenuItem("Last File") firstSubmenuItem.Name = "firstOpenSubmenuItem" Var secondSubmenuItem As New DesktopMenuItem("Select File…") secondSUbmenuItem.Name = "secondOpenSubmenuItem" openMenu.AddMenu(firstSubmenuItem) openMenu.AddMenu(secondSubmenuItem) FileMenu.AddMenuAt(0, openMenu) .. youtube:: Tq8AT8w80M4 .. _/topics/user_interface/desktop/everything_about_desktop_menus/how_to_set_the_action_to_menus_created_from_code: How to set the action to menus created from code ------------------------------------------------ We have already seen how easy it is to set the Menu Event to the statically created menu items; but, how can we assign the action that should be executed by a menu item created from code? We have two options for that, and probably the fastest and most versatile is using the AddHandler command. This command allows us to assign the Event execution to the Delegated Method of our choice. The designated method has to provide the same amount and type of parameters that is expected by the original Event, where the first additional parameter will be the one representing its own type of the Sender object (the one whose MenuBarSelected event we are substituting, :doc:`DesktopMenuItem` in this case). The designated method also has to return the same type as the original Event, if this is the case (Boolean for the :doc:`DesktopMenuItem` MenuBarSelected). In order to see how this works, add a new method to the App object using the following signature: * Method Name: ActionMethod * Parameters: Sender as DesktopMenuItem * Return Type: Boolean * Scope: Public Next, write the following code in the associated Code Editor for the Method: .. code:: xojo MessageBox("Option Selected: " + Sender.Text) Return True Let's modify our previous example code to assign the action executed by every submenu entry: .. code:: xojo Var openMenu As New DesktopMenuItem("Open") openMenu.Name = "OpenMenu" Var firstSubmenuItem As New DesktopMenuItem("Last File") firstSubmenuItem.Name = "firstOpenSubmenuItem" Var secondSubmenuItem As New DesktopMenuItem("Select File…") secondSUbmenuItem.Name = "secondOpenSubmenuItem" AddHandler firstSubmenuItem.MenuBarSelected, AddressOf ActionMethod ' We assign the Delegate Method as the Action to execute AddHandler secondSubmenuItem.MenuBarSelected, AddressOf ActionMethod ' We assign the Delegate Method as the Action to execute openMenu.AddMenu(firstSubmenuItem) openMenu.AddMenu(secondSubmenuItem) FileMenu.AddMenuAt(0, openMenu) Run the example app. You'll notice that now the submenu items are active because they have their Action set. Try to select these menu items in order to see how it executed the code from the Delegate Method. In addition, it is possible to assign the same delegated method as the Action for several menu items. The second option we have is to create our own subclasses based on :doc:`DesktopMenuItem` and implement the MenuBarSelected Event on these with the code they have to execute. Then, we just need to create the new menu item instances from our subclasses. On one hand, the main advantage of this approach is the reuse and encapsulation it provides, while in the other hand it is less flexible and versatile when compared with the AddHandler approach. Let's see the subclass approach in action applied to our example. First, add a new Class to the project and use the following data in the Inspector Panel for the added class: * Name: MySubMenuItem * Super: DesktopMenuItem When done, you will notice in the Project Browser that the icon displayed for our new subclass changes to show the one associated with the :doc:`DesktopMenuItem` instances. With our new subclass still selected, access the contextual menu and select the Add to "MySubMenuItem" > Event Handler… option. Choose the MenuBarSelected entry in the resulting window and write the following code in the associated Code Editor: .. code:: xojo MessageBox("Option Selected: " + Me.Text) Return True Return now to the Opening event of the App object and modify the code so it makes use of the new subclass: .. code:: xojo Var openMenu As New DesktopMenuItem("Open") openMenu.Name = "OpenMenu" Var firstSubmenuItem As New MySubMenuItem("Last File") ' We create the instance from our DesktopMenuItem subclass, using the inherited Constructor firstSubmenuItem.Name = "firstOpenSubmenuItem" Var secondSubmenuItem As New MySubMenuItem("Select File…") ' We create the instance from our DesktopMenuItem subclass, using the inherited Constructor secondSUbmenuItem.Name = "secondOpenSubmenuItem" openMenu.AddMenu(firstSubmenuItem) openMenu.AddMenu(secondSubmenuItem) FileMenu.AddMenuAt(0, openMenu) Run the example app again and you will notice that the submenu entries will run their events, providing the same result. .. youtube:: 4NhbhQwghHQ .. _/topics/user_interface/desktop/everything_about_desktop_menus/how_to_hide_and_destroy_menu_items:_visible,_remove_and_close: How to hide and destroy menu items: visible, remove and close ------------------------------------------------------------- Now you know how to create and add static and dynamically created menu items, even how to provide the code executed in response to user selection; but, how do you hide or destroy menu items displayed in the menu bar? First, let's differentiate between not seeing and removing a menu item from a menu. The Visible property determines exactly that: if the affected menu item will be visible or not as part of the menu hierarchy; in this case the object still will be available for use (and reference) during the app execution. That is, we still will be able to reference a menu item whose Visible property has been set to False. On the other hand, when we remove a menu item from a menu hierarchy, we will be destroying the object from memory, so we will not be able to reference it in the future. If we need the functionality provided by a removed menu item, then we will need to create a new instance from scratch. The :doc:`DesktopMenuItem` class (and, thus, all the subclasses derived from it) offers two methods (with variants) we can use to remove (destroy) menu items: Remove and Close. The first method is available in two variants: the first variant accepts as parameter an Integer value representing the zero based index for the menu item we want to remove; while the second version of the method expects to receive as its parameter a reference to the menu item to remove. For example, these two pieces of code will provide the same result, removing our secondSubmenuItem from the menu hierarchy. In order to make this a bit more interesting, add a new button to the example project window and write the following code into its Action Event: .. code:: xojo Var sourceMenu As DesktopMenuItem = FileMenu.Child("openMenu") ' We get a reference to the DesktopMenuItem whose name is "openMenu", including its menu hierarchy If sourceMenu <> Nil Then sourceMenu.RemoveMenuAt(1) ' We remove the DesktopMenuItem placed in the second position of the submenu hierarchy If you don't want to deal with index values, you can use the second version of the Remove method: .. code:: xojo Var sourceMenu As DesktopMenuItem = FileMenu.Child("openMenu") ' We get a reference to the DesktopMenuItem whose name is "openMenu", including its menu hierarchy If sourceMenu <> Nil Then sourceMenu.RemoveMenuAt( sourceMenu.Child("secondOpenSubmenuItem") ) ' We remove the DesktopMenuItem whose object name matches the provided as parameter In addition, we also can use the Close method. For example, this will have the same effect in our submenu: .. code:: xojo Var sourceMenu As :doc:`DesktopMenuItem` = FileMenu.Child("openMenu") ' We get a reference to the DesktopMenuItem whose name is "openMenu", including its menu hierarchy If sourceMenu <> Nil Then sourceMenu.Child("secondOpenSubmenuItem").Close ' We Close (or remove) the DesktopMenuItem whose object name matches the provided as parameter. .. _/topics/user_interface/desktop/everything_about_desktop_menus/display_menus_anywhere!: Display menus anywhere! ----------------------- The :doc:`DesktopMenuItem` class provides a really powerful method: PopUp. This method allows us to display any :doc:`DesktopMenuItem`, including complete menu or submenu hierarchies, in any position inside a DesktopUIControl object, providing for that the `X` and `Y` coordinates as pixels values. When combined with the Clone method we can implement menus for practically any control under any circumstance. In addition, the PopUp method will return as result the :doc:`DesktopMenuItem` entry selected by the user, if any. With the following example we will put this feature in action, displaying our OpenMenu every time we click on the app window. So, let's start adding the MouseDown event to the default Window1 object of the App. Next, write the following code in the associated Code Editor: .. code:: xojo Var menu As DesktopMenuItem = FileMenu.Child("OpenMenu") ' We get a reference to the menu object and all its hierarchy If menu <> Nil Then Var selection As DesktopMenuItem = menu.Clone.PopUp(x, y) ' We clone the menu and display it in the same coordinates where the user clicked If selection <> Nil Then MessageBox(selection.Value) ' We verify if the user has selected a menu item option, displaying its text if is a valid object End If Return True .. youtube:: iD2HCB2zWgg .. _/topics/user_interface/desktop/everything_about_desktop_menus/enabling_and_disabling_menu_items: Enabling and disabling menu items --------------------------------- As we have seen, the :doc:`DesktopMenuItem` class uses the AutoEnabled property with the True value by default, so the instances will be displayed as selectable they always have an associated Menu Handler or Delegated Method. However, you will probably want to decide on the fly which menu items should be active based on several conditions and variables. This is something we can do implementing the MenuBarSelected Event Handler in any of the project Windows that should decide about the menu items of their associated menu bars, the App object and, of course, the DesktopContainers. In order to see this feature in action, let's add our own feature to copy text that will be enabled only if there is text to copy in a text field added to the default window of the project. First, we will add our own "Copy Text" menu item under the Edit menu that is available in the default menu bar of the project. Write this code in the Opening event of the Window1 object: .. code:: xojo Var MyCopyTextVersion As DesktopMenuItem = New DesktopMenuItem("Copy Text") MyCopyTextVersion.Name = "MyCopyTextMenuItem" MyCopyTextVersion.KeyboardShortcut = "Cmd-Shift-C" AddHandler MyCopyTextVersion.MenuBarSelected, AddressOf ActionMethod EditMenu.AddMenu(MyCopyTextVersion) Of course, we need to add the ActionMethod delegated method we already used in a previous example, but for the Window1 object in this case. Here is where we will execute the action. Create the ActionMethod using this signature: * Method Name: ActionMethod * Parameters: Sender as DesktopMenuItem * Return Type: Boolean * Scope: Public And write the following code in the associated Code Editor: .. code:: xojo Var cb As New ClipBoard cb.Text = TextField1.Text cb.Close Return True Add now a new DesktopTextField control to the Window1 layout. Next, add the MenuBarSelected Event to the Window1 window and type the following code in the resulting Code Editor: .. code:: xojo Var sourceMenu As DesktopMenuItem = EditMenu.Child("MyCopyTextMenuItem") ' We get a reference to our DesktopMenuItem using its name. If sourceMenu <> Nil Then sourceMenu.Enabled = TextField1.Text <> "" ' We enable the menu item only if there is text in the TextField. Run the app and you will notice that menu option is always disabled when the text field is empty, and enabled when you put some text in the text field. When we select the menu item (or use the associated keyboard shortcut), then the textfield text will be copied to the clipboard, so you can paste it into any app that accepts text. Add now the MenuBarSelected Event Handler to the App object and type this code: .. code:: xojo EditMenu.Child("MyCopyTextMenuItem").Enabled = True Run the app again and now you will notice that the menu item is always active no matter if the textfield has text or is empty. This is because the event chain responsibility executes the handler on the frontmost active window (if implemented) first, and then the one available in the App object (if implemented). Thus, double check for this when dealing with the EnableMenuItems handlers for your menu items! .. youtube:: 98Hh7NCABRo .. seealso:: :doc:`DesktopMenuItem` class Linux ===== .. toctree:: :maxdepth: 1 :name: sec-linux GTK3 Themes =========== GTK3 Themes =========== Starting with Xojo 2017r2, Linux apps now use GTK3. Since Xojo uses native controls that means your app's controls will use the theme of the Linux distribution the app runs on. This can sometimes mean that your app's UI will not look exactly like what you designed in the Layout Editor because a theme may dramatically change control sizes and padding. This is a problem that can occur with any modern GTK3 app. If this is a problem for your apps, there are a couple ways you can work around it: * Use Fixed/Locked Control Padding * Have Your App Load and Use a Specific Theme Both of these solutions can be handled by Declares that you put in your App.Opening event handler. .. _/topics/user_interface/desktop/linux/gtk3_themes/use_fixed/locked_control_padding: Use fixed/locked control padding -------------------------------- For many apps, the primary concern is to not have controls change their size or position depending on the theme being used. You can ensure specific styling for controls in your apps by using a static amount of padding that won't change regardless of the user's theme selection. This has the benefit of letting your app use other aspects of the theme, such as colors, while maintaining control layout better. You can put this code in the App.Opening event handler to set a specific padding: .. code:: xojo #If TargetLinux Then Declare Function gdk_screen_get_default Lib "libgdk-3" () As Ptr Declare Function gtk_css_provider_new Lib "libgtk-3" () As Ptr Declare Function gtk_css_provider_load_from_data Lib "libgtk-3" _ (provider As Ptr, data As CString, dataLen As Integer, error As Ptr) As Boolean Declare Sub gtk_style_context_add_provider_for_screen Lib "libgtk-3" _ (screen As Ptr, provider As Ptr, priority As UInt32) Declare Sub g_object_unref Lib "libgobject-2.0" (obj As Ptr) Const GTK_STYLE_PROVIDER_PRIORITY_APPLICATION = 600 Var screen As Ptr = gdk_screen_get_default Var provider As Ptr = gtk_css_provider_new If provider = Nil Then Break Var data As String = "GtkWidget:not(GtkMenuItem) { padding-left: 1; padding-right: 1; padding-top: 1; padding-bottom: 1;}" If gtk_css_provider_load_from_data(provider, data, Data.Len, Nil) Then gtk_style_context_add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION) End If g_object_unref(provider) #Endif .. _/topics/user_interface/desktop/linux/gtk3_themes/have_your_app_load_and_use_a_specific_theme: Have your app load and use a specific theme ------------------------------------------- If you would like your app to use a specific theme so that your layout works exactly as you want it, you can bundle a theme and load it when your app starts. Your app will use this loaded theme regardless of what the user has selected as a theme for the distribution. You load the theme from the App.Opening event handler like this: .. code:: xojo #If TargetLinux Then Declare Function gdk_screen_get_default Lib "libgdk-3" () As Ptr Declare Function gtk_css_provider_new Lib "libgtk-3" () As Ptr Declare Function gtk_css_provider_load_from_path Lib "libgtk-3" _ (provider As Ptr, path As CString, error As Ptr) As Boolean Declare Sub gtk_style_context_add_provider_for_screen Lib "libgtk-3" _ (screen As Ptr, provider As Ptr, priority As UInt32) Declare Sub g_object_unref Lib "libgobject-2.0" (obj As Ptr) Const GTK_STYLE_PROVIDER_PRIORITY_APPLICATION = 600 Var screen As Ptr = gdk_screen_get_default Var provider As Ptr = gtk_css_provider_new If provider = Nil Then Break ' Load the theme Var themePath As FolderItem = App.ExecutableFile.Parent.Parent.Child("mintxtheme").Child("gtk.css") If gtk_css_provider_load_from_path(provider, themePath.NativePath, Nil) Then gtk_style_context_add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION) End If g_object_unref(provider) #Endif In the above code the "mintxtheme" that is used is loaded from a folder that is placed alongside the app's folder, but you can choose to load it from anywhere and may want to bundle it in your app's Resouces folder. You can download Mint themes from here: `Mint Themes Download `_ .. _/topics/user_interface/desktop/linux/gtk3_themes/analyzing_theme_properties: Analyzing theme properties -------------------------- The above code should be sufficient for most people's apps. But if you require even more control of themes and how they render widgets you will have to dig into Linux more. The GTK+ Inspector can be a great tool for this as it lets you see property and widget information in real time. `The GTK+ Inspector `_ On some distributions you may need to install the "libgtk-3-dev" package before GTK+ Inspector can be enabled. You may also find it helpful to review the `GtkCssProvider documentation `_. .. _/topics/user_interface/desktop/linux/gtk3_themes/modgtk3: modGtk3 ------- The Xojo community has created a project (`download modGTK3 `_) that contains all the required Declares and Styles for theming. If you intend to use it in your own projects, have a look at where the global theme is set, and where/how the Control tweaks are set: * App.Opening -> ``modGTK3.InitGlobalGTK3Style`` * Control.Opening -> ``Me.InitGTK3Control`` .. Note:: modGTK3 extends InitGTK3Control for Controls. In this example project, it's called from Control Subclasses. Subclassing your Controls is highly recommended anyway, as you get a single place for tweaks and workarounds for different XojoVersions or BuildTargets. Refer to the `original Xojo forum conversation `_ for more information. macOS ===== .. toctree:: :maxdepth: 1 :name: sec-macos User interface considerations for macOS ======================================= User interface considerations for macOS ======================================= Cocoa is the current user interface framework supported by Macs. If you are moving an older project from Carbon (an older Mac UI framework) to Xojo, your existing applications should just work for the most part, but there are some differences to consider. .. _/topics/user_interface/desktop/macos/user_interface_considerations_for_macos/general_ui_differences: General UI differences ---------------------- * Resizable windows can be resized from any window edge. * Spellchecking and grammar checking are now available in TextField and TextArea controls by setting AutomaticallyCheckSpelling property to ON in the Inspector. * Buttons have a ButtonStyle property than can be used to change the look of the button. * Windows in your apps can be dragged around the screen while code is running. * Timers, threads and sockets continue to get events when menus or OS modal dialog boxes are displayed. * StyledText draws much faster. .. _/topics/user_interface/desktop/macos/user_interface_considerations_for_macos/graphics: `Graphics` --------- All drawing using a Canvas must be done from the Paint event or a method called from it. If you have older projects that use the `Graphics` property of a Canvas directly, you will have to update them to use the `Graphics` object from the :ref:`DesktopCanvas` event instead. All drawing now takes place in a generic colorspace that is independent of the current output device. This means that colors will render more similarly between screens and printers. .. _/topics/user_interface/desktop/macos/user_interface_considerations_for_macos/threads: Threads ------- You cannot have any code that is in a thread that modifies anything related to the user interface (which runs on what is called the main thread). If your app does this it will raise a :doc:`ThreadAccessingUIException` exception. If your user interface needs to be updated based on information in a thread, you should instead have the UI request the information from the thread. One way to do this is to use the :ref:`Thread` method that allows you to periodically updates the UI based on information passed to it. .. _/topics/user_interface/desktop/macos/user_interface_considerations_for_macos/menuitems: MenuItems --------- Mac apps do not allow the same :doc:`DesktopMenuItem` to be used in two different places. To work around this, use the :ref:`DesktopMenuItem` method to create a copy of the MenuItem instead. .. _/topics/user_interface/desktop/macos/user_interface_considerations_for_macos/font_display: Font display ------------ Mac apps can only display font styles that are available. You cannot force a font to display in bold or italic if it does not have bold or italic variations available. In this situation, the Bold or Italic properties will not affect the font. To check if the font you wish to use has the variation you need, use the system Font Book app in the Applications folder. Prior to 2018r4, displaying of digits used a non-proportional font even though the system font was always proportional. Apple changed this behavior starting with the macOS 10.11 SDK, which Xojo started using in 2018r4. So if you require a non-proportional font for displaying digits you will need to change your font accordingly. .. _/topics/user_interface/desktop/macos/user_interface_considerations_for_macos/keyboard_handling: Keyboard handling ----------------- Unhandled KeyDown event handlers cause a Beep instead of behaving silently. This is intentional and gives the user feedback that their action did not do anything. To eliminate the Beep, Return True from the KeyDown event handler. Additionally, the KeyDown event handler literally means “a key was pressed” and does not attempt to interpret the key event for dead key sequences or input methods. For example, pressing Option+E and then E again results in two key presses sent to KeyDown, not a single key press with the value “é”. .. _/topics/user_interface/desktop/macos/user_interface_considerations_for_macos/unsupported_items: Unsupported items ----------------- These older Mac features are no longer supported: * Drawer windows * ResourceFork class * Cursors stored in the resource fork * TextArea.Save method (use :ref:`DesktopTextArea` and :ref:`RTFData` instead) ======================== Supporting Drag and Drop ======================== Controls support drag & drop between each other and to or from destinations outside of Xojo. These are the type of drops that are supported: * Files * Pictures * Raw Data * Text To allow one of these types of drops onto a control, in the Opening event for the control call the appropriate "Accept" method. The methods are: AcceptFileDrop, AcceptPictureDrop, AcceptRawDataDrop and AcceptTextDrop. When a control is set to accept a drop it will then receive the various drag events: DragEnter, DragExit, DragOver and DropObject. In most cases you are interested in the DropObject event which passes in a DragItem containing the details of the item being dropped. Text Fields and Text Areas support textual drag and drop automatically. .. youtube:: jKBl3tLot74 .. _/topics/user_interface/desktop/supporting_drag_and_drop/drag_text_onto_a_label: Drag text onto a Label ---------------------- The idea here is that you will let the user select some text in a :doc:`Text Field` and drag that text onto a :doc:`Label` where the label will display the dragged text. Here are the steps: 1. Add a Label and a TextField to a Window layout. 2. In the Opening event handler for the Label, add this code to allow text drags: .. code:: xojo Me.AcceptTextDrop 3. Add the DropObject event handler to the Label and add this code to display the text that was dropped on it: .. code:: xojo If obj.TextAvailable Then Me.Text = obj.Text End If Now you can run the project, type some text in the text field, select it and then drag and drop it onto the label to see the label's text get replaced with your selected text. .. _/topics/user_interface/desktop/supporting_drag_and_drop/drag_label_text_onto_a_text_field: Drag Label text onto a TextField -------------------------------- Suppose you want to do the opposite and allow the user to drag a label and have its text be added to a text field. Starting with the project you created for Drag Text Onto a Label you can add some additional code to allow the label to be dragged. 1. Go back to the Label you added above and add the MouseDown and MouseDrag event handlers. 2. Add this code to MouseDown: .. code:: xojo Return True ' Allow subsequent mouse events 3. Now add this code to MouseDrag which creates a DragItem, populates it with the text from the Label and then starts the drag: .. code:: xojo Var dragLabel As DragItem dragLabel = New DragItem(Self, Me.Left, Me.Top, Me.Width, Me.Height) dragLabel.Text = Me.Text dragLabel.Drag 4. Add the Opening event handler to the Text Field and add this code to tell it to accept text drops: .. code:: xojo Me.AcceptTextDrop 5. Add the DropObject event handler to the textfield with this code to get the text from the drop and display it: .. code:: xojo If obj.TextAvailable Then Me.Text = obj.Text End If Run the project and drag the Label onto the TextField to see the Label text replace what is in the TextField. .. _/topics/user_interface/desktop/supporting_drag_and_drop/drag_a_file_onto_a_list_box: Drag a file onto a ListBox -------------------------- To get a little more advanced, suppose you want to allow the user to drag files from the Finder (or File Explorer, etc.) and add the filename to a :doc:`DesktopListBox`. Here are the steps. 1. Since you are going to accept files the first thing to add is a :doc:`File Type Group` from the Insert menu or toolbar. 2. In the File Type Group, select the first toolbar button (Add Common File Type) and from the dropdown choose "special/any". 3. Now go to Window1 and add a ListBox to it. 4. Add the Opening event handler to the ListBox with this code to tell it to accept file drops for any file: .. code:: xojo Me.AcceptFileDrop(FileTypeGroup1.Any) 5. Lastly, add the DropObject event handler to the ListBox with this code to grab the filename from the dropped file and add it as a row to the ListBox: .. code:: xojo If obj.FolderItemAvailable Then Me.AddRow(obj.FolderItem.Name) End If Run the project and drag a file from the Finder (or File Explorer, etc.) onto the ListBox where its name should get added to the list. .. _/topics/user_interface/desktop/supporting_drag_and_drop/drag_a_picture_onto_a_canvas: Drag a picture onto a Canvas ---------------------------- A picture can come from a variety of sources. It could actually be a file so accepting a picture would work similarily to accepting a file as shown above. But a picture can also sometimes come directly from another app such as a web browser or drawing app. This is how you can accept picture drags from other apps to display in a :doc:`Canvas`. 1. In order to accept drags of files that are pictures you need to create a File Type Set. 2. Add two Common File Types, one for image/png and one for image/jpeg. 3. Now add a Canvas to Window1. 4. In the Opening event handler for the Canvas, add this code to accept drops for Pictures and the PNG and JPG files that you added to the File Type Set: .. code:: xojo ' Accept pictures from other apps such as some web browsers Me.AcceptPictureDrop ' Accept pictures dragged from files Me.AcceptFileDrop("image/png") Me.AcceptFileDrop("image/jpeg") 5. Add a property to the window which will store the picture that gets dropped onto the window so that the Canvas can draw it: .. code:: xojo DropPic As Picture 6. Add the Paint even to the Canvas. This code draws the picture when it is available: .. code:: xojo If DropPic Is Nil Then Return g.DrawPicture(DropPic, 0, 0, g.Width, g.Height, 0, 0, DropPic.Width, DropPic.Height) 7. Add the DropObject event to the Canvas with this code which determines the type of drop, gets the Picture and then tells the Canvas to draw it: .. code:: xojo If obj.PictureAvailable Then DropPic = obj.Picture ElseIf obj.FolderItemAvailable Then DropPic = Picture.Open(obj.FolderItem) End If Me.Refresh ' Draw the picture You can now run the project and drag a picture file onto the Canvas area. Some browsers (such as Safari) also let you drag a picture from the browser onto the Canvas area. .. _/topics/user_interface/desktop/supporting_drag_and_drop/drag_rows_from_a_list_box_to_a_text_field: Drag rows from a ListBox to a TextField --------------------------------------- You can drag rows within a single ListBox for reordering by turning on the AllowRowReordering property. But if you want to allow individual rows to be dragged out of a ListBox you have to do a bit more coding. For this example you will allow a ListBox row to be dragged onto a TextField. 1. Add a ListBox to a Window and turn its AllowRowDragging property to ON from the Inspector. 2. Add some initial data to the ListBox using the InitialValue property in the Inspector. 3. Add the DragRow event handler to the ListBox with this code to make a DragItem using the text from the row: .. code:: xojo drag.Text = Me.CellTextAt(row, 0) Return True ' to allow the drag 4. Now add a TextField and in its Opening event handler tell it to accept text drops: .. code:: xojo Me.AcceptTextDrop 5. Add the DropObject event handler to the TextField with this code to display the text from the drop: .. code:: xojo If obj.TextAvailable Then Me.Text = obj.Text End If Now you can run the project and drag a row from the ListBox onto the Text Field where the text will be displayed. .. _/topics/user_interface/desktop/supporting_drag_and_drop/see_also: .. seealso:: :doc:`DragItem` class, :doc:`WebDragItem` class ============================= The desktop control hierarchy ============================= The built-in controls have an inheritance hierarchy. The base class is called :doc:`DesktopControl` and it contains several common events, properties and methods. :doc:`DesktopUIControl` subclasses DesktopControl and adds additional events, properties and methods. Most desktop controls subclass :doc:`DesktopUIControl`, although a few have another subclass in between. Below is a list of some of the common events, properties and methods that are available for UI controls. .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/the_desktop_control_hierarchy_new_desktop_control_heirarchy.png .. _/topics/user_interface/desktop/the_desktop_control_hierarchy/desktopcontrol: DesktopControl -------------- :doc:`DesktopControl` is the base class for all desktop controls. Refer to the :doc:`Desktop Control` for details on all its events, properties and methods. .. _/topics/user_interface/desktop/the_desktop_control_hierarchy/events: Events ****** :ref:`Closing` - Called when the control is closing because the layout upon which it rests is closing. :ref:`Opening` - Called when the control is opening because the layout upon which it rests is opening. Use this to do any control initialization. .. _/topics/user_interface/desktop/the_desktop_control_hierarchy/properties: Properties ********** :ref:`Handle` - The handle can be used to interface with OS APIs using the :doc:`Declare` command. :ref:`Index` - The Index is used with Control Sets. :ref:`Name` - The name of the control. :ref:`Scope` - Set to Public or Private. Private controls cannot be accessed outside the Window. :ref:`Window` - Identifies the parent window of the control. .. _/topics/user_interface/desktop/the_desktop_control_hierarchy/methods: Methods ******* :ref:`Close` - Call this method to remove the control from the window. .. _/topics/user_interface/desktop/the_desktop_control_hierarchy/desktopuicontrol: DesktopUIControl ---------------- :doc:`DesktopUIControl` is a subclass of DesktopControl. Most controls that can appear on a Window are subclassed from DesktopUIControl. Refer to the :doc:`Desktop Control` for details on all its events, properties and methods. .. _/topics/user_interface/desktop/the_desktop_control_hierarchy/see_also: .. seealso:: :doc:`Windows` topic ======= Windows ======= Typically, most of a Desktop app's user interface will be in its windows. You create your user interface by adding interface controls such as Buttons and Check Boxes to windows. By default, a Desktop project has one window (Window1) that is displayed automatically when the app runs. Typically, you will begin designing your app's interface by adding controls to this window and then by adding code to the events of the controls and perhaps the window itself. To add additional windows to an app: * Add a new window to the project by clicking the Insert button on the toolbar or menu and selecting Window. * Set the window's Type and other properties using the Inspector. * Add controls to the window from the Library. * Add code as needed. * Add code to display the window in the finished app. Below is a list of commonly used Window events, properties and methods. Refer to :doc:`DesktopWindow` for the complete list. .. _/topics/user_interface/desktop/windows/window: Window ------ .. _/topics/user_interface/desktop/windows/events: Events ****** :ref:`CancelClosing` - Called when the window is asked to close. Return True to prevent the window from closing. This can be a useful place to display an "Are you sure" message. :ref:`Closing` - Called when the window is actually is closing. This is a good place for cleanup code. :ref:`Opening` - Called when the window is opening. This is a good place for code that initializing the window. :ref:`Resized`, :ref:`Resizing` - Called when the window has been resized or is in the process of being resized. Use this event to move controls or modify the layout as necessary. .. _/topics/user_interface/desktop/windows/properties: Properties ********** :ref:`MenuBar` - The menu bar that is displayed when this window is front most. If no menu bar is specified then the menu bar specified in the App object is used. :ref:`Title` - The text that appears in the title bar for the window. :ref:`Type` - The type of the window. The different Window Types are described below. .. _/topics/user_interface/desktop/windows/methods: Methods ******* :ref:`Close` - Close the window, calling the CancelClose event. :ref:`Show` - Displays the window. :ref:`ShowModal` - Display as a modal window. .. _/topics/user_interface/desktop/windows/window_types: Window types ------------ Your Desktop apps can have several different types of windows. The window type is set by its Type property. Some types, however, are rarely used in modern apps and are retained only for historical reasons and backward compatibility. A few specialized windows are supported only on Mac. The specifics of how a window looks on each OS tends to vary over time as OS vendors change themes and their overall look-and-feel. Be sure to test your apps on all platforms you ship with to verify you get the behavior you want. .. _/topics/user_interface/desktop/windows/document: Document ******** The Document window is the most common type of window. When you add a new window to a project, this is the default window type. It is also the window type for the default window, Window1. Document windows are most often used when the window should stay open until the user dismisses it by clicking its close box (if it has one) or clicking a button programmed to close the window. The user can click on other windows to bring them to the foreground, moving the document window behind the others. .. image:: https://documentation.xojo.com/topics/user_interface/desktop/images/windows_eddies_electronics_desktop.png Document windows can have a close box, a minimize box, a maximize box, and can be user-resizable. You can display document windows using the Show method: .. code:: xojo ReportWindow.Show On Windows and some versions of Linux, the default menu bar, MainMenuBar, appears within the window by default. You can choose to display the window with no menubar by setting the MenuBar property of the window to None, although this is uncommon. If you have created additional menu bars, you can also choose a different menu bar to display for a specific window. On Mac, the menu bar always displays at the top of the screen. .. _/topics/user_interface/desktop/windows/movable_modal_dialog: Movable Modal dialog ******************** This type of window stays in front of the app's other open windows until it is closed, preventing the user from being able to interact with the rest of the app. Use a Movable Modal window when you need to briefly communicate with the user without allowing the user to have access to the rest of the app. Because the window is movable, the user will be able to drag the window to another location in case they need to see information in other windows. You will typically display a Movable Modal Dialog using the ShowModal method: .. code:: xojo ResultWindow.ShowModal ' Code stops until window is closed On Windows, a Movable Modal dialog has minimize, maximize, and close buttons in the Title bar. In Windows MDI interfaces, the window opens in the center area of the screen rather than in the center area of the MDI window. Therefore, the Movable Modal dialog may open outside the MDI window. On Linux, the window typically has minimize and close buttons in its Title bar, but this can vary by Linux distribution. On Mac, Movable Modal windows do not have a close box, so you need to include a button that the user can click to dismiss the window unless the window will dismiss itself after the app finishes a particular task. This code in a button's :ref:`Pressed` event handler closes the window: .. code:: xojo Self.Close To have a Movable Modal window automatically dismiss itself, you can use a :doc:`Timer` that calls Self.Close when its Period is reached. .. _/topics/user_interface/desktop/windows/modal_dialog: Modal dialog ************ These windows are very similar to Movable Modal Dialogs. The only difference is that Modal Dialogs have no Title bar, so they cannot be moved. On Windows, a Modal Dialog has no minimize, maximize, or close buttons. In Windows MDI applications, a Modal Dialog opens in the center area of the screen rather than the center area of the MDI window. Therefore, a Modal Dialog may open outside of the app's MDI window. On Linux, Modal Dialogs are modal but have a Title bar and close and minimize buttons, so work the same as Movable Modal Dialogs. .. _/topics/user_interface/desktop/windows/floating_window: Floating Window *************** Like Movable Modal and Modal Dialog windows, a Floating window stays in front of all other windows in your app. The difference is that the user can still interact with other windows in the app. If you have more than one Floating window open, clicking on another Floating window will bring that window to the front, but all open Floating windows will still be in front of all non-floating windows. Because they are always in front of other types of windows, their size should be kept to a minimum or they will quickly get in the user's way. This type of window is most commonly used to provide tools the user frequently needs or to display information. Use the Show method to display a Floating Window: .. code:: xojo StatusWindow.Show .. _/topics/user_interface/desktop/windows/plain_box: Plain Box ********* These windows function as Modal Dialog windows. The only real difference is their appearance. Plain Box windows are sometimes used for splash screens and for apps that need to hide the desktop. On Windows MDI apps, a Plain Box window opens in the center area of the screen rather than the center area of the MDI window so it may open outside the MDI window. .. _/topics/user_interface/desktop/windows/shadowed_box: Shadowed Box ************ Like Plain Box windows, Shadowed Box windows function as Modal Dialog windows. The only difference is their appearance. Shadowed Box windows are not commonly used. On Mac, a Shadowed Box window works like a Modal Dialog box with a minimize button. .. _/topics/user_interface/desktop/windows/rounded_window: Rounded Window ************** Retained for compatibility purposes. This type is the same as a document window. .. _/topics/user_interface/desktop/windows/global_floating_window: Global floating Window ********************** A Global Floating window looks like a Floating window, except that it is able to appear in front of another app's windows, even when you bring another app window to the front. A "regular" Floating window appears only in front of its own app's windows. On Windows MDI apps, a Global Floating window can appear outside of the MDI window. By default, it opens in the top-left area of the screen. .. _/topics/user_interface/desktop/windows/sheet_window_(macos): Sheet Window (macOS) ******************* A Sheet Window is a special type of Mac dialog box that drops down from the window title bar. A Sheet Window behaves like a Modal Dialog window, except that an animation makes it appear to drop down from the parent window's Title bar. It can't be moved from that position and it puts the user interface in a modal state for the window. The user must respond to the choices presented in the Sheet window before they can interact with the window. They can interact with other windows in the app, however, so they are often a better choice than a Modal Dialog. You display a Sheet Window using the ShowModal method, supplying a reference to the parent window: .. code:: xojo PromptWindow.ShowModal(Self) ' Displays the Sheet within the current window On Windows and Linux, Sheet windows behave like Movable Modal Dialogs. This means if you are creating a cross-platform app you can design your dialogs as Sheets and they will works as Movable Modal Dialogs on Windows and Linux without you having to do anything specific. .. _/topics/user_interface/desktop/windows/metal_window: Metal Window ************ A Metal window uses a darker, solid background on Mac. .. note:: This window type is a relic of older versions of macOS, is deprecated by Apple and is no longer commonly used. On Windows and Linux, a Metal window looks like a regular Document window. .. _/topics/user_interface/desktop/windows/modeless_dialog: Modeless dialog *************** The Modeless Dialog window is similar to the Modal Dialog, except that it is paired with a parent window (usually a Document window). Unlike a Modal Dialog, it allows you to access the parent window while it is displayed. If you hide the parent window, the Modeless dialog hides as well. If you show the parent window, the dialog reappears. The Modeless Dialog is supported on Windows and Linux. On Mac, it behaves as a Document window. .. _/topics/user_interface/desktop/windows/subclassing: Subclassing ----------- Windows are a special construct in Xojo. The class representing a window is a :doc:`DesktopWindow`. You can create a subclass of :doc:`DesktopWindow` that contains methods and properties, but you cannot subclass a :doc:`DesktopWindow` to share the user interface. To create a :doc:`DesktopWindow` subclass for containing shared code, create a new class (called MyBaseWindow) and set its Super property to :doc:`DesktopWindow`. Add a new :doc:`DesktopWindow` to the project and change its Super to MyBaseWindow. You will now be able to access the Public and Protected members of MyBaseWindow on this new :doc:`DesktopWindow`. .. _/topics/user_interface/desktop/windows/implicit_window_instances: Implicit window instances ------------------------- Unlike other classes you can use windows directly by their name without having to first create an instance. For example this code displays Window2: .. code:: xojo Window2.Show This syntax is allowed because an "implicit instance" of the window is automatically created for you if its Implicit Instance property in the Inspector is ON (which is the default for all Windows). Although an implicit instance is convenient at times, there are side effects to keep in mind. 1. A window can be displayed inadvertently Accessing properties or methods of a window using its implicit instance can show the window, which might not be what you expect. For example, this code looks like it is just changing the title: .. code:: xojo Window2.Title = "Test" But if Window2 is not displayed, the act of setting its title will cause it to be displayed. 1. When using an Implicit Instance, it is not possible to have multiple copies of the same window displayed on screen at one time. #. Accessing enumeration values of a window when Implicit Instance is ON does not work. To avoid these limitations, you can disable implicit window instances for a window by turning the Implicit Instance property to OFF in the Inspector for the window. As this behavior is preferred, Implicit Instance will default to OFF in a future version of Xojo. To access a window when ImplicitInstance is OFF you create an instance using "New" just like you would for a class: .. code:: xojo Var w As New Window2 w.Show If you need to access this reference elsewhere then you will want to store it in a property, such as a public property on the App object. In that case your code would look like this: .. code:: xojo App.SalesWindow = New Window2 App.SalesWindow.Show .. _/topics/user_interface/desktop/windows/keyboard_access_on_macos: Keyboard access on macOS ------------------------ Macs includes a system-wide feature called Full Keyboard Access. This feature enables a user to do work with only the keyboard that ordinarily is done using both the keyboard and the mouse. For example, the standard Mac interface calls for mouse gestures to operate the menu system and the Dock. With full keyboard access, when the menu bar has the focus, menu items can be highlighted by the up and down arrow keys and an item is selected by pressing Spacebar. Full Keyboard Access is enabled in the Keyboard Shortcuts panel of the Keyboard System Preference. Click the All Controls radio button to enable Full Keyboard Access. When full keyboard access is on, you can select and set values for controls via the keyboard (such as when using tab and tab order) that normally do not have the focus. When a control accepts keystrokes via full keyboard access, it has a halo around it. For example, when full keyboard access is on, the user can select radio buttons via the keyboard only. To learn more about Full Keyboard Access, refer to `Apple's Mac accessibility shortcuts docs `_. Full Keyboard Access is a Mac system-wide option that the end-user must select using System Preferences. The Mac version of Xojo supports full keyboard access if the user chooses to turn it on. However, you cannot turn it on for them, so you cannot assume that all (or any) of your users will be using full keyboard access. .. _/topics/user_interface/desktop/windows/user_interface_guidelines: User interface guidelines ------------------------- The quality of your app interface determines how useful and usable it is. An intuitive interface is one of the most critical things in a successful app. Studies have shown that if a user can't accomplish something within the first 15 minutes of using an app, they will give up in frustration. Beyond being intuitive, the more polished an app's interface is, the more professional it will appear to the user. Remember that without realizing it, your users will be comparing your app's interface to all of the other apps they have used. The alignment guides in the Layout Editor help you make sure all your controls are aligned and positioned properly. But there is more to a professional, polished interface than aligning controls. Each supported platform has its own conventions. User interface guidelines are available from the following sources: * `Windows - Microsoft User Experience Guidelines `_ * `macOS - Apple Human Interface Guidelines `_ * `Linux - KDE Human Interface Guidelines `_ * `Linux Gnome Desktop Human Interface Guidelines `_ * KDE and Gnome are the most popular Linux desktops and are used by default in several major Linux distributions, however there are others. Linux also supports a greater degree of desktop customization than Windows and Mac. .. _/topics/user_interface/desktop/windows/see_also: .. seealso:: :doc:`Me vs Self` topic Dynamically adding and removing controls at runtime --------------------------------------------------- You may find situations where you can't create user interface controls ahead of time. Fortunately you can add and remove controls in desktop, mobile and web apps dynamically at runtime. .. note:: If you are creating additional controls and need them to share the same event handlers, consider using a :doc:`Control Set`. Creating and destroying a control instance ========================================== A control is simply an instance of a class. With that in mind, you create one the same way you create any instance of a class, with the :doc:`New` method. Accordingly, as with any class instance, you need to store the instance in some container (a variable, a property, etc.) that can keep it for as long as you need it. If you only need the control itself, you can create an instance of that control directly. In this example, an instance of a DesktopTextField is created and stored in a property of the window upon which it's going to be placed: .. code:: xojo UserName = New DesktopTextField If you need the control to respond to events, add a subclass of the control to your project and then create an instance of your subclass. In this example, UserNameField is a class that has been added to the project. It's Super property has been set to DesktopTextField: .. code:: xojo UserName = New UserNameField Unlike dragging a control from the Library to a layout, when you create a control instance, none of the properties have default values. This means you need to set all of them including their Left, Top, Width, Height and more. In this example, a DesktopTextField is created and default values set for some properties: .. code:: xojo UserName = New DesktopTextField UserName.Left = 100 UserName.Top = 100 UserName.Width = 200 UserName.Height = 22 UserName.Hint = "Enter a username here" When you no longer need the control instance, you can destroy it by calling the control's Close method: .. code:: xojo UserName.Close Of course since closing the instance destroys it, it also removes it from the layout. Adding controls to a layout =========================== Create a control instance does not add it to the layout. To do that, call the AddControl method of the layout (:doc:`DesktopWindow`, :doc:`MobileScreen` or :doc:`WebPage`). In this example, the control instance is being added in the Opening event of the layout: .. code:: xojo UserName = New DesktopTextField UserName.Left = 100 UserName.Top = 100 UserName.Width = 200 UserName.Height = 22 UserName.Hint = "Enter a username here" AddControl(UserName) Removing controls from a layout =============================== To remove a control, use its Close method. In this example, the control is being removed from the Pressed event of a button: .. code:: xojo UserName.Close .. seealso:: AddControl method for :ref:`DesktopWindow`, :ref:`MobileScreen` and :ref:`WebPage`; Close method for :ref:`DesktopControl`, :ref:`MobileScreen` and :ref:`WebControl` iOS === .. toctree:: :maxdepth: 1 :name: sec-ios Adding the Sharing Panel to your app Creating a Launch Screen Creating Dialog Boxes Creating Screens that auto-resize using Auto Layout Creating Split Layout for larger iOS Screens iOS Control Hierarchy iOS Controls that have no user interface Screen Design Considerations Understanding Screens Understanding the Message Box ==================================== Adding the Sharing Panel to your app ==================================== The Sharing Panel allows the app to share text, a URL, or a picture with any registered iOS system service or app. This control does not have user interface that displays on the layout so it appears in the Shelf when dragged to the layout. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/adding_the_sharing_panel_to_your_app_ios_tutorial_mobile_sharing_panel_icon.png Below is a list of commonly used events, properties and methods. Refer to :doc:`MobileSharingPanel` for details on its events and methods. .. _/topics/user_interface/ios/adding_the_sharing_panel_to_your_app/events: Events ------ :ref:`Cancelled` - Called if the user cancels the sharing operation. :ref:`Completed` - Called when the sharing operation is completed. .. _/topics/user_interface/ios/adding_the_sharing_panel_to_your_app/methods: Methods ------- :ref:`SharePicture` - Shares a picture. You need to provide the picture, the parent screen and parent control. :ref:`ShareText` - Shares text. You need to provide the text, the parent screen and parent control. :ref:`ShareURL` - Share a URL. You need to provide the URL, the parent screen and parent control. .. _/topics/user_interface/ios/adding_the_sharing_panel_to_your_app/usage: Usage ----- This code on a button's Pressed event handler shares text: .. code:: xojo SharingPanel1.ShareText("Hello world!", Self, Me) .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/adding_the_sharing_panel_to_your_app_ios_generic_sharing_panel.png If you use the SharePicture method, you will need to enable the Photos Access option. In the Navigator, click on iOS under Build Settings then in the Inspector's Advanced tab, enable Photo Access. .. youtube:: BFFyMYyz5lg .. _/topics/user_interface/ios/adding_the_sharing_panel_to_your_app/see_also: .. seealso:: :doc:`MobileSharingPanel` class ======================== Creating a Launch Screen ======================== You have a Launch Screen that displays when your app is launching before the main screen is displayed. A Launch Screen looks like a :doc:`MobileScreen`, but can only have these user interface elements and controls: * **Background Color**: Change using the property in the Inspector for the launch screen. * **Label**: Drag from the Library. That the text displayed in Labels on a Launch Screen is not localized. * **Image Viewer**: Drag from the Library. You cannot add other controls to a Launch Screen. No code can be run on a launch screen or any controls it contains. A default iOS project contains a project item called "LaunchScreen". If your project does not have a "LaunchScreen" item you can add it using the Insert menu or toolbar button. Set up the Launch Screen how you want and it will appear automatically when your app is launching. You should only consider adding a launch screen for apps that take a noticeable amount of time to start. Apps that start quickly may only show the launch screen for a split second before main app screen is displayed. A Launch Screen is required in order for your iOS app to be optimized for the 12" iPad Pro. Older versions of Xojo that instead used a Launch Image will scale your apps to fit on the 12" iPad Pro screen instead of allowing your app to use the full available screen area. .. _/topics/user_interface/ios/creating_a_launch_screen/see_also: .. seealso:: :doc:`MobileScreen` class ===================== Creating Dialog Boxes ===================== .. _/topics/user_interface/ios/creating_dialog_boxes/message_box: Message box ----------- A Message Box displays a prompt for the user. Although it is a user interface control, it does not appear on the Layout so when you drag it to your Layout, it is added to the Shelf. To learn more about Message Box go to the :doc:`Understanding the Message Box` topic. .. _/topics/user_interface/ios/creating_dialog_boxes/modal_screens: Modal screens ------------- A modal screen is a screen that is also displayed without being "pushed" onto the current screen. A modal screen cannot have toolbars and should not have other screens pushed onto it. You can show and close a modal screen using the :ref:`MobileScreen.ShowModal` method. In this example, the Login screen is being displayed modally from the current screen: .. code:: xojo Login.ShowModal(Self) .. _/topics/user_interface/ios/creating_dialog_boxes/sample_project: Sample project ************** * Examples/Platforms/iOS/ShowModal .. _/topics/user_interface/ios/creating_dialog_boxes/see_also: .. seealso:: :doc:`MobileMessageBox` class; :doc:`Understanding Screens`, :doc:`Understanding the Message Box` topic =================================================== Creating Screens that auto-resize using Auto Layout =================================================== Auto-Layout describes the ability for the controls on the layout to resize and reposition them as the layout size changes. It consists of a collection of rules that allow you to specify controls in relationship to other controls, making it possible to have layouts that work in all these situations: * Different device screen sizes * Changing device orientations * Language translations * User-specific settings such as font size * OS changes such as control sizes, fonts and spacing Without Auto-Layout, you typically would have to create multiple UI screens to account for different possibilities and then also add code to handle other situations. With AutoLayout, you can more easily design a single UI whose controls have constraints that allow them to adjust for the above situations. For example, the Auto-Layout rules for a button could indicate the following: * The Left margin of the button should match the Left margin of the TitleLabel control (at the same scale with no offset) * The Top of the button should be 10 points below the bottom of NoteTitleField (at the same scale). * The Height is fixed at 40 points * The Width is fixed at 100 points .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/creating_screens_that_auto-resize_using_auto_layout_ios_inspector_auto_layout.png Should you adjust the layout to change the position of TitleLabel or NoteTitleField, the button will be repositioned on the screen as defined by the rules. This is handy when designing you layout, but is even more important when you want your layouts to appropriately use the available screen area of the many different size iOS devices. You can see in the above set of rules that the label is set to have the same Left and Right edges as another label on the Screen (NameLabel). This means that if NameLabel is repositioned, then this label is also adjusted. You can also see the Top is set to be the bottom of FullAddressLabel plus a gap. So if FullAddressLabel is repositioned then this label is also adjusted accordingly. Some Auto-Layout rules are set for you automatically as you move and drag the control on the layout. But you can also set any of the rules manually. To edit a rule, click on its name and then click the Edit button. To add a new rule, click the "+" button and choose the rule from the list. To add and move a control around on the layout without auto-layout being applied, hold down the Command key as you drag the control. This adds the control using only offsets. The following Auto-Layout rules can be added, although not all controls have every rule available: * Left: The left position of the control. * Right: The right position of the control. * Top: The top position of the control. * Bottom: The bottom position of the control. * Horizontal Center: The centered horizontal position for the control. * Vertical Center: The centered vertical position for the control. * Width: The width of the control. * Height: The height of the control. * Baseline: Refers to an invisible line, offset from the bottom of the alignment rectangle, along which glyphs of characters are laid out. * Leading: Refers to the edge of the alignment rectangle where words and sentences begin. For left-to-right languages such as English, Leading is the same as Left, whereas in a right-to-left environment such as Hebrew or Arabic, Leading is the same as Right. * Trailing: Refers to the edge where words and sentences end. Trailing is the same as Right for English, but Trailing is the same as Left for Hebrew and Arabic. You should use Leading and Trailing to make sure your interface is laid out appropriately in all languages, unless the horizontal position should remain fixed at left or right regardless of the language. You do not need to specify every rule for a control. In fact, sometimes that would be confusing. For example, if you specify a Left and Right rule for a control, then also specifying a Width is not necessary as the Width can be inferred by the Left and Right rules. .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/auto_layout_rules: Auto Layout rules ----------------- .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/creating_screens_that_auto-resize_using_auto_layout_auto_layout_properties.png Each Auto-Layout rule has the following properties that can be set: * Is (Equal To, Min or Max) * Relative To (None, Containing Screen, TopLayoutGuide, BottomLayoutGuide or any other control on the layout) * Edge (None, Left, Right, Top, Bottom, Horizontal Center, Vertical Center, Width, Height, Leading, Trailing, Baseline, Top Constraint, Bottom Constraint) * Scale %: The percentage of the Edge property to use. * Offset (points): The offset to use with the Edge property. * Priority (Low, Medium, Medium-High, High, Highest): Specifies the importance of the Auto-Layout rule and thus the order that they are applied. In left to right writing systems Left is the same as leading and Right is the same as trailing. In right to left writing systems, like Arabic, Right is Leading and Left is trailing as the normal flow of layout is from right to left or from the leading edge to the trailing edge. .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/example_projects_for_auto_layout: Example projects for Auto Layout -------------------------------- * Examples/Platforms/iOS/Auto-Layout/Auto-Size Label * Examples/Platforms/iOS/Auto-Layout/Add and Edit Layout Constraints from code * Examples/Platforms/iOS/Auto-Layout/Proportional Spacing without code * Examples/Platforms/iOS/Auto-Layout/Switching Constraints .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/technical_information: Technical information --------------------- For any single control there are a number of attributes which can be used to refer to its position and size. In left to right writing systems Left is the same as leading and Right is the same as trailing. In right to left writing systems, like Arabic, right is Leading and left is trailing as the normal flow of layout is from right to left or from the leading edge to the trailing edge. There are other positional and sizing attributes that are very useful as well. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/creating_screens_that_auto-resize_using_auto_layout_auto_layout_view.png Suppose you have an Screen (seen below in grey) and a button placed on that screen where the left guide line appeared. That gap between the Screen and the button is the LeadingMargin. There is also a trailing margin, top margin and bottom margin. Suppose you have buttons positioned close to each other and you placed the right most after the left most. When you did this you snapped it to the guideline that appeared offset from the left most button. Something like below. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/creating_screens_that_auto-resize_using_auto_layout_auto_layout_standard_gap.png That gap between the two buttons is a “Standard gap” and is appropriately sized for the version of iOS that you are using. .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/setting_ioslayoutconstraint_in_code: Setting iOSLayoutConstraint in code ----------------------------------- You can use the iOSLayoutConstraint class to add new constraints to a control or modify existing constraints. Constraints are mathemetical expressions but are not written in mathematical form. So say you want to express that ControlA's edge is equal to ControlB's edge. Here is the expression: .. code:: xojo ControlA.EdgeA = ControlB.EdgeB * factor * offset To configure that using an iOSLayoutConstraint, these are the values that would be passed in the Constructor: .. csv-table:: :header: "Parameter", "Value" :widths: auto "firstItem As Object","ControlA" "firstAttribute As AttributeTypes","EdgeA" "relation As RelationTypes","iOSLayoutConstraint.RelationTypes.Equal" "secondItem As Object","ControlB" "secondAttribute As AttributeTypes","EdgeB" "multiplier As Double","factor" "addend","offset" "priority","1000 (typically)" .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/adding_a_constraint_to_a_control: Adding a constraint to a control ******************************** This code adds a right constraint to a TextField with an offset of the StandardGap and the highest priority to the containing Screen (Self): .. code:: xojo Var rightConstraint As New iOSLayoutConstraint( _ MyTextField, _ ' firstItem iOSLayoutConstraint.AttributeTypes.Right, _ ' firstAttribute iOSLayoutConstraint.RelationTypes.Equal, _ ' relation Self, _ ' secondItem iOSLayoutConstraint.AttributeTypes.Right, _ ' secondAttribute 1.0, _ ' multiplier iOSLayoutConstraint.StandardGap, _ ' gap 1000) ' highest priority .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/setting_a_specific_attribute_without_a_relation: Setting a specific attribute without a relation *********************************************** Typically you do not specifically set the width or height for a control, you instead set them as relative to some other edge of the containing screen or another control. The reason to set a relation that says "keep this edge this far from some other edge" is so that if you rotate the screen, the control remains in its original relative positioning (say 20 pixels from the edge of the screen it's in). Now if you want to specifically say "keep this to 10 pixels high", remember the original expression: .. code:: xojo ControlA.EdgeA = ControlB.EdgeB * factor + offset This is how you would configure an iOSLayoutConstraint to set the height of a Label to 10: .. code:: xojo Var heightConstraint As New iOSLayoutConstraint( _ MyLabel, _ ' firstItem iOSLayoutConstraint.AttributeTypes.Height, _ ' firstAttribute iOSLayoutConstraint.RelationTypes.Equal, _ ' relation Nil, _ ' secondItem iOSLayoutConstraint.AttributeTypes.None, _ ' secondAttribute 1.0, _ ' multiplier 10, _ ' addend 1000) ' highest priority The key here is to specify Nil for the secondItem as you are not actually relating to another control, but instead setting a specific offset (addend) value. This essentially creates this expression: .. code:: xojo MyLabel.Height = (Nothing.NoEdge) * 1.0 + 10 Which evaluates to .. code:: xojo MyLabel.Height = 10 .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/modifying_an_existing_constraint: Modifying an existing constraint ******************************** You can also modify existing constraints that you have configured using the Auto-Layout properties in the Layout Editor. Before you can do this, you first have to give the constraint you want to modify a name. To do so, select the control, then select the specific constraint in the Auto-Layout section of the Inspector and click Edit. In the topmost field (that has the name of the constraint as its label), give it a name and click Done. This is the name you will use to refer to the constraint in code. For example, a TextField with a Width constraint named TFWidth can be modified at runtime with this code: .. code:: xojo Var c As iOSLayoutConstraint = Self.Constraint("TFWidth") c.Offset = 100 .. _/topics/user_interface/ios/creating_screens_that_auto-resize_using_auto_layout/see_also: .. seealso:: :doc:`iOSLayoutConstraint`, :doc:`MobileScreen` classes ============================================ Creating Split Layout for larger iOS Screens ============================================ A Split layout allows you to split the layout area into two sections: a master on the left and a detail on the right. Each of these sections is its own Screen (or Tab). This layout is only available for iPads so you set it up on the iPadLayout used by iPad-sized devices. In landscape orientation, the master and detail appear on the screen at the same time. In portrait orientation, by default only the detail section appears. Swipe from the left edge of the screen to the right to show the master section. To force the split to display both sections in portrait, set the :ref:`iOSSplitView.DisplayMode` property. The Mail app included with iOS is an example of a split layout in action. When displayed in landscape orientation it displays the email messages on the left and the selected message in the detail section on the right. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/creating_split_layout_for_larger_ios_screens_ios_split_view.png You can also have each section of a Split be a Tab for further granularity. .. _/topics/user_interface/ios/creating_split_layout_for_larger_ios_screens/split_layout: Split Layout ------------ You configure a split layout in the iPadLayout project item. In its Inspector, go to the Content property and change the value to "Split". This displays two additional properties: * Left (Main) * Right (Detail) For these two sections you can specify a Screen to display or you can choose to set up tabs that display within the sections. Change the layout control to "Landscape" to see how the two sections will look side-by-side. Be sure to select an iPad device in the iOS settings to run and test the split in the iOS Simulator. You use the MobileScreen.ParentSplitScreen property to see if a split is active. If it is not Nil then a split is active. This returns an iOSSplitView with the properties Detail and Master that allow you to get the screens (or tabs) that are set for those sections. You can use these properties to cause other screens to be displayed in the sections at run time. This code (which, for example, could be on a table in the master screen) changes the detail section to display a screen containing customer details: .. code:: xojo If Self.ParentSplitView <> Nil Then Var details As New CustomerDetailsScreen Self.ParentSplitView.Details = details End If This code pushes a new screen onto whatever screen is displayed in the details section: .. code:: xojo If Self.ParentSplitView <> Nil Then If Self.ParentSplitView.Detail IsA MobileScreen Then Var info As New InfoScreen info.Show(MobileScreen(Self.ParentSplitView.Detail)) End If End If .. _/topics/user_interface/ios/creating_split_layout_for_larger_ios_screens/dynamic_split_layout: Dynamic Split Layout -------------------- You can also create split layouts at run time using the iOSSplitView class and the MobileApplication.CurrentLayout property. You can manually create a new iOSSplitView in your code, assign views to the Master and Detail properties and then assign the split to the MobileApplication.CurrentLayout property. This technique allows your app to switch between a split and a non-split screen. To check if a split is available for use, you can use the iOSSplitView.Available shared method. You do not want to set up a split on an iPhone because only the Detail section will be visible and there will be no way to get to the Master section. This code sets up a split: .. code:: xojo If iOSSplitView.Available Then Var split As New iOSSplitView Var master As New MasterScreen split.Master = master Var detail As New DetailScreen split.Detail = detail App.CurrentLayout.Content = split End If .. _/topics/user_interface/ios/creating_split_layout_for_larger_ios_screens/see_also: .. seealso:: :doc:`iOSSplitView`, :doc:`MobileScreen` classes ===================== iOS Control Hierarchy ===================== The built-in controls have an inheritance hierarchy. The base class for all iOS user interface controls is called :doc:`MobileUIControl`. It along with its parent class :doc:`MobileControl` contain several common events, properties and methods. Below is a list of some of the common events, properties and methods that are available for UI controls. .. _/topics/user_interface/ios/ios_control_hierarchy/events: Events ------ :ref:`Closing` - Called when the control is removed from its container, such as a :doc:`Screen`. :ref:`Opening` - Called after the control is created. This is where you typically put initialization code. .. _/topics/user_interface/ios/ios_control_hierarchy/properties: Properties ---------- :ref:`AccessibilityHint`, :ref:`AccessibilityLabel` - Text that is read aloud when accessibility is enabled for the device. :ref:`Height`, :ref:`Left`, :ref:`Top`, :ref:`Width` - These read-only properties indicate the position and size of the control (in points). :ref:`Visible` - When :doc:`True`, the control is visible, when :doc:`False` the control is not visible. .. _/topics/user_interface/ios/ios_control_hierarchy/methods: Methods ------- :ref:`TintColor` - This lets you influence the color of some iOS controls. .. _/topics/user_interface/ios/ios_control_hierarchy/see_also: .. seealso:: :doc:`MobileControl` and :doc:`MobileUIControl` classes ======================================== iOS Controls that have no user interface ======================================== .. _/topics/user_interface/ios/ios_controls_that_have_no_user_interface/timer: Timer ----- .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/ios_controls_that_have_no_user_interface_ios_timer_library_icon.png A :doc:`Timer` is not actually a user interface control, but it is often used with user interface controls. A :doc:`Timer` runs code at periodic intervals, which is useful for updating progress, animation and anything else that requires the user interface to be updated periodically. Below are the common events, properties and methods. Refer to the :doc:`Timer` control for for all its details. .. _/topics/user_interface/ios/ios_controls_that_have_no_user_interface/events: Events ****** :ref:`Run` - This event is called when the Timer interval is reached, as specified by its RunMode and Period. .. _/topics/user_interface/ios/ios_controls_that_have_no_user_interface/properties: Properties ********** :ref:`RunMode` - The RunMode of a Timer indicates how its Run event gets called. It can have one of three values from the Timer.RunModes enumeration: * Off: The Run event is not called. * Single: The Run event is called once when the Period is reached. * Multiple: The Run event is called each time the Period is reached. Period: Indicates (in milliseconds) how frequently the Run event is called. .. _/topics/user_interface/ios/ios_controls_that_have_no_user_interface/methods: Methods ******* :ref:`CallLater` - Used to set up a Timer that will call a method at when the Period is reached. :ref:`CancelCallLater` - Used to cancel a Timer set up with CallLater. .. _/topics/user_interface/ios/ios_controls_that_have_no_user_interface/usage: Usage ***** When you add a Timer to a Layout, the Inspector shows the RunMode and Period properties. As noted above, the RunMode can be Off, Single or Multiple (the default). Period defaults to 1000 milliseconds (1 second). A Timer is used to update the user interface. Your app code runs in what is called the "main thread". Timer code also runs in the main thread. If you have a long-running process, it is possible that the Timer's Run event handler call could be delayed until the process finishes. Essentially, a Timer's Run event is only called when the app is idle. This is fine for nearly all cases because and app is most often in the idle state. Another way to think of a Timer is that it is a "waiter". It waits at least until the Period has elapsed and the app is idle. When those conditions are met, it runs its Run event handler. You can change the RunMode and Period values within the Timer's Run event handler in order to change how often the Timer runs or to stop it from running entirely. This code updates a Progress Bar every 1/2 second (RunMode = Multiple and Period = 500) and then turns the Timer off when the Maximum of the Progress Bar is reached: .. code:: xojo ProgressBar1.Value = ProgressBar1.Value + 1 ' Stop Timer when ProgressBar reaches maximum If ProgressBar1.Value > ProgressBar1.MaxValue Then Me.RunMode = Timer.RunModes.Off End If Timers are also used for animation, typically to tell a Canvas to redraw itself. If you have code in a Canvas Paint event handler that updates the position of some graphics, you can tell it to redraw itself from a Timer: .. code:: xojo AnimationCanvas.Refresh You can also add Timers directly in code, but when you do so you have to provide a method that will be called in place of the Run event handler. For a Timer you only want to call once, you can do this using the CallLater method. You create a method that you want the Timer to call, such as this method which will clear a Label's text: .. code:: xojo Sub ClearLabel MyLabel.Text = "" End Sub Then you use the CallLater method to tell the Timer to call this method after a period of time. This code sets the Label text and then tells it to call the ClearLabel method after two seconds so that it gets cleared: .. code:: xojo MyLabel.Text = "Help text goes here" Timer.CallLater(2000, AddressOf ClearLabel) If you need a repeating Timer, you can create one and then assign a method to run in place of the Run event by using the :doc:`AddHandler` command. This code calls the TimerRun method once a second: .. code:: xojo MyTimer = New Timer MyTimer.Period = 1000 MyTimer.Mode = Timer.RunModes.Multiple AddHandler MyTimer.Run, AddressOf TimerRun If you use AddHandler with a Timer, you also need to call :doc:`RemoveHandler` at some point. You'll usually want to do this when the Timer is stopped or when the Layout containing the Timer is closed. .. _/topics/user_interface/ios/ios_controls_that_have_no_user_interface/generic_object: Generic object -------------- .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/ios_controls_that_have_no_user_interface_generic_object_library_icon.png A Generic Object is used to add classes that are not user interface controls to a Layout. These classes appear on the Shelf of the Layout. Adding classes in this manner makes it easier to implement their events. Because the class is now on the Layout, you can just click on it and select "Add Event Handler" to implement its event handlers. The alternative to this is creating a subclass, implementing the events on the subclass and then manually creating an instance via code. After dragging a Generic Object onto your layout, you need to go to the Super field in the Inspector and change it to the name of the class you want to use. .. _/topics/user_interface/ios/ios_controls_that_have_no_user_interface/see_also: .. seealso:: :doc:`Timer` class ============================ Screen Design Considerations ============================ A Layout defines how your initial layout appears on the iOS device. By default your iOS project contains two screens: iPhoneLayout and iPadLayout. You can change the preview mode for the layout by using the toolbar buttons to change the orientation (portrait or landscape) and the device type. These settings affect the preview only, are not saved, and do not affect how the app runs in the Simulator or on the device. Use the Supported Orientations settings to control how the app displays when run. The Inspector for a Layout has two sections of importance: `screen` Layout and Supported Orientations. The `screen` Layout setting allows you to specify the Content property, which determines how the display looks and works. There are three choices: `screen`, Split and Tabs. .. _/topics/user_interface/ios/screen_design_considerations/layout: Layout ------ .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/screen_design_considerations_ios_tutorial_xojo_notes_split_layout.png A Layout have have several layouts styles, including just a single `screen`, a split of two screens and tabs for multiple screens. .. _/topics/user_interface/ios/screen_design_considerations/screen: `Screen` ******** By default, Content points to `screen`. This is the initial `screen` that will be displayed when the app launches, filling it entirely. You can change this to point to any `screen` that is in your project. New Screens can be displayed over this default `screen` by calling the PushTo method on `screen`. You can also use the MobileApplication.CurrentLayout property to change the initial `screen`. .. _/topics/user_interface/ios/screen_design_considerations/split: Split ***** A Split layout allows you to split the layout into two sections: a master on the left and a detail on the right. Each of these sections is its own `screen`. This layout is only available on the iPadLayout used by iPad-sized devices. In landscape orientation, the master and detail appear on the layout at the same time. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/screen_design_considerations_ios_tutorial_split_settings.png In portrait orientation, only the details `screen` appears. Swipe from the left edge of the `screen` to the right to show the master `screen`. The Xojo Notes app included with iOS is an example of a Split in action (when displayed in landscape orientation). It displays the email messages on the left and the selected message in the detail area on the right. You can also have each section of a Split be a Tab for further granularity. .. _/topics/user_interface/ios/screen_design_considerations/tabs: Tabs **** Much like with a desktop app, the tabs layout allows you to have multiple screens that the user can select by using the tab control. In the case of iOS apps, the tab control is displayed at the bottom of the `screen`. For an example, the iOS Clock app uses tabs at the bottom to select the type of clock you want to use. Use the Edit button for the Tabs property to add or remove tabs, specifying their name or icon. Use the Tab Content property to specify the initial `screen` to display for each tab. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/screen_design_considerations_ios_tutorial_tabs_settings.png You need to click on each tab on the Layout to change the selected tab so that you can set the Tab Content property for it. .. _/topics/user_interface/ios/screen_design_considerations/supported_orientations: Supported orientations ---------------------- You can control the types of orientations that are allowed using the Supported Orientation settings of the Inspector for the iPhone and iPad screens. There are four options: * Portrait (Home on Bottom) * Landscape (Home on Left) * Landscape (Home on Right) * Portrait (Home on Top) By default, each of these settings is set to ON. If you want to disable an orientation for your app, set it to NO. For example, it is common for iPhone apps to have Portrait (Home on Top) disabled and many iPhone apps also have Landscape orientations disabled. .. _/topics/user_interface/ios/screen_design_considerations/app_multitasking: App multitasking **************** The iPad supports app multitasking which allows you to use more than one app at a time on the device. Your app automatically supports this if all the "Supported Orientations" are turned on. If you turn off any supported orientations, then your app cannot be used by iOS app multitasking. .. _/topics/user_interface/ios/screen_design_considerations/device_rotation: Device rotation --------------- When the user rotates the device to change its orientation, the Resized event handler on the currently displayed `screen` is called. In this event handler, you can check the size of the `screen` (using its Size method) to see if it is now in portrait or landscape. With this information, you can then decide to reposition controls or to display a completely different `screen`, allowing you to have one `screen` for portrait and one for landscape. To check if the `screen` is in landscape, check if the height is greater than the width: .. code:: xojo If Self.Size.Height > Self.Size.Width Then ' Landscape End If To check if the `screen` is portrait, check if the height is less than the width: .. code:: xojo If Self.Size.Height < Self.Size.Width Then ' Portrait End If If you not use any special processing, the auto-layout rules you have specified for the controls on the `screen` will adjust your controls. For example, a text field may expand to use more available width when a device is rotated from portrait to landscape. .. _/topics/user_interface/ios/screen_design_considerations/changing_screens_at_run-time: Changing Screens at runtime ---------------------------- You can add additional layouts (in addition to the default iPhoneLayout and iPadLayout) to your project. You can then switch these screens to display at runtime by using the iOSApplication.CurrentLayout property like this: .. code:: xojo Var s As New MySpecialLayout App.CurrentLayout.Content = s.Content When you do this the `screen` is immediately replaced and the `screen` hierarchy that is accessible with the Back button on the Navigation bar is lost. .. _/topics/user_interface/ios/screen_design_considerations/see_also: .. seealso:: :doc:`iOSLayout` class; :doc:`Mobile Screens` topics ===================== Understanding Screens ===================== Screens are where you design a mobile layout by adding controls. This is similar to a Window in desktop projects or a WebPage in web projects. You drag controls from the Library onto the Screen, positioning them as appropriate. .. note:: iOS uses a feature called Auto-Layout to determine how the controls move on the screen as the device size or orientation changes. Use the Insert button or menu to add new Screens to your project. You can change the way the Screen layout appears by using the Portrait/Landscape buttons or by clicking the Device Type button to change the size of the device to various iPhone and iPad sizes. Note that these only affect the preview and do not alter how the app runs. If you want to control Portrait/Landscape support for your app, do it in for the appropriate :doc:`iOSLayout`. Otherwise the Layout Editor for a Screen works as described in the Layout Editor topic. Add event handlers to the controls the same way you do in desktop and web projects: choose the "+" button in the Layout Editor toolbar and select "Add Event Handler". A view can have several event handlers: Activated, Closing, Deactivated, Opening, Resized and ToolbarButtonPressed. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/understanding_screens_ios_view_simple.png Below are commonly used events, properties and methods used with Screens. Refer to :doc:`MobileScreen` for the complete list. .. _/topics/user_interface/ios/understanding_screens/events: Events ------ :ref:`Activated` - Called when the Screen is first opened or redisplayed after another Screen that was displayed over it is closed. :ref:`Closing` - Called when the Screen is closing. :ref:`Deactivated` - Called when the Screen is deactivated by either closing it or displaying another Screen on top of it. :ref:`Opening` - Called when the screen first opens. This is typically where you will do initialization such as setting up toolbars. :ref:`ToolbarButtonPressed` - Called when a button on the Navigation Bar or the Toolbar is pressed. .. _/topics/user_interface/ios/understanding_screens/properties: Properties ---------- The Behavior section of the View Inspector has these properties you can use: BackButtonCaption, HasNavigationBar, and Title. :ref:`BackButtonCaption` - BackButtonCaption is the title that appears on a subsequent Screen to get back to this Screen. For example, if this Screen contains Notes, then you might set BackButtonCaption to "Notes" so that if a later Screen displays details for a note (by pushing onto this Screen), its back button will show "Notes" instead of just "Back". When you then click the "Notes" button, the new Screen closes and this Screen reappears. :ref:`Title` - The Title property is the title of the view. :ref:`HasNavigationBar` - The Title and BackButton are only displayed if the Navigation Bar is visible (HasNavigationBar is ON or True). .. _/topics/user_interface/ios/understanding_screens/methods: Methods ------- :ref:`Close` - Call this method to close the Screen. The initially displayed Screen cannot be closed. :ref:`Show` - This method is used to display new Screens as shown below. :ref:`Size` - Returns the width and height of the Screen area as a Size. .. _/topics/user_interface/ios/understanding_screens/usage: Usage ----- .. _/topics/user_interface/ios/understanding_screens/displaying_a_new_screen: Displaying a new Screen *********************** You are often going to have more than just a single Screen for your app. Unlike a desktop app that can have multiple windows open at once, a mobile app can only have one Screen open at a time. When you call the Show method to display another Screen, the new Screen is displayed in front of the current one in a stack formation. This means that when the frontmost Screen closes, the one underneath it will be displayed automatically. The Show method automatically creates this stack so that the back button (in the Navigation Bar) on the new Screen can be used to go back to the original Screen. For example, say you have a view called NotesScreen and a view called NoteDetailScreen and you want NoteDetailScreen to appear when the user clicks on a note displayed in NotesScreen. This code on NotesScreen displays NoteDetailScreen: .. code:: xojo Var details As New NoteDetailScreen Self.Show(details) The previous Screen can be displayed by the user tapping the back button in the Navigation Bar (if enabled) or by closing the new Screen in your code: .. code:: xojo Self.Close .. _/topics/user_interface/ios/understanding_screens/changing_the_current_screen: Changing the current Screen *************************** There may be times when you want to change the current Screen but do not want it to be part of the Screen navigation stack. This situation can occur with a startup login screen, for example. You can have the login Screen be displayed by default and when the user successfully logins in, you can change the base screen to a new Screen so that the navigation stack is not available. You can directly change the current Screen to a new Screen by using the MobileApplication.CurrentLayout.Content property like this: .. code:: xojo Var myNewScreen As New MyScreen App.CurrentLayout.Content = myNewScreen The above example simple switches to a new Screen, but you can also switch the layout to point to a new tab or split (on an iPad). To switch to a new tab, you have to create a new iOSTabBar and then add your Screens to the tabs. This example adds two tabs to a tab and then makes it the current layout: .. code:: xojo Var tab As New iOSTabBar Var mv As New MainScreen mv.Title = "Main" tab.AddTab(mv) Var v2 As New Screen2 v2.Title = "Tab 1" tab.AddTab(v2) App.CurrentLayout.Content = tab Similarly, this example adds two Screens to a split and then makes it the current layout: .. code:: xojo Var split As New iOSSplitView Var master As New MainScreen split.Master = master Var detail As New DetailScreen split.Detail = detail App.CurrentLayout.Content = split .. _/topics/user_interface/ios/understanding_screens/displaying_a_modal_screen: Displaying a modal Screen ************************* A modal Screen is a Screen that is also displayed without replacing the current Screen. A modal Screen cannot have toolbars and should not have other Screens pushed onto it. You can show and close a modal Screen using the :ref:`ShowModal` method: .. code:: xojo Var v As New ModalScreen v.ShowModal .. _/topics/user_interface/ios/understanding_screens/adding_toolbar_buttons: Adding toolbar buttons ********************** A view can have a Navigation Bar, which displays at the top of the Screen, or a toolbar which displays at the bottom of the Screen. In a horizontally regular environment, a navigation bar can also display within a Screen that doesn't extend across the display, such as one pane of a split view controller. Refer to :doc:`Toolbar` for more information. .. _/topics/user_interface/ios/understanding_screens/see_also: .. seealso:: :doc:`MobileScreen` class; :doc:`Screen Design Considerations`, :doc:`Mobile Toolbars` topics ============================= Understanding the Message Box ============================= A MessageBox displays a prompt for the user. Although it is a user interface control, it does not appear on the Layout so when you drag it to your Layout, it is added to the Shelf. Unlike with desktop a dialog (but similar to a web dialog), the Mobile Message Box is not modal. Your code does not stop executing when the Message Box is displayed. If you have code you want to run after the Message Box is closed, then put it in the Pressed event. .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/understanding_the_message_box_ios_message_box_library_icon.png Below are common events, properties and methods. Refer to :doc:`MobileMessageBox` for the complete list. .. _/topics/user_interface/ios/understanding_the_message_box/events: Events ------ :ref:`Pressed` - This event is called when one of the buttons on the Message Box is tapped, with a parameter containing the index of the button that was tapped. .. _/topics/user_interface/ios/understanding_the_message_box/properties: Properties ---------- :ref:`Message` - This is the message to display in the Message Box. In the sample shown on the right it is the "This is a sample message." text. :ref:`Title` - This is the title that appears at the top of the Message Box. In the sample shown on the right it is the "Hello" text. .. _/topics/user_interface/ios/understanding_the_message_box/methods: Methods ------- :ref:`Buttons` - These are the buttons to display. :ref:`Show` - This displays the Message Box on the screen. .. _/topics/user_interface/ios/understanding_the_message_box/usage: Usage ----- .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/understanding_the_message_box_ios_message_box.png The easiest way to use a Message Box is to drag it from the Library onto the Layout. The Inspector for a Message Box can be used to change the Message, Title, Left Button and Right Button properties. If you need more than two buttons, you can add them using code. When there are more than two buttons, they display vertically. This example adds three buttons to a Message Box that has been added to the Screen: .. code:: xojo Var buttons() As String buttons.Add("Yes") buttons.Add("No") buttons.Add("Maybe") HelloMessage.Buttons = buttons HelloMessage.Show It is important to understand that your code does not pause at the "Show" call to wait for a button to be tapped. A Message Box is asynchronous, which means it displays immediately and your code continues running to the end of the method or event. .. note:: Calling the Show method does not pause your code until a button is tapped. To determine when a button was tapped, you must add the Pressed event handler to the Message Box. This event is called with a parameter containing the index of the button that was tapped. This code displays the index for the tapped button: .. code:: xojo Label1.Text = "You selected button " + buttonIndex.ToString .. _/topics/user_interface/ios/understanding_the_message_box/more_advanced_techniques: More advanced techniques ************************ .. image:: https://documentation.xojo.com/topics/user_interface/ios/images/understanding_the_message_box_ios_message_box_vertical_buttons.png You can also add a Message Box entirely in code, but you'll need to provide a method (on the View) to handle the Pressed event (using :doc:`AddHandler`). To add a simple Yes/No Message Box, start by adding a property to the Layout: .. code:: xojo SampleMessageBox As MobileMessageBox In the code that displays the Message Box, create it and tell it to call a method when the Pressed is called. You might do this on a Button: .. code:: xojo SampleMessageBox = New MobileMessageBox SampleMessageBox.Title = "Hello" SampleMessageBox.Message = "Hello, World!" Var buttons() As String buttons.Add("OK") SampleMessageBox.Buttons = buttons AddHandler SampleMessageBox.Pressed, AddressOf SampleButtonPressed SampleMessageBox.Show Now add the method to the Screen to perform an action and remove the handler: .. code:: xojo Private Sub SampleButtonPressed(sender As MobileMessageBox, index As Integer) OutputLabel.Text = "Button tapped." RemoveHandler SampleMessageBox.ButtonPressed, AddressOf SampleButtonPressed End Sub .. _/topics/user_interface/ios/understanding_the_message_box/see_also: .. seealso:: :doc:`MobileMessageBox` class; :doc:`Creating Dialog Boxes` topic ======================== Playing movies and sound ======================== Your apps can play movies using several available controls: :doc:`DesktopMoviePlayer`, :doc:`WebMoviePlayer` and :doc:`MobileMoviePlayer`. .. _/topics/user_interface/playing_movies_and_sound/playing_movies_in_desktop_apps: Playing movies in desktop apps ------------------------------ To play a movie in your desktop app, drag a MoviePlayer control onto a window. You can then set the Movie property to the movie that you want to play. This can be done in the Inspector for movies that have been added to the project or it can be done at runtime by assigning a Movie to the Movie property. On macOS, MoviePlayer uses AVFoundation to play the movie. On Windows, Windows Media Player is used to play the movie. MoviePlayer is not supported on Linux. This code prompts the user to select a movie and then plays it in a MoviePlayer: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog("") If f <> Nil Then MoviePlayer1.Movie = Movie.Open(f) MoviePlayer1.Play End If .. _/topics/user_interface/playing_movies_and_sound/webmovieplayer: WebMoviePlayer ************** WebMoviePlayer can play movies from a variety of sources using the browser's built-in HTML5 video capabilities. To play a movie, you assign a URL to the MovieURL property. .. _/topics/user_interface/playing_movies_and_sound/playing_movies_in_mobile_apps: Playing movies in mobile apps ---------------------------- To play movies in iOS apps, you can use the :doc:`MobileMoviePlayer` control. To do this, add a Copy Files Build Step to your project to add the movie file. Refer to the :doc:`Copy Files to iOS Device` topic for information on how to set up a Copy Files Step. In your app you point a FolderItem to the movie file and then tell the MobileMoviePlayer to display it like this: .. code:: xojo Var f As FolderItem = SpecialFolder.Resource("MyMovie.mp4") If f.Exists Then MoviePlayer1.LoadFile(f.URLPath) End If .. _/topics/user_interface/playing_movies_and_sound/playing_sounds_and_audio_in_desktop_apps: Playing sounds and audio in desktop apps ---------------------------------------- With desktop apps, you have a several options for playing sounds. .. _/topics/user_interface/playing_movies_and_sound/sound_class: Sound class *********** Sound files that have been added to your project can be played simply by referencing their name and calling the Play method: .. code:: xojo SoundName.Play This works for most sound files such as WAV, AIFF, MP3, AAC, etc. You can also load sounds at runtime using the FolderItem.ShowOpenFileDialog method and the Sound class: .. code:: xojo Var f As FolderItem f = FolderItem.ShowOpenFileDialog("") If f <> Nil Then Var s As Sound = Sound.Open(f) If s <> Nil Then s.Play End If End If The Sound class has properties to adjust the volume and left/right panning of the sound. It also has methods to play, loop, stop, clone and check if a sound is playing. .. _/topics/user_interface/playing_movies_and_sound/note_player: Note Player *********** The NotePlayer class is used to play musical notes. NotePlayer is not supported on Linux. This code plays “do-re-mi-fa-so-la-ti-do”: .. code:: xojo NotePlayer1.Instrument = 1 ' Notes for Do Re Mi Fa So La Ti Do ' (C, D, E, F, G, A, B, C) Var DoReMi(7) As Integer DoReMi = Array(60, 62, 64, 65, 67, 69, 71, 60) For Each note As Integer In DoReMi NotePlayer1.PlayNote(note, 100) ' Pause to let note play Thread.SleepCurrentThread(500) Next .. _/topics/user_interface/playing_movies_and_sound/movieplayer: MoviePlayer *********** You can also use a MoviePlayer to play sounds. Open your sound file as if it were a Movie and assign it to the Movie Property. Using the MoviePlayer to play sounds allows you to use the Movie controller to play and stop the sound or you can make the MoviePlayer invisible if necessary. .. _/topics/user_interface/playing_movies_and_sound/playing_sounds_in_web_apps: Playing sounds in web apps -------------------------- To play sounds in web apps, use the :doc:`WebAudioPlayer` control. Just assign a URL that points to an MP3 file to the URL property then call the Play method to play it. .. _/topics/user_interface/playing_movies_and_sound/loading_sounds_at_runtime_in_mobile_apps: Loading sounds at runtime in mobile apps ---------------------------------------- Use a Copy Files Build Step to copy the sound files to your resources and then you can load a sound file like this: .. code:: xojo Var soundFile As FolderItem soundFile = SpecialFolder.Resource("MySound.mp3") Var mySound As Sound = Sound.Open(soundFile) mySound.Play .. _/topics/user_interface/playing_movies_and_sound/see_also: .. seealso:: :doc:`Movie`, :doc:`DesktopMoviePlayer`, :doc:`DesktopNotePlayer`, :doc:`Sound`, :doc:`WebMoviePlayer`, :doc:`MobileMoviePlayer` classes ======================================== Sharing event handlers with Control Sets ======================================== Control Sets allow you to have a collection of controls that share a single set of event handlers. They are available for both desktop and web projects. You create a control set by selecting a control and then changing the value for the Member Of property in the Inspector (located in the Control Set group on the Advanced tab of the Inspector). To create a new Control Set for the selected control, choose New Control Set from the popup menu. This creates a Control Set with the name of the selected control. .. image:: https://documentation.xojo.com/topics/user_interface/images/creating_a_new_control_set.png :scale: 50 % You add other controls to the Control Set by clicking on them and selecting the Control Set name to add them to. When you do this you'll see the Index property get a value to indicate its position in the Control Set. This is how you can tell them apart. .. image:: https://documentation.xojo.com/topics/user_interface/images/adding_a_control_to_a_control_set.png :scale: 50 % .. note:: If you only need to create controls dynamically at runtime, this :doc:`can be done without the use of a Control Set`. You can also use a :doc:`DesktopContainer` or :doc:`WebContainer` as an alternative to Control Sets. They cannot be configured as a Control Set, although you can use Control Sets within a Container. Once you have a Control Set you can refer to the individual controls by their index value. You will need to manually keep track of the total number of controls in your set for looping purposes. For example, if you have created a control set that consists of four TextFields, you can create a loop to update the text in each of the fields: .. code:: xojo Var lastSetIndex As Integer = 3 For i As Integer = 0 To lastSetIndex MyTextFieldSet(i).Text = "Field " + i.ToString Next .. _/topics/user_interface/desktop/control_sets/sharing_event_handlers: Sharing event handlers ---------------------- If you have multiple controls on the window that are part of a Control Set, the controls share their events. However, each event now has a new index parameter that you use to identify a specific control. Controls Sets are a handy way to manage multiple controls that share similar functionality. You can use the index parameter to iterate through the controls. For example, if you have a Control Set called MyCheckBox that consists of 4 Check Boxes, you could loop through them like this in a button's Pressed event to see which ones where checked: .. code:: xojo For i As Integer = 0 To 3 If MyCheckBox(i).Value Then MessageBox(MyCheckBox(i).Caption + " is selected") End If Next .. _/topics/user_interface/desktop/control_sets/creating_new_controls_in_a_control_set_via_code: Creating new controls in a Control Set via code ----------------------------------------------- There may be situations where you can't build the entire interface ahead of time and need to create some or all of the interface elements programmatically. This can be done provided that the window already contains a control of the type you wish to create and that control belongs to a Control Set. In this case, the existing control is used as a template. For example, if you wish to create a Button via code, there must already be a Button on the window that you can “clone.” Remember that controls can be made invisible, so there is no need for your template control to be visible to the user. Once you have created a new instance of the control, you can then change any of its properties. .. youtube:: cje6etv42_Y Suppose you need to clone a Button that is already in the window, named Button1. To create a new Button control via code, do this: 1. In the Inspector for the Button, select New Control Set from the Member Of popup menu in the Control Set group. When the new controls are created while the app is running, they become additional elements of this Control Set. In this example, you will create a new Button when the user clicks on the original Button. 2. In the Pressed event of Button1, Var a variable of type DesktopButton, which is the base class for a Button. Assign the variable a reference to a new control using the New operator and use the name of the Control Set. This example shows a new Button being created using the Button1 Control Set. When the new control is created, it is moved to the right of the template control: .. code:: xojo Var b As DesktopButton ' this is the control class b = New Button1 ' create clone of the control on the layout b.Caption = "Clone" b.Left = Me.Left + Me.Width + 10 ' reposition it 3. Click the Run button. When the app launches, click the *original* button. This creates a new Button to the right of the original. If you click *clone*, you will create another clone to the right of the first two, and so on. Since any new control you create in this manner shares the same event handler code as the template control, you may need to differentiate between them in your code. You use the index parameter of the control that is (passed in to the event handler to identify the control being used. If your code needs to create different kinds of controls and store the reference to the new control in one variable, you can declare the variable as being of the type of object that all the possible controls you might be creating have in common (e.g, the super class or an interface). For example, if a variable can contain a reference to a new CheckBox, the variable can be declared, for example, as a :doc:`DesktopUIControl` because :doc:`CheckBoxes` inherit from the :doc:`DesktopUIControl` class. The same concept applies to `Web` controls as well. Keep in mind, however, since the variable is a :doc:`DesktopUIControl`, the properties specific to a :doc:`DesktopCheckBox` will not be accessible without casting. Here is the preceding example using `DesktopUIControl` and casting: .. code:: xojo Var c As DesktopUIControl c = New Button1 ' create clone DesktopButton(c).Caption = "Clone" DesktopButton(c).Left = Me.Left + Me.Width + 10 .. _/topics/user_interface/desktop/control_sets/removing_controls_from_a_control_set_via_code: Removing controls from a Control Set via code --------------------------------------------- To remove a control that you added at run-time, you call the Close method for the control. This code can be used to remove the button created in the example above: .. code:: xojo c.Close Remember, because Control Sets are not arrays if you remove a control in the middle of the Control Set you will get a *hole* in the index values. For example if you have four items in a Control Set they will initially have index values of 0, 1, 2, 3. If you remove index 2 then you will have three items in the Control Set with index values of 0, 1, 3. Index value 2 will be :doc:`Nil`. .. _/topics/user_interface/desktop/control_sets/using_control_sets_with_menus: Using Control Sets with desktop menus ------------------------------------- Control Sets can be used to allow a group of static menu items to share an event handler. To dynamically create menus it's better that you :ref:`add to or remove items from menus at runtime` using the :ref:`AddMenu`, :ref:`AddMenuAt` and :ref:`RemoveMenuAt` methods of the :doc:`DesktopMenuItem` class. .. seealso:: * :doc:`DesktopUIControl` class; :doc:`WebUIControl` class * Examples/Platforms/Desktop/Control Sets ==================== Supporting Dark Mode ==================== Xojo supports Dark Mode on versions of iOS, Linux, macOS, Web and Windows that support it. To enable Dark Mode support for your app, turn the *Supports Dark Mode* property on the Shared Build Settings. For new projects, *Supports Dark Mode* defaults to ON. For existing projects, it defaults to OFF. .. image:: https://documentation.xojo.com/topics/user_interface/images/supporting_dark_mode_supports_dark_mode_shared_build_setting.png .. _/topics/user_interface/supporting_dark_mode/default_colors: Default colors -------------- There are some subtle changes to how default colors work. * Controls whose text defaults to Solid Black (&c00000000) will automatically use the system's Text Color (except Label, see below), which changes depending on Light or Dark mode. * Controls whose background color defaults to Solid White (&cFFFFFF00) will automatically use the system's Text Background Color, which changes depending on Light or Dark Mode. * When painting to a Graphics context (such as in :ref:`Canvas`), :ref:`Color` returns the system Text Color, :ref:`Color` returns the system's Window Fill Color and :ref:`Color` now uses the system's Control Color. * The :doc:`DesktopLabel` control TextColor property defaults to Solid Black (&c000000) and as a result will automatically use the system's Label Color so they automatically match the captions on Checkboxes and Radio buttons and change depending on light/dark mode. * If you do not want Xojo to automatically swap these colors for you, instead choose an Off-White or Off-Black color, such as &c00000001 or &cFFFFFF01. These will be indistinguishable from Solid Black and Solid White and will be ignored by Xojo. .. _/topics/user_interface/supporting_dark_mode/methods_and_events: Methods and events ------------------ There is a new global method called :ref:`Color` which returns :doc:`True` if the system is running in Dark Mode and the app has Supports Dark Mode set to ON. It returns False when Supports Dark Mode is set to OFF. Use this to detect Dark Mode for situations when you have to manually adjust colors, graphics or icons yourself. The :ref:`DesktopApplication` is called when the user switches between light and dark modes or when the accent color changes. You can use this to tell your UI to update or redraw as necessary. .. _/topics/user_interface/supporting_dark_mode/control_differences: Control differences ------------------- Due to changes made by Apple, controls on macOS dark mode are translucent. Because of this it is recommended you avoid overlapping controls in your layouts. Because of the translucent controls you may find redrawing occurs differently in some situations. If you are not seeing redraws when you expect, try calling the Refresh method instead of Invalidate. .. _/topics/user_interface/supporting_dark_mode/window_backgrounds: Window backgrounds ------------------ Windows with custom background colors do not automatically change the color for Dark Mode. You will have to adjust the color in your code when :ref:`Color.IsDarkMode` returns :doc:`True`. .. _/topics/user_interface/supporting_dark_mode/see_also: .. seealso:: * :ref:`DesktopApplication` event; :ref:`Color.IsDarkMode`, :ref:`Color.FillColor`, :ref:`Color.FrameColor`, :ref:`Color.TextColor` methods; :doc:`AppSupportsDarkMode` constant; :doc:`ColorGroup` class; :doc:`Supporting Dark Mode and More with Color Groups` topic Web === .. toctree:: :maxdepth: 1 :name: sec-web Changing the Appearance of Controls Control Hierarchy Controllers Dialog Boxes Menus Using Google Fonts Web Pages =================================== Changing the appearance of controls =================================== The appearance of controls in a web app can be changed in two ways: for all controls in the project at once or for individual controls. .. _/topics/user_interface/web/changing_the_appearance_of_controls/changing_all_controls: Changing all controls --------------------- Changing the appearance of all controls at once is done through the project's theme. Each Xojo web project has a theme which controls everything about the appearance of controls from size to font to colors, even some behavior. Xojo has a default theme but you can substitute other themes as well. Under the hood, Xojo uses as web technology called `Bootstrap `_ and thus the themes you can use in Xojo must be design for use with `Bootstrap `_. `Bootswatch `_ is a website that provides many free Bootstrap themes that are Xojo-compatible. On the `Bootswatch `_ website, clicking the Preview button for any theme will give you an idea of just how many different aspects web control appearance are governed by themes. Once you find a theme you like, click on the menu connected to the theme's download button and select the file titled *bootstrap.min.css*. That is the only file you need. To use it in your project, drop it into the project in the Navigator. The layout editor itself will even take on the theme as well. There are many free themes available on the Internet. Some are more compatible with Xojo than others at the moment but those at `Bootswatch `_ are very compatible. If the look of your web application needs to match a website, changing the theme is an easy way to do that. In fact, many websites use Bootstrap and can provide the Bootstrap theme file they are using. Because themes can affect the size of controls, changing themes after you have laid out controls may require some adjustments. .. _/topics/user_interface/web/changing_the_appearance_of_controls/theme_samples: Theme samples ************* Here are some of the controls with the default theme: .. image:: https://documentation.xojo.com/topics/user_interface/web/images/changing_the_appearance_of_controls_web_controls_with_default_theme.png Here are the same controls with the Darkly theme: .. image:: https://documentation.xojo.com/topics/user_interface/web/images/changing_the_appearance_of_controls_web_controls_with_darkly_theme.png Here are the same controls with the Pulse theme: .. image:: https://documentation.xojo.com/topics/user_interface/web/images/changing_the_appearance_of_controls_web_controls_with_pulse_theme.png .. _/topics/user_interface/web/changing_the_appearance_of_controls/creating_your_own_theme: Creating your own theme *********************** You can create your own themes as well. `Bootstrap Magic `_ is one example of a free online editor. .. _/topics/user_interface/web/changing_the_appearance_of_controls/changing_individual_controls: Changing individual controls ---------------------------- Changing the appearance of individual controls is done through their Style property. This property allows you to change appearance properties such as ForegroundColor, BackgroundColor, BorderColor, BorderThickness, font, font size, font styles and opacity. All this is done in code. .. _/topics/user_interface/web/changing_the_appearance_of_controls/example: Example ******* The example below, the first :doc:`WebTextField` is unchanged. Via changing it's Style property, the second :doc:`WebTextField` has the Opacity set to 80 (out of 100), the border thickness set to 3 and the border color set to :ref:`Color`: .. image:: https://documentation.xojo.com/topics/user_interface/web/images/changing_the_appearance_of_controls_webstyle_example.png The code for this (in this case in the :ref:`WebTextField.Opening` event, is straightforward: .. code:: xojo Me.Style.Opacity = 80 Me.Style.BorderThickness = 3 Me.Style.BorderColor = Color.Blue Changing a control's :doc:`style` can be a great way to indicate errors. For example, a the user has forgotten to fill in a mandatory field, setting its border color to red helps make clear which fields need to be completed: .. image:: https://documentation.xojo.com/topics/user_interface/web/images/changing_the_appearance_of_controls_webstyle_example_-_blank_mandatory_field.png You can also change the look of individual controls by assigning `Bootstrap CSS classes `_ at design time via the advanced panel of the Inspector or at runtime via the :ref:`CSSClasses` method. .. image:: https://documentation.xojo.com/topics/user_interface/web/images/web_inspector_cssclasses.png For example, assigning ``btn-close rounded-circle`` to the CSSClasses designtime-only property in the Inspector will make the button have a circular background when the mouse is hovering over it. .. image:: https://documentation.xojo.com/topics/user_interface/web/images/css_example.png .. _/topics/user_interface/web/changing_the_appearance_of_controls/see_also: .. seealso:: :doc:`WebStyle` and :doc:`WebCSSClasses` classes, :ref:`CSSClasses` method ================= Control hierarchy ================= The built-in web controls have an inheritance hierarchy. .. _/topics/user_interface/web/control_hierarchy/object: Object ------ :doc:`Object` is the base class for all webpages, dialogs and controls. .. _/topics/user_interface/web/control_hierarchy/webcontrol: WebControl ---------- :doc:`WebControl` is a subclass of Object and is the base class for all web controls including those with and without a user interface. .. _/topics/user_interface/web/control_hierarchy/webuicontrol: WebUIControl ------------ :doc:`WebUIControl` is a subclass of :doc:`WebControl` and is the base class for all web controls that have a user interface such as :doc:`WebButton`, :doc:`WebCheckBox`, and others. .. _/topics/user_interface/web/control_hierarchy/webview: WebView ------- :doc:`WebUIControl` is a subclass of :doc:`WebControl` and is the base class for things that can contain other controls including :doc:`WebPage`, :doc:`WebDialog`, :doc:`WebContainer`, :doc:`WebPagePanel` (and :doc:`WebTabPanel` because it's a subclass of :doc:`WebPagePanel`) and :doc:`WebRectangle`. .. _/topics/user_interface/web/control_hierarchy/see_also: .. seealso:: :doc:`Web Pages` topic =========== Controllers =========== These are the controls and classes in the Controllers group of the Library. The controls in this section are all non-visual. They do not display on the Web Page in the built app and the user cannot see or interact with them. When added to a Layout, they appear on the Shelf area. Adding these controls to the Layout is merely a convenience to give you easy access to their event handlers. Although you can also create these controls in using the New operator you will not have access to the event handlers unless you first subclass the control (and access the event handlers there) or use the AddHandler command to map event handlers to methods on the web page. You can also add just about any non-visual class to a layout by dragging the Object control onto the layout and changing its Super to the class you want. .. _/topics/user_interface/web/controllers/shell: Shell ----- Provides a quick way to run Unix or DOS shell commands. .. _/topics/user_interface/web/controllers/shell/see_also: See also ******** :doc:`Shell` class .. _/topics/user_interface/web/controllers/serialconnection: SerialConnection ---------------- Although the SerialConnection control displays an icon when placed in a window in the Window Editor, it is not visible in the built application. It is designed only for executing code to communicate via the serial port. For more information, refer to the :doc:`Communicating with Serial Devices` topic. .. _/topics/user_interface/web/controllers/serialconnection/see_also: See also ******** :doc:`SerialConnection` class .. _/topics/user_interface/web/controllers/object: Object ------ Use this generic object to add any class to the layout so it can work as a non-visual control, giving you easy access to all its event handlers. Drag the Object to the layout and then change its Super property to your actual class name. .. _/topics/user_interface/web/controllers/xojoscript: XojoScript ---------- The XojoScript control allows the end user to write and execute Xojo code within a compiled app. Scripts are compiled into machine code. You pass the code that you want to run via the Source property and execute it by issuing the Run method. .. _/topics/user_interface/web/controllers/see_also: .. seealso:: :doc:`XojoScript` class ============ Dialog Boxes ============ There are two ways to create dialog boxes in web applications. You can use the simple MessageBox command to display a simple dialog box or you can create a full-featured dialog box by creating a WebDialog. .. _/topics/user_interface/web/dialog_boxes/messagebox: MessageBox ---------- MessageBox can only be used to display a simple text message with a single button. .. code:: xojo MessageBox("File transfer complete!") This line of code displays a message box with the message and one button that the user can click to dismiss the dialog. The MessageBox command does not cause your code to wait until the MessageBox is closed. Your code continues running to the end of the method. .. _/topics/user_interface/web/dialog_boxes/web_dialog: Web dialog ---------- Most of the time you will need a more advanced dialog box, perhaps with additional controls, or a more sophisticated layout than what MessageBox offers. To do this, you add a Web Dialog to your project, layout its design and add it to a web page. On the dialog, you add the controls for the layout you need. In particular, remember to add a button that dismisses the dialog by calling the Close method. Your code does not pause and wait for the dialog to be closed. Instead the code in your method (or event) continues to the end. The Dismissed event (shown below) is called when the dialog is closed. Below is a list of commonly used events, properties and methods. Refer to :doc:`WebDialog` for the complete list. .. _/topics/user_interface/web/dialog_boxes/events: Events ****** :ref:`Dismissed` - The Dismissed event is called when the dialog closes by calling its Hide or Show methods. It is also called when the close button on the title bar is clicked for palette dialogs. .. _/topics/user_interface/web/dialog_boxes/methods: Methods ******* :ref:`Close` - Call the Close method in a web dialog dismisses the dialog and calls its Dismissed event handler. :ref:`Show` - Displays the web dialog. Remember, web dialogs do not cause your code to wait until the dialog is closed. .. _/topics/user_interface/web/dialog_boxes/usage: Usage ***** Here is how you can add a dialog to a web page: 1. Create a new Web Dialog called “TestDialog” using the Insert button on the toolbar or the Insert menu. Or you can drag a “Modal Dialog” from the Library. #. Add two buttons to TestDialog. Name the buttons “OKButton” and “CancelButton” and change their captions to “OK” and “Cancel” respectively. #. Add a public property called SelectedButton As WebButton. #. In the Pressed event for each button, add this code: .. code:: xojo SelectedButton = Me Self.Close 1. Now you can add the dialog to the web page. Drag the dialog from the Navigator to the default web page. The dialog appears in the shelf at the bottom of the Layout Editor and it is called TestDialog1. #. Double-click on TestDialog1 to add an event handler. Choose the Dismissed event from the list and click OK. #. In the code editor add this code: .. code:: xojo Select Case Me.SelectedButton Case Me.OKButton MessageBox("OK pressed.") Case Me.CancelButton MessageBox("Cancel pressed.") End Select 1. Add a Button to the web page and name it “DialogButton” with a caption of “Test”. #. Double-click DialogButton and add the Pressed event handler. Add this code: .. code:: xojo TestDialog1.Show What you have created is a simple dialog that gets displayed when you click the Test button on the web page. When you click either OK or Cancel on the dialog, the SelectedButton property gets set to the button that was pressed (referred to by :doc:`Me`) and the dialog is closed. This calls the Dismissed event handler, where your code checks the SelectedButton property of the dialog (that you just set) and displays a message telling you which button was clicked. .. _/topics/user_interface/web/dialog_boxes/see_also: .. seealso:: :doc:`WebDialog` class; :doc:`MessageBox` method ===== Menus ===== A web app cannot have a menu bar like a desktop app, but it can have menus. Two places that menus are used are in Contextual menus for controls and for Menu Buttons on Toolbars. In both cases, you use the :doc:`WebMenuItem` class to create your menu. Below is a list of commonly used properties and methods for a Web Menu. Refer to :doc:`WebMenuItem` for the complete list. .. _/topics/user_interface/web/menus/properties: Properties ---------- :ref:`Tag` - A :doc:`Variant` value that is associated with the specific menu. :ref:`Value` - The text that is displayed for the menu. .. _/topics/user_interface/web/menus/methods: Methods ------- :doc:`Constructor` - Use the Constructor to quickly create a menu with its text. :ref:`AddMenuItem` - Adds a menu to a menu. .. _/topics/user_interface/web/menus/usage: Usage ----- To add a contextual menu to a Button, you create the menu in the Shown event handler for the control and assign it to the ContextualMenu property of the Button: .. code:: xojo Var menu As New WebMenuitem menu.AddMenuItem("Item 1") menu.AddMenuItem("Item 2") Me.ContextualMenu = menu The user can now right-click on the button to show the menu. The :doc:`ContextualMenuAction` event is available for all web controls. To determine which menu was selected, use this code in the ContextualMenuSelected event handler of the Button: .. code:: xojo Select Case hitItem.Caption Case "Item 1" MessageBox("Item 1 selected.") Case "Item 2" MessageBox("Item 2 selected.") End Select You can also created submenus by adding them to an existing menu. This code creates a Dark Wizards menu and adds Sauron and Saruman menus to it: .. code:: xojo Var myMenu As New WebMenuItem Var menuItem1 As New WebMenuItem("Dark Wizards") Var subMenuItem1 As New WebMenuItem("Sauron") Var subMenuItem2 As New WebMenuItem("Saruman") menuItem1.AddMenuItem(subMenuItem1) myMenu.AddMenuitem(menuItem1) Me.ContextualMenu = myMenu .. _/topics/user_interface/web/menus/see_also: .. seealso:: :doc:`WebMenuItem` class; :doc:`Toolbar` topic ================== Using Google fonts ================== You can use `Google Fonts `_ in your web app to make it look snazzier. There are hundreds of Google Fonts available to choose from in all kinds of styles. .. image:: https://documentation.xojo.com/topics/user_interface/web/images/using_google_fonts_google_font_in_web_app.png Here are the steps to use a Google Font in your web app. 1. Go to `fonts.google.com `_ and click on the name of the font to go to its page. As an example font to try, search for the font called "Orbitron" (use the search field in the top right) and then click it to display its font page. #. On the Orbitron page, click "+ Select this style" to the right of the font style you want to use. 3. In the upper-right corner of the page, click on this: |images/using_google_fonts__googleselectedfontsicon.png_| .. |images/using_google_fonts__googleselectedfontsicon.png_| image:: images/using_google_fonts__googleselectedfontsicon.png_ :scale: 50 % 4. In the pane that opens from the right, click Embed. #. Copy the contents of the box that begins with "** tags to the editor. #. In between the style tags, add a period followed by the name of the Google Font and two curly brackets. For Orbitron it looks like this: .. code:: CSS 1. Back on the Google Fonts page, copy to the clipboard the text in the second box under "CSS rules". For Orbitron it looks like this: .. code:: CSS font-family: 'Orbitron', sans-serif; 1. Go back to Xojo and in the HTMLHeader property editor, add this CSS in between the curly brackets. #. You should now have text in the editor that looks like this: .. code:: HTML 12. Now for any control in your project, you can assign "Orbitron" to the FontName property to use it. When you run, the text will appear using Google's Orbitron font. To use multiple Google Fonts in your app, add additional HTML to the HTMLHeader as described above for each font you want to use and create the corresponding Web Style. Note that the Google Font only shows when you run the project. It does not show in the IDE Layout Editor. And remember, Google Fonts are always loaded from the Internet, not locally, which may cause your web app to load more slowly as the font is fetched. .. _/topics/user_interface/web/using_google_fonts/see_also: .. seealso:: :doc:`WebStyle` class ========= Web Pages ========= A Web app's user interface exists in your projects's web pages. You create your user interface by creating web pages and adding controls such as Buttons and Check Boxes. By default, a Web Application project has one web page (WebPage1) that is displayed automatically when the app runs. Typically, you will begin designing your app's interface by adding controls to this web page and enabling the controls by writing code. To add additional web pages to an application: * Add a new web page to the project by clicking the Insert button on the toolbar or menu and selecting Web Page. * Add controls to the web page from the Library. * Add code as needed. * Add code to display the web page in the finished application. Web Pages have a :doc:`hierarchy`. Commonly used events, properties and methods are described below. Refer to :doc:`WebPage` in the Language Reference to a complete list of events, properties and methods. .. _/topics/user_interface/web/web_pages/web_page: Web Page -------- .. _/topics/user_interface/web/web_pages/events: Events ****** **Closed** - The web page is closing. **ContextualMenuSelected** - Called when an item in a ContextualMenu was selected. **Hidden** - Called when the control is about to be hidden because another page is about to be shown. **Opening** - Called when the web page has been created but before it is displayed. Unlike with desktop projects, you should instead use the Shown event to initialize the web page or any of its controls. **Resized** - Called after the browser page is resized. **Shown** - Use the Shown event instead of the Opening event to initialize the web page or any of its controls. .. _/topics/user_interface/web/web_pages/properties: Properties ********** **ContextualMenu** - Assign a WebMenuItem to display the menu when the user contextual-clicks on the page. **Enabled** - When False, disables all controls on the page. **Tooltip** - Set to the text you want displayed in a Tooltip window when the mouse is hovered over the page. **ImplicitInstance**, **IsImplicitInstance** - When ImplicitInstance is True, you can refer to the web page by its name (instead of having to declare an explicit instance using New). IsImplicitInstance allows you to check if the page was created implicitly. **Height**, **Width** - Used to get the height and width of the page. **MinHeight**, **MinWidth** - If the browser size is set smaller than the minimum width or height, scroll bars appear so that you can still see the content. **Name** - The name of the web page. **Style** - Assign a Style to this property to change the look and feel of the web page (such as its background color). **Title** - The text that appears in the Title Bar of the web browser. **Visible** - Hides all the contents of the page. .. _/topics/user_interface/web/web_pages/methods: Methods ******* **Close** - Closes the page. **ScrollTo** - Scrolls the page to the specified coordinates. This affects scrolling for all subsequently displayed pages as well. **Show** - Displays the page. If ImplicitInstantiation is True, then you can show a page by using WebPageName.Show. .. _/topics/user_interface/web/web_pages/see_also: .. seealso:: :doc:`Control Hierarchy` topic ===================== Windows UI guidelines ===================== Starting with Xojo 2018r1, the Windows framework has had a significant overhaul to reduce flicker inherent to native Win32 controls. This page contains tips on how you can update your existing projects to improve UI performance on Windows. .. _/topics/user_interface/windows_ui_guidelines/transparent_property_for_controls: Transparent property for controls --------------------------------- UI controls now have a Transparent property which determines whether the control is transparent on Microsoft Windows. The default is False for new controls added to layouts. For existing projects and controls, this property is set to True for maximum compatibility. However, controls that have Transparent set to True require more drawing, use more memory and are thus slower. For best results you should set the Transparent property to False for as many controls as possible. If you are going to place a control on top of another control, it is recommended that you only do so on top of :doc:`DesktopWindow`, :doc:`DesktopCanvas`, :doc:`DesktopRectangle`, :doc:`DesktopOval` and :doc:`DesktopContainer`. Controls should not be partially overlapping but always be placed completely within the bounds of these controls. Placement in any other way or on any other control will result in transparency being unsupported. .. _/topics/user_interface/windows_ui_guidelines/desktopcontainer.composited_property: DesktopContainer.Composited property ------------------------------------ Set this property to True to reduce flicker on Microsoft Windows when the :doc:`DesktopContainer` is scrolled. This will cause slower rendering. Otherwise you can leave this to False. .. _/topics/user_interface/windows_ui_guidelines/avoid_overlapping_controls: Avoid overlapping controls -------------------------- The easiest thing you can do to prevent flickering is to not overlap any controls. Overlapped controls result in more requests to redraw the controls which results in flickering. .. _/topics/user_interface/windows_ui_guidelines/use_a_canvas: Use a Canvas ------------ For best results, display any graphics using the Paint event of a :doc:`DesktopCanvas` control. Stay away from using the :ref:`DesktopWindow.Backdrop` property, the :ref:`DesktopWindow.Paint` event, the :ref:`DesktopCanvas.Backdrop` property or the :doc:`DesktopImageViewer` control. Although those techniques work fine in certain situations, they often lead to improper drawing or slower performance in more complex window layouts. With these tweaks, you can do all your drawing in the Paint event using the supplied graphics object parameter: g. .. note:: Do not do any drawing directly to the old Canvas.Graphics or Windows.Graphics properties. This ability was deprecated in 2011 and has been removed starting with 2018r3. You can have separate methods that update the graphics, but they need to be called from the Paint event with the graphics object supplied to the methods as a parameter. Another technique is to have a Picture property that you use to draw you graphics to and then in the Paint event handler you draw the Picture to the Canvas to display it. When you want the Canvas to update itself, redrawing any changes to your graphics, you call the Refresh method: .. code:: xojo Canvas1.Refresh or .. code:: xojo Canvas1.Refresh(True) Refresh tells the Canvas to update itself when it gets a redraw request from the operating system. Passing :doc:`True` to the Refresh method tells the Canvas to update itself immediately. Normally you want to use Refresh(False) as it results in fewer draw requests, improving performance. By using these techniques, you can create stable, flicker-free graphics in your Windows applications. .. _/topics/user_interface/windows_ui_guidelines/remove_or_update_older_code: Remove or update older code --------------------------- If you previously had specialized code in your app to help minimize Windows flicker, you should try removing it as the code may now prove to be unnecessary and cause worse performance. .. _/topics/user_interface/windows_ui_guidelines/freeze_drawing: Freeze drawing -------------- In certain situations you may find it helpful to force screen redrawing to happen all at once, rather than a portion of the screen at a time as is the Windows default. You can use this code to manage that: .. code:: xojo Const WM_SETREDRAW = 11 Declare Sub SendMessage Lib "User32" Alias "SendMessageW" _ (hwnd As Integer, msg As UInt32, wParam As Integer, lParam As Integer) ' Prevent redraw SendMessage(pagePanel1.Handle, WM_SETREDRAW, 0, 0) ' Switch pages ' Restore redrawing SendMessage(pagePanel1.Handle, WM_SETREDRAW, 1, 0) ' After that you'll have to refresh the contents, PagePanel1.Refresh may not be enough, ' if not then you can use this code ' Quickly mark all children for repainting Const RDW_INVALIDATE = 1 Const RDW_ALLCHILDREN = &h80 Const RDW_UPDATENOW = &h100 Declare Function RedrawWindow Lib "User32" _ (hwnd As Integer, updateRect As Ptr, updateRgn As Ptr, flags As UInt32) As Boolean Call RedrawWindow(pagePanel1.Handle, Nil, Nil, RDW_INVALIDATE + RDW_ALLCHILDREN + RDW_UPDATENOW) .. _/topics/user_interface/windows_ui_guidelines/virtual_machines: Virtual machines ---------------- Many people run Windows in Virtual Machines for testing. If you do so you'll want to ensure that you have the best performance possible. Xojo apps use Direct2D for all screen drawing and not all VM software has properly accelerated graphics drivers for this. In particular, VMware Fusion has rather poor performance when "Accelerate 3D Graphics" is enabled in the Display preferences. For best performance you should make sure that option is turned off. Alternatively, Parallels Desktop and VirtualBox both seem to have better graphics performance with Direct2D and Xojo apps. .. _/topics/user_interface/windows_ui_guidelines/winapilib: WinAPILib --------- The `open-source WinAPILib project `_ provides Declares to Windows-specific features and might be worth considering for your Windows projects. .. youtube:: yXRW-GRVLe0 Web === .. toctree:: :maxdepth: 1 :name: sec-web HTTP communication Porting Desktop apps to Web apps Pushing data to the browser Secure web login screens SSL for Web apps Reconnecting to a dropped web session Run Web apps in the background Web app optimization Web app security Getting started --------------- * :doc:`QuickStart` * :doc:`Tutorial` See also -------- :doc:`User interface topics` ================== HTTP communication ================== Web communication is done using HTTP (HyperText Transfer Protocol). To communicate on the web with HTTP you can use the :doc:`URLConnection` class, which has methods and properties that enable you to do all types of web communication such as retrieving the contents at a URL or posting to a form. .. _/topics/web/http_communication/getting_web_content: Getting web content ------------------- You can send a web request to a URL to get back content. This content could be the actual content of a web page if you URL is a web page or it could be binary content if your URL is to a file or it could be data or some form if your URL points to a web service API call. To send these web requests you use the :doc:`URLConnection` class. ================================ Porting Desktop apps to Web apps ================================ To create a web version of an existing desktop app, there are several things to consider. Obviously, you cannot use your project exactly as it is as desktop apps and web apps are different project types. Since you cannot change the type of a project, you will need to create a new web app project in order to create a web app. But with an appropriate design and understanding of the UI differences, you will find that you can create a web app easily, re-using (or even sharing) a significant amount of non-UI code. .. _/topics/web/porting_desktop_apps_to_web_apps/user_interface: User interface -------------- .. image:: https://documentation.xojo.com/topics/web/images/porting_desktop_apps_to_web_apps_eddie's_electronics_desktop_window_layout.png The user interface for a web app is completely different than the user interface for a desktop app. Not all of the desktop controls have equivalent web controls (:doc:`DesktopGroupBox`, for example) and not all the features of the desktop controls are available in web controls (such as :doc:`DesktopListBox` vs :doc:`WebListBox`). There are also web controls that do not have an equivalent desktop control (for example, :doc:`WebMapViewer`). In addition, web apps do not have a concept of a Menu Bar, which is something that almost every desktop app uses, so you'll want to re-think how you present this information to the user. Because of these differences, you are going to need to completely re-implement your existing desktop user interface using web app controls. Keeping your UI and business logic code separate can make this transition easier. .. _/topics/web/porting_desktop_apps_to_web_apps/web_pages_replace_windows: Web pages replace windows ************************* Generally speaking, each window in a desktop app can be designed as a web page in a web app. You use the Show method to display different pages based on user actions, similar to how you might show additional windows. Keep in mind that a web app can only show a single page at one time. If your desktop app relied on having multiple visible windows, then you will need to rethink that design and come up with an alternative. In most cases your layouts should not change much. .. _/topics/web/porting_desktop_apps_to_web_apps/dialogs: Dialogs ******* .. image:: https://documentation.xojo.com/topics/web/images/porting_desktop_apps_to_web_apps_eddie's_electronics_web_page_layout.png Dialogs in desktop apps can use the :doc:`MessageDialog` class or be modal windows. In web apps, those options are not available. To create a dialog in a web app, you instead add a :doc:`WebDialog` to your project and add your layout to it. Then you add this web dialog to the page or pages on which it should appear and call it using Show where appropriate. When the dialog is closed, its Dismissed event handler is called where you can determine what action to take. Unlike desktop app dialogs, Web dialogs are not modal. After you call Show to display the dialog, the rest of the code in your method runs without waiting for the dialog to close. .. _/topics/web/porting_desktop_apps_to_web_apps/shown_event_handler: Shown event handler ******************* In desktop apps, you often use the Opening event handler to do initial setup of your controls or windows. In web apps, you should instead use the Shown event handler. .. _/topics/web/porting_desktop_apps_to_web_apps/control_appearance: Control appearance ****************** In desktop apps, you can modify the style of a control by changing properties on it for color, font, etc. With web apps, you can do this as well. In some cases the control will have specific properties available and in others you will use the control's :doc:`Style` property. Web projects use :doc:`Bootstrap themes` to provide the overall appearance of controls. .. _/topics/web/porting_desktop_apps_to_web_apps/multiple_users: Multiple users -------------- One fundamental difference between desktop and web apps is that desktop apps are designed to be used by a single user at a time while web apps are designed to be used by many users at a time. Sessions and Cookies can help with this. .. _/topics/web/porting_desktop_apps_to_web_apps/latency: Latency ------- With a web app, your Xojo code runs on the server where the web app is installed and not in the user's web browser. This means that there is a delay, slight in some cases and more significant in others, between when an action is taken on the browser, when the Xojo code runs on the server and then when the result of that code appears back on the browser. It's what is called “round trip” and it depends on network traffic, how physically far a user is from your app and among other things, how long it takes for your app to respond. The result is that you 're going to have to think asynchronously when designing. .. _/topics/web/porting_desktop_apps_to_web_apps/sessions: Sessions ******** Having to deal with multiple users means you may need to manage global data differently. In desktop apps, public properties and methods on the App object are global to the entire app, which can often be useful. In a web app, the :doc:`App` is also global to the entire app, which means it is globally available to all users of the web app. This is usually not the behavior you want. Instead you want data to be specific to each user (aka Session) that is connected to the web app. So you do not want to store global information that is specific to the current user, such as the UserName that was used to log in, in the App object. Instead, web apps have a concept called a :doc:`WebSession`. Each user that connects to your web app gets its own WebSession in the form of a :doc:`Session` object. Use the Session object to manage global information just for the user. For example, saving the UserName to a property on the Session means that it is only visible to the one user. .. _/topics/web/porting_desktop_apps_to_web_apps/cookies: Cookies ******* Most apps need to save preferences or settings of some kind. With web apps, you can easily do this using Cookies, a web technology that provides a way for a web browser to save settings that can be later requested by the web app. Cookies are part of the :doc:`WebSession`. This code saves the user name in a Cookie: .. code:: xojo Session.Cookies.Set("UserName") = UserNameField.Text .. _/topics/web/porting_desktop_apps_to_web_apps/log_in: Log in ****** If you've saved a cookie containing the UserName as shown above you may want your web app to retrieve it so that it can pre-populate the UserName field on a login form. This code (in the Shown event handler for the page) fetches the saved Cookie and uses it to pre-fill a UserName field: .. code:: xojo UserNameField.Text = Session.Cookies.Value("UserName") .. _/topics/web/porting_desktop_apps_to_web_apps/databases: Databases --------- Database desktop apps often keep a global reference to the database as a property of the App object. With a web app, you should instead use Session to store a property for the database reference. Each user that connects to the web app will then have their own connection to the database so that transactions work properly and so that you can isolate database access to prevent other users ' data from being visible. For example, a desktop app may use the App.Opening event handler to connect to the database once when the app starts and save the connection to a public DB property that is accessed throughout the app as App.DB. In a web app, you would instead do the connection in the :ref:`Session.Opening` event handler and save the connection to a public DB property that is accessed throughout the session as Session.DB. For desktop apps, if you want to allow multiple users to access a database (using multiple installations of the desktop app), then you usually want to use a database server. With web apps, you may find that you do not always need a database server. Because your web app is itself running as a server, :doc:`SQLite` is often more than sufficient for handling light to medium web app loads. .. _/topics/web/porting_desktop_apps_to_web_apps/code_sharing: Code sharing ------------ If you tend to keep most of your code in your user interface objects, then you will not be able to share your code between desktop and web projects. But if you instead separate your code out into classes that are called by your user interface objects, then you can start to share code between web and desktop projects. This is sometimes referred to as Model-View-Controller design. The View is the user interface, either web or desktop. This cannot be shared. The Model is your data. This is shareable. The Controller is the interface between the Model and the View. Using this design in conjunction with conditional compilation allows you to create shared code that works for both desktop and web apps. For details on how to set up projects that share code, refer to :doc:`Sharing Code among multiple projects`. .. _/topics/web/porting_desktop_apps_to_web_apps/circular_references: Circular references ------------------- You 'll need to be careful not to create circular references up and down the :doc:`Session` structure. For instance, Session hierarchy looks like this: * Session * Pages * Controls * Dialogs * Controls It's very important that the lower items don't refer directly to the higher ones (like having a reference to Session in a WebPage). If you do, the Session and it's contents will never be destroyed, even when a user leaves your app. If you need such functionality, you 'll need to use a :doc:`WeakRef`. .. _/topics/web/porting_desktop_apps_to_web_apps/navigator_example: Navigator example ***************** This simple example uses a class to open a new web page or a new window, depending on the type of app. Create a new desktop project and add a new class, called Navigator. In it, add a method called ShowScreen2 with this code: .. code:: xojo #If TargetWeb Then WebPage2.Show #ElseIf TargetDesktop Then Window2.Show #Endif The above code uses conditional compilation to display either WebPage2 or Window2 depending on the type of app. :doc:`TargetWeb` indicates it is a web app and :doc:`TargetDesktop` indicates it is a desktop app. In Window1, add a Button and put this code in its Pressed event handler: .. code:: xojo Var n As New Navigator n.ShowScreen2 Now add a new Window (it should default to Window2 as the name). You should give this window a title that says “Window 2” so you know when it has opened. Run the project and click the button on the default window to see that Window2 opens. Now create a new web project. Add a Button to WebPage1 and add the same code to its Pressed event handler: .. code:: xojo Var n As New Navigator n.ShowScreen2 Now copy the Navigator class from the desktop project to this web project. Lastly, add a second web page, called WebPage2. Give it a title so you know when it displays. Run the project and click the button on the default web page to see that Web Page 2 appears. You have now created a (very simple) class that can be used in either a desktop or web app. This technique can apply to just about anything. Code that refers to web-specific objects or features should be included in “#If TargetWeb” and code that is for desktop apps should use “#If TargetDesktop”. Although you are using the same Navigator class, you are not actually sharing the exact same class between the two projects. Changes made to the Navigator in one project do not affect it in the other project. If you want to share the exact same class so that a change in one project is reflected in another, then you need to use an External Project Item as described in :doc:`Sharing Code among multiple projects`. .. _/topics/web/porting_desktop_apps_to_web_apps/see_also: .. seealso:: :doc:`Web Apps`, :doc:`Considerations when using a database with a web application` topics =========================== Pushing data to the browser =========================== It is possible to have a user interface action on a page send out updates to all the clients that are currently connected to the web app. In order to update your user interface via code, the update must be initiated by a user interface event such as an event of the web page, a button clicked event, etc. This means that if you want to update your user interface using data from the app (for example, data read from a socket or created by a XojoScript and then stored in a property of the application or Session classes), you will need to have a WebTimer control on a web page, container control or dialog, that fires and retrieves that information. The app itself, without a user interface event, cannot push data to the client. .. _/topics/web/pushing_data_to_the_browser/pushing_updates_out_to_all_clients: Pushing updates out to all clients ---------------------------------- If you want to broadcast information out to all clients (browsers) that are connected to your app, you can do so by retaining a reference to the pages you want to update. .. image:: https://documentation.xojo.com/topics/web/images/pushing_data_to_the_browser_web_help_desk_chat_request.png This still needs to be triggered by a user interface event, of course. For example, suppose you have a chat application and would like to broadcast a chat message from one user out to all the users with the chat app open in their browser. .. image:: https://documentation.xojo.com/topics/web/images/pushing_data_to_the_browser_web_help_desk_chat_session.png Each time a ChatPage is opened, you can add it to a global array on the App class. Then when a user sends a message (probably by clicking a send button), you can have the app loop through the chat pages that you have saved in the array and call a method on the page to display the message. .. youtube:: uLkZ1U69nR0 ======================== Secure web login screens ======================== When creating login screens for your web apps, it is important that that the user's login information is protected. By default, a hosted web app cannot transmit information securely. This means that the username or password could be read by anyone watching network traffic. The solution to this is to use SSL to encrypt the traffic between the web browser and the web app. In order to use SSL, you'll need to install a certificate on your web server and set up your app to use it. Refer to :doc:`SSL for Web Apps` for information on how to set up a web app to use SSL. Use :doc:`Introduction to Xojo Cloud` for simple 1-click SSL setup. .. _/topics/web/secure_web_login_screens/designing_the_login_screen: Designing the login screen -------------------------- A typical login screen prompts for a username of some kind (maybe an email address or some other unique identifier) and a password. A good strategy for creating your login screen is to use a modal dialog on an otherwise empty page. This allows you to deal with the complexities of the login process without any other code possibly getting in the way. .. image:: https://documentation.xojo.com/topics/web/images/secure_web_login_screens_weblogindialog.png Create the layout as a modal dialog (LoginDialog) and then add it to a web page (called LoginPage). Drag LoginDialog to the page (name it MyLoginDialog) and in the Shown event for the page you can display the dialog like this: .. code:: xojo MyLoginDialog.Show .. _/topics/web/secure_web_login_screens/displaying_the_login_page: Displaying the login page ------------------------- Because SSL connections are noticeably slower than non-SSL connections (because no caching is allowed, among other things), you likely won't want your entire web application to be using SSL. What you really want is for your app to switch to SSL when it gets to the login screen. If you've created your login page as a web page called LoginPage that displays a web dialog in its Shown event, you can use code like this to check if the connection is secure in the LoginPage.Opening event: .. code:: xojo ' Make sure the user is connecting securely, ' If not have the browser load the application securely If Not Session.Secure Then Var host As String = Session.Header("Host") Var url As String = "https://" + host System.GotoURL(url) End If This reloads your web app and securely takes the user to the login page. To display the login page by default, you have the Session do it. In the :ref:`WebSession.Opening` event add this code which displays the login page whenever a secure connection is made to the web app: .. code:: xojo If Self.Secure Then LoginPage.Show End If .. _/topics/web/secure_web_login_screens/remember_the_user: Remember the user ----------------- If you look at the screenshot above, there is a "Remember Me" checkbox. Typically these are used to remember the UserID to help jog the end-user's memory about what their password might be. This function uses a browser feature called Cookies which stores a snippet of information on the computer where the user is (assuming cookies are enabled, of course). Note: It is best to not remember the user's password because if this box gets checked on a public computer (like a library or internet cafe) the user's account could be compromised. To make the checkbox work, you need two pieces of code in LoginDialog.Shown: .. code:: xojo If Not Session.Cookies.Value("username").IsEmpty Then UserNameField.Text = Session.Cookies.Value("username") RememberMeCheck.Value = True PasswordField.SetFocus End If This code first checks to see if the "username" cookie has been set, and if it has, places the value into the Username TextField, checks the RememberMe checkbox and sets the focus to the Password field (mostly a convenience so the user doesn't need to do that manually). Then create a DoLogin method on LoginDialog that checks the value of the RememberMe checkbox and either saves the username to the cookie or removes the cookie: .. code:: xojo If RememberMeCheck.Value Then Session.Cookies.Set("username", UserNameField.Text) Else Session.Cookies.Remove("username") End If ' Now validate the credentials and if they are OK, you can close the dialog ' and show the main page. If ValidateCredentials Then Self.Close MainPage.Show Else MessageBox("The username/password combination does not match.") End If Lastly, in the Pressed event of the Login button call the DoLogin method: .. code:: xojo DoLogin .. _/topics/web/secure_web_login_screens/example_project: Example project --------------- * Example Projects/Web/SecureLoginExample .. _/topics/web/secure_web_login_screens/see_also: .. seealso:: :doc:`Dialog Boxes`, :doc:`SSL for Web Apps` topics ================ SSL for Web apps ================ You can deploy standalone web apps that take advantage of SSL (URLs that use the https prefix) starting with Xojo 2014r3. Secure standalone web apps use TLS v1.2 by default, which may not be supported by some older versions of Internet Explorer (IE 10 and earlier) unless you enable TLS 1.2 in their Security Settings. If you'd rather not deal with this yourself, :doc:`Xojo Cloud` has built-in SSL support. .. _/topics/web/ssl_for_web_apps/set_up_the_ssl_certificate: Set up the SSL certificate -------------------------- First you'll need an SSL certificate, which you can obtain from many sources. If you're doing local testing, feel free to use a self-signed certificate. If your web app will be available on the internet, please be sure to use a real purchased SSL certificate because it adds extra protection from certain kinds of internet attacks. Generally, a self-signed certificate will have two components (the certificate and key file) while a purchased certificate will likely have three or more (certificate, key, CABundle or intermediates). Once you have a certificate and have downloaded it and the private key to your computer, you need to make a single file, putting all of the pieces together into that file. Create a text file that has the same name as your application with a .crt extension and paste the contents of the certificates into it, one after another in this order: 1. Certificate #. CABundle #. Private Key Make sure to include the entire contents of your primary certificate, intermediate certificate bundle (if required) and private key. Each file must start on a new line (don't concatenate the last line of one file and the first line of another onto the same line). As mentioned, the name of this file should match your web app name, so if your web app is named MyBestApp or MyBestApp.exe, the certificate file should be named MyBestApp.crt. If you are running in debug mode, the built app will be named MyBestApp.Debug, but the certificate should still be named MyBestApp.crt. Now that you have a correctly named and formatted certificate file, you'll need to make sure it is next to the built web app when it runs. A Copy Files Build Automation Step works great for this to make sure it ends up in the right place every time. An example file might look like this: .. code:: plain -----BEGIN CERTIFICATE----- MIICuzCCAiQCCQD+1X0TfzZ2qDANBgkqhkiG9w0BAQUFADCBoTELMAkGA1UEBhMC VUsxDzANBgNVBAgMBkxvbmRvbjEPMA0GA1UEBwwGTG9uZG9uMRkwFwYDVQQKDBBG YWtlIENvbXBhbnkgTExDMQ0wCwYDVQQLDARGYWtlMR0wGwYDVQQDDBRmYWtlZG9t YWluLmxvY2FsLmNvbTEnMCUGCSqGSIb3DQEJARYYZmFrZWFkbWluQGZha2Vkb21h aW4uY29tMB4XDTE0MDExNDE5MTk1NloXDTE1MDExNDE5MTk1NlowgaExCzAJBgNV BAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xDzANBgNVBAcMBkxvbmRvbjEZMBcGA1UE CgwQRmFrZSBDb21wYW55IExMQzENMAsGA1UECwwERmFrZTEdMBsGA1UEAwwUZmFr ZWRvbWFpbi5sb2NhbC5jb20xJzAlBgkqhkiG9w0BCQEWGGZha2VhZG1pbkBmYWtl ZG9tYWluLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA8GJcPMzT/O+v 8x4hoAikz7wQGhLqeNaeu6u0vDaC0lJ25HJ4sTUqNatYzPlBc6cXrDXY1bCpBtSb nKOdAbQgPpz6aHDyDT8P3dDn/U6QxNaQAm15hjvgtsF2AuXV3f/6xVxE2RAs9/eh 3WRAH1G8fcrP86vKwbJnA5bAIEKoFfUCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAd 1/lv9aRGpqOMrNVgBLTWBjznV56n1CyBx/3a2Yk6y9iLtexRyc57MfmmGwpnZ4mt qWvdVK/FJkn9SYsefoUxt59yMBaxHNQMgih+EwoMbWrPCOZI3P997HQHRH4qO6Sp SM3wtzL5PwJ7lczNIdhKdeVZ1ayDG04skIIhqx9J3Q== -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDwYlw8zNP876/zHiGgCKTPvBAaEup41p67q7S8NoLSUnbkcnix NSo1q1jM+UFzpxesNdjVsKkG1Juco50BtCA+nPpocPINPw/d0Of9TpDE1pACbXmG O+C2wXYC5dXd//rFXETZECz396HdZEAfUbx9ys/zq8rBsmcDlsAgQqgV9QIDAQAB AoGAWRf7m8VG5L5pdjA6wjex7hSD20YbhUH2fxQ63m9NuWo7CpyqwvMze8TQGthf O+A4U+l5PmpFm3R9YGb7sD/0mshrMCn0+Oiwn14cHh3LRrANljeHZCCgF386Sye3 GeQkfOF27N+SKkwydLE1utQQWPoSiCmcBH2+IhVdkGYLTEECQQD6FUpcDSIFoEGO fk7LqyIBI352cUTeu7C8aFDiABBiDtBz4ypeK9K+tRkFcng1Pk6JPtPJZEER1a9S 24ktdGu5AkEA9hJTNXLf0wp5eNJ7dbVMNSOaBcU1M4h4oaYcZhR5X6Feo3+nFKmg 2EAU34auPPENW3Tl1ROpkH/LJ2tekzvyHQJBAJ/j7tftuZvZOzDMhrpm3uXVeKxn fP3fCH9dPqFQIyleiV4ell8BK8usY6P87Og1vua22ZeAVq39bgvOuuTp81kCQQCb utU2SlEkushNksfXorlsF+/uHvSgfIn7o6jtYZ++yd2fE7al+QR2V3feTtoOb2/I pZ6ezybM8FOdyvG7tIBxAkAEYS5tAgRSJhy/h4vq5ZpHWaIZ0dMEXxmbq7eAALv5 aCT4ePXIx7xrlKbiWZB0wUBMUBrANhmwCcpM9eE0bKYb -----END RSA PRIVATE KEY----- .. _/topics/web/ssl_for_web_apps/configure_web_server_to_use_ssl: Configure web server to use SSL ------------------------------- The last piece of this puzzle is to tell your web app that you want it to listen on a secure port. You cannot set the Secure Port in the Shared Build Settings, so you'll need to use some command-line parameters to get it to work. These are the parameters that you'll be interested in: .. csv-table:: :header: "Parameter", "Description" :widths: auto "--SecurePort","Specifies the port number for secure connections. You must specify a port with this in order to allow secure connections." "--MaxSecureSockets","Set the maximum number of secure sockets (default=200)." "--Certificate","The path to the SSL certificate if you don't want to put it beside the executable (starting with 2015r2). --certificate=/full/path/to/file" "--SslType","Specifies the type of security used. These integer values can be used: 0 (SSLv2), 1 (SSLv23), 2 (SSLv3), 3 (TLSv1), 4 (TLSv11), 5 (TLSv12, the default)" "--SecureNetworkInterfaceIndex","The index value (of NetworkInterfaces) to use as the NIC for secure connections." The secure port **must** be different than the port selected in the IDE (or specified with the --port command line property). For example, if you built your web application using port 8080 as the listening port, you might use: .. code:: plain MyBestApp --secureport=8081 As with the non-ssl version, you may also increase the maximum allowed number of connected sockets if necessary, which is useful if you notice that your app rejects connections when there are a lot of users connected at the same time. .. code:: plain MyBestApp --secureport=8081 --maxsecuresockets=400 Remember, with the maxsecuresockets option, the number of open sockets does not represent the number of simultaneous users. Depending on the construction of your app, and how your users use it, a browser could have zero open connections (like between keystrokes or mouse clicks) or multiple open connections (if the user is clicking quickly on a control). That being said, you also shouldn't set this value too high. With more open connections comes more memory and CPU use, so you'll need to experiment with this number to find the best threshold for your app/server combination. If you try to start with SSL but the certificate is not found or is not readable, the app will display an error and quit. Once you've launched your app, use a browser to connect to it: .. code:: plain https://127.0.0.1:8081/ Your app should now display the appropriate "secure" indicator in the browser address area. If you also want to prevent unsecured access to the web app, you can set the number of unsecured sockets to 0: .. code:: plain MyWebApp --secureport=8081 --maxsockets=0 By default, SSL standalone web apps use TLSv12 for the connection type. This is the most secure method and is recommended. But if you need to choose an older version for compatibility with older web browsers, you can do so using the command line option like this: .. code:: plain MyWebApp --secureport=8081 --ssltype=3 You can also set the connection type by change the WebApplication.Security.ConnectionType property in your web app's Opening event: .. code:: xojo Self.Security.ConnectionType = WebAppSecurityOptions.ConnectionTypes.TLSv1 .. _/topics/web/ssl_for_web_apps/generating_the_certificate_signing_request: Generating the certificate signing request ------------------------------------------ Here are example commands you can run from the Terminal for Linux/Apache to generate a certificate signing request (CSR): .. code:: xojo # Generate the 2048-Bit private key openssl genrsa -out server.key 2048 # Generate the CSR openssl req -new -key server.key -out server.csr # Remove the passphrase from the key (so you don't have to type it in to restart Apache) cp server.key server.key.org openssl rsa -in server.key.org -out server.key # Generate a self-signed public key (for testing only) openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt .. _/topics/web/ssl_for_web_apps/see_also: .. seealso:: :doc:`WebApplication` class, `Configuring SSL post on Xojo forum `_; `GoGetSSL `_, `Let's Encrypt `_ SSL services ===================================== Reconnecting to a dropped web session ===================================== When the user's connection to a web app drops for any reason, the Xojo web framework displays a message to let the user know the connection has dropped and that it will attempt to reconnect to the server. If it can reconnect, the page the user was on will be redisplayed as if the connection was never lost at all. .. _/topics/web/reconnecting_to_a_dropped_web_session/see_also: .. seealso:: :doc:`WebSession` ============================== Run Web apps in the background ============================== .. _/topics/web/run_web_apps_in_the_background/windows_service: Windows service --------------- To run an app in the background on Windows, you use a Service. Normally only console apps can be used as Service apps, but since web apps are actually subclassed from :doc:`ConsoleApplication` (technically they are subclassed from :doc:`ServiceApplication`, but that is a subclass of ConsoleApplication) they can easily be made to run as a Windows Service. The only tricky part is actually installing it as a Windows Service. Windows has a built-in command for this, sc which standard for "service control". Here are the steps to install your standalone web app as a Windows Service: 1. Build your app as a Standalone Web App and place the app in an accessible location. Be sure to note the port number that you used when building the app. You'll want to use a port number that is not already in use on the system. An example might be 8104. #. Start the Windows Command line app with administrator privileges #. Use the sc command to install the service as follows: .. code:: plain sc create XojoWebSvc type= own start= auto binpath= c:\\Path\\To\\Exe\\WebApp.exe 1. After you press Return you should see: [SC] CreateService SUCCESS #. Now open up Control Panel and go to the Services Manager. It is located in the Administrative Tools section and is called Services. #. Find the service you just created. It will be listed by its app name. Click on it and select Start. That's it. Your web application is now running as a service. To test it, navigate to the URL in your browser using the port you specified for the build: .. code:: plain http://localhost:8104 To stop the service, click on it in Service Manager and click Stop. .. _/topics/web/run_web_apps_in_the_background/linux_daemon: Linux daemon ------------ A daemon is essentially a background process. You can use a daemon easily to deploy your Standalone web applications to remote web servers. Here are steps to create a Standalone web app that can run as a daemon: 1. Add a single line of code to the App.Opening event handler (or App.Run for a Console app): .. code:: plain Call Daemonize 1. Now build the app as Standalone and select a port number that is not in use on the machine. An example might be 8104. Copy the app to an accessible location. #. Open the Terminal and navigate to the location of your app. #. Start the app by typing its name in the Terminal: .. code:: plain ./MyWebApp 1. You will immediately return to the command line because the app is running as a daemon. #. Verify the daemon is running using the ps command: .. code:: plain ps -C MyWebApp That's it. Your application is now running as a daemon. To test it, navigate to the URL in your browser: .. code:: plain http://localhost:8104 If you are remotely connected to a Linux server (using SSH), then disconnecting will also quit an app started in this manner. Instead you'll want to either start he app using the "&" suffix or using the nohup command. ==================== Web app optimization ==================== .. _/topics/web/web_app_optimization/web_app_optimization: Web app optimization ==================== Unlike a desktop app that is on the same computer as the user, a web app could be on a server thousands of miles away from the user. Also, different users are connecting with varying degrees of speed in between. Some may truly be using a state-of-the-art broadband service, while others are making do with less than an optimal mobile connection. The web app running on the server has to send commands to the browser to communicate with the user interface so it is important that you take into consideration how to build your app to optimize for performance on the internet. Here are a few techniques you can use to optimize your web app's performance. .. _/topics/web/web_app_optimization/graphics: Graphics -------- .. _/topics/web/web_app_optimization/file_size_matters: File size matters ***************** Any pictures you use in your project or open and display via code are going to be sent from the server to the user's browser. The bigger those pictures/files are, the longer sending will take and the less responsive your app will be. The smaller the file can be the better. .. _/topics/web/web_app_optimization/load_pictures_separately_and_store_in_properties: Load pictures separately and store in properties ************************************************ Pictures that are part of the project take up space in the app binary and are always loaded into memory, which causes your app to take longer to load. They also can take up up to twice as much memory as the picture when you go to use it because they are converted from Picture to WebPicture. Instead you can load your pictures from the drive as they are needed at runtime and store the picture in a property. Pictures stored in properties of a web app are cached by the web browser so they are sent only once from the app to the browser. As a result, storing pictures as App properties reduces the amount of data that is transmitted between the app on the server and the browser. Storing your pictures in Modules as properties also allows the browser to cache them. A good technique is to use a Computed Property to "lazy load" the picture when it is first used. To do this, add a module to your project and call it "Images". Add a private property called: .. code:: xojo Private mMyPicture As WebPicture Add a protected Computed Property: .. code:: xojo MyPicture As WebPicture Put this code in its Get block (there is no code in the Set block making this a read-only property): .. code:: xojo If mMyPicture Is Nil Then Var picFile As FolderItem = SpecialFolder.Documents.Child("images").Child("MyPicture.png") If picFile <> Nil And picFile.Exists Then Try mMyPicture = Picture.Open(picFile) mMyPicture.Filename = picFile.Name mMyPicture.MIMEType = "image/png" mMyPicture.Session = Nil Catch ioex As IOException ' Opening the picture failed mMyPicture = Nil End Try Else ' Picture does not exist MessageBox("Missing picture: " + picFile.NativePath) End If End If Return mMyPicture Now you can use this property to refer to a picture and it will be loaded only once: .. code:: xojo ImageView1.Picture = Images.MyPicture You can also get the URL like this: .. code:: xojo url = Images.MyPicture.URL .. _/topics/web/web_app_optimization/use_rectangles: Use rectangles ************** Rather than creating and loading pictures for simple backgrounds, use :doc:`Rectangles` when possible. They can be dramatically altered using Styles. For example, by setting the corner radii to 50, you can turn a rectangle into a circle. .. code:: xojo Rectangle1.style.value("border-radius") = "50px" Rectangles are very small in terms of the amount of data that needs to be sent from your app to the browser making them quick to draw. .. _/topics/web/web_app_optimization/image_locations: Image locations *************** Displaying small images (stored in the project) is generally fine. However browsers will cache the images that don't change, so it's important to use a WebPicture whenever possible (as shown above) because they return caching headers. If you are having issues with image loading speed, you will want to consider moving your images out of your app entirely so that they can be delivered by another web server or content delivery network (CDN). This reduces the load on your Xojo web app and with a CDN it can make the delivery of the image quicker because it is closer to where the user is located. .. _/topics/web/web_app_optimization/reduce_latency: Reduce latency -------------- Xojo web apps run in the browser and communicate with an app running on the server. This communication happens over the Internet, which has varying speeds for how long data communication takes that can depend the broadband, congestions, distance and other factors. This is called latency and the best way to minimize it is to limit unnecessary communication between the browser and the server web app. .. _/topics/web/web_app_optimization/remove_unused_event_handlers: Remove unused event handlers **************************** Event handlers cause communication between the browser and the web app on the server even if they have no code in them. For this reason, remove any event handlers that do not have code in them. .. _/topics/web/web_app_optimization/send_large_objects_in_the_background: Send large objects in the background ************************************ If you have large objects you know you will likely need, you can use a Timer or use Push to assign them to properties of the page while the user is busy doing something else. For example, Google Maps sends in the background map segments that are around the area you are viewing in case you scroll the map. You can use this same technique in your app. This allows your app to load and display quickly, while the data is used continues to load in the background. You can also use the :ref:`WebControl.UpdateBrowser` method to force the browser to update instead of waiting or the event or method to end. This is handy when you are making visible page changes in a loop. .. _/topics/web/web_app_optimization/general_tips: General tips ------------ .. _/topics/web/web_app_optimization/webpage_handling: WebPage handling **************** When a :doc:`WebPage` object is sent to the browser, it becomes part of the page that makes up your app. Delivering this page to the browser is typically the most time-consuming operation. After the initial delivery, when you call Show, the browser is told to bring that page to the foreground, make it visible and make all other WebPages invisible. Because the WebPage contents is already part of the browser's web page at this point, this happens quickly. After the page is shown a command is sent back to the app on the server to call the Shown event(s) as appropriate. To properly clean up and remove a WebPage you should call the Close method, but remember that also means that your app will have to deliver the page back to the web browser the next time is is needed. Pages that load slowly are usually the result of very complex designs which in turn mean more memory usage on the browser. Some browsers, mobile in particular, have hard memory limits and your app will cease to function if you go over that limit. You can check this on iOS by launching Safari on a Mac with an iPhone or iPad connected and use the developer tools to inspect the memory usage on the device (10MB is often cited as the maximum for iOS). .. _/topics/web/web_app_optimization/deployment: Deployment ---------- .. _/topics/web/web_app_optimization/connections_and_sessions: Connections and sessions ************************ By default, web apps can handle 200 simultaneous connections. Keep in mind that a connection is not the same as a session. When a browser first connects to your app, there are typically 3-4 simultaneous connections to get all of the initial assets down to the the browser. This is a standard number across web browsers and not something that can be adjusted. Once the initial payload has been delivered, browsers tend to settle down to 1 or 2 connections. There is always at least one connection per session open. Assuming CPU and RAM are plentiful during initial connection, the effective number of users per web app instance is about 50, but once the initial setup work is done, it gets closer to 200 (probably around 150). That said, once your app needs to host more than that, there are several techniques you can use to increase that number. For instance, you can specify a command line option to increase the number of simultaneous connections. There's a limit to this however, because Xojo apps run on a single core, eventually you'll need to start running instances of your app in parallel to take advantage of multiple CPU cores. This can be done with a Reverse Proxy or a Load Balancer. .. _/topics/web/web_app_optimization/ensuring_your_app_can_handle_all_of_your_users: Ensuring your app can handle all of your users ---------------------------------------------- A Xojo web app has its own web server built-in, but it is not as full-featured as a dedicated web server such as Apache. Tests suggest that a Xojo web app should handle a couple hundred users without a problem, depending on what the app is doing. In general, the number of users that a single instance your app can comfortably handle at once is almost wholly a function of your app and deployment environment. If you need to support more than a handful of simultaneous users, you should design load testing into your app from the beginning so you know how much memory, CPU time, and bandwidth each user needs (it's a function of your app, not Xojo) and do the deployment math accordingly. Xojo Cloud manages this automatically via *load balancing* (which means deploying an instance of your app for every core your server has) and a reverse proxy. If you find your app is getting too sluggish because you now have significantly more users than in the past, simply upgrading your Xojo Cloud server to one with more cores will likely solve the problem. If you are deploying on your own server, you may find that a :doc:`load balancer`, such as HAproxy or Nginx, can be used to handle large amounts of users by directing them to multiple instances of the web app. .. _/topics/web/web_app_optimization/memory_leaks: Memory leaks ------------ Memory leaks occur when objects are created but never destroyed. As more and more objects are created and not destroyed, the amount of memory used increases. Eventually, the app will crash because the machine runs out of available memory. In a desktop app this may not be a big deal because the user will eventually quit the app and that will clear memory. However, in a web app it is more serious because the web app may be running for days, months or even longer. To help avoid this, set the :ref:`WebApplication.AutoQuit` property to :doc:`True` which will cause your app to quit when the last user exits the app, clearing any memory leaks in the process. Keep in mind that if you have any code that runs outside of a session (such as to do maintenance or to reply to API calls via :ref:`WebApplication.HandleURL` then you may need to consider the state of those operations when quitting.) .. _/topics/web/web_app_optimization/finding_memory_leaks: Finding memory leaks ******************** The first thing to do is to look for circular references in your code. That is, object a has a reference to object b and object b has a reference to object a. When it comes time to call destructors to release memory, neither one can be called because their reference counts cannot reach 0. The trickier circular references to track down are places where you have made references to framework objects. To help understand this, keep in mind how the web framework manages its references: * App has references to Sessions * Sessions have references to Views (Pages and non-implicit dialogs) * Views have references to Controls (and implicit dialogs and Containers) * Implicit dialogs and Containers have references to Controls (and other implicit dialogs and Containers) If at any time you make a reference in your code which makes a reference that goes laterally or up this tree, you must set it to Nil when you are done with it. For example, if you had a WebContainer with a property which points back to its Session, and the user leaves the session, then when the framework calls the Close method, the Session would get disconnected from the framework but not freed from memory because your class is technically still holding a reference to it and vice versa because the session (indirectly) holds a reference to the container. Refer to :doc:`How Xojo Manages Memory` to learn more about how Xojo manages memory. .. _/topics/web/web_app_optimization/local_laws: Local laws ---------- Web apps are sometimes affected by the local laws in your area. For example, the European Union recently passed a directive requiring web sites to ask visitors for their consent before they can install most cookies. ================ Web app security ================ Because web apps are accessible to any number of online users, the security of web apps is paramount. Xojo web apps are serious about security. Most traditional web development languages are interpreted, meaning your web app is a set of files on a server. If someone gains access to that server, they gain access to your source code. Xojo web apps are compiled to binary code so your source code is not stored on the server. In order for someone to alter your app they would have to be very familiar with x86 assembly code and be willing to spend a lot of time tracing through that code. This is, at the least, an order of magnitude far more difficult than hacking HTML, JavaScript, CSS, AJAX, and PHP or Java source code. The Open Web Application Security Project (OWASP) provides information on web app security and posted a list of the top 10 web app security issues. While a few of these issues require the developer to be more diligent, most cannot be used to hack into a web app created with Xojo. .. csv-table:: :header: "Hack", "Xojo Protection" :widths: auto "SQL Injection Attacks","Xojo provides developers with prepared statement support for database access. This takes the values to be used in a query and sends them separately to the database server so that it can determine if the values are valid or contain SQL." "Cross-Site Scripting","Xojo web apps can't be used for this purpose because all data sent to the browser is automatically escaped. As a result, the user cannot inject HTML into a page. Also, because the developer doesn't work in HTML or JavaScript, there's no way for the developer to accidentally create this security breach." "Application Authentication","Xojo does not have authentication routines to compromise and session tokens are automatically protected from theft." "Insecure Direct Object References","Xojo does not allow direct object references in this manner so it would be impossible for such a security hole to be created." "Cross-Site Request Forgery","When the user logs into a web site (such as a banking site) and then leaves by navigating to a page of another site without first logging out, the original site will still see the user is logged in until their session times out. The developer can mitigate this by reducing the timeout from the 60 second default." "Security Misconfiguration","This involves the developer making sure they have good passwords for their server, not exposing data that does not need to be exposed, etc. This particular security concern is completely within the control of the developer and is outside the scope of what any development tool can guard against." "Insecure Cryptographic Storage","Be sure to use appropriate function in the Crypto library to properly secure your data." "Failure to Restrict URL Access","Because Xojo web apps create the HTML page on the fly, there's no way for a hacker to access any page except the one that is currently in their browser. However, if the developer chooses to support bookmarking, they would need to make sure they authenticate the user before taking the user to the requested page." "Insufficient Transport Layer Protection","Web Servers provide SSL support which is the appropriate place to handle this issue." "Unvalidated Redirects and Forwards","There is nothing any development tool can do to prevent this. It's up to the developer to make sure their app doesn't depend on untrusted data when redirecting or forwarding the user to another site. For example, the developer should always use the EncodeURLComponent function to encode any values used in a URL which come from a user or database." Go `here `_ to read a full description of each of these types of hacks. Windows ======= .. toctree:: :maxdepth: 1 :name: sec-windows ActiveX, COM and OLE Dealing with Windows security Creating an installer with the Inno setup script (32-bit apps) Creating an installer with the Inno setup script (64-bit apps) Information about the Windows Universal Runtime XAML Getting started --------------- * :doc:`QuickStart` * :doc:`Tutorial` See also -------- :doc:`User interface topics` ==================== ActiveX, COM and OLE ==================== OLE (Object Linking and Embedding) and COM (Component Object Model) are ways to communicate with Windows objects in your applications. ActiveX describes controls that utilize COM or OLE. All of these features are Windows-specific and cannot be used in a cross-platform app. You use the :doc:`OLEObject`, :doc:`DesktopOLEContainer`, :doc:`OLEParameter` and :doc:`OLEException` classes to access these Windows features. .. _/topics/windows/activex,_com_and_ole/oleobject: OLEObject --------- :doc:`OLEObject` can be used to send messages to other Windows apps that support OLE, such as Internet Explorer. Use the Value method to get and set values of the OLE object. Use the Invoke method to call methods (with or without arguments) on the OLE object. This code creates a connection to Internet Explorer and then tells it to display Wikipedia: .. code:: xojo Var obj As OLEObject Var v As Variant Var params(1) As Variant obj = New OLEObject("InternetExplorer.Application", True) obj.Value("Visible") = True params(1) = "http://www.wikipedia.org" v = obj.Invoke("Navigate", params) Exception e As OLEException MessageBox(e.message) .. _/topics/windows/activex,_com_and_ole/olecontainer: OLEContainer ------------ :doc:`DesktopOLEContainer` is used to embed ActiveX controls into your apps. To use an OLEContainer, you drag it from the Library onto a Window. In the ProgramID property of the Inspector, you specify the program ID for the control. You can access the properties and methods of the ActiveX by using the Content property, which returns an OLEObject where you can use the Value and Invoke methods. This code (in a Button's Pressed event) displays a PDF in an Adobe Reader ActiveX OLEContainer that has been added to a window: .. code:: xojo PDFContainer.Content.Value("Src") = "C:\\Document.pdf" Depending on the version of Adobe Reader, you may need to click on the container before the PDF is displayed. To print the PDF in the OLEContainer, you can call the “printWithDialog” method of the Adobe Reader ActiveX control: .. code:: xojo PDFContainer.Content.Invoke("printWithDialog") .. _/topics/windows/activex,_com_and_ole/using_insert_activex_component: Using Insert ActiveX component ------------------------------ You can also directly add ActiveX controls and automatable/OLE objects by selecting Insert > ActiveX Component from the menu. This displays a window with two tabs: Controls and References. The Controls tab lists the ActiveX controls that you can add to a window. The References tab lists the automatable COM objects are are not controls, like the iTunes Library, Microsoft Word, etc. When you select an item and click OK, a module (containing classes for the component) is added to your project for you to use. Refer to the docs for the component to understand how to use its classes, methods and properties. Note that not all ActiveX controls can be used with Xojo. .. _/topics/windows/activex,_com_and_ole/see_also: .. seealso:: :doc:`OLEObject`, :doc:`DesktopOLEContainer`, :doc:`OLEParameter`, :doc:`OLEException`, :doc:`DesktopXAMLContainer` classes; :doc:`Variant` data type ============================= Dealing with Windows security ============================= Windows User Access Control (UAC for short) is a security feature of Windows which helps prevent unauthorized changes to the operating system. It prompts you to give additional permissions for certain system actions. You may need to do this to install files or registry items into system locations for example. .. _/topics/windows/dealing_with_windows_security/64-bit_apps: 64-bit apps ----------- For 64-bit Windows apps you can build your app so that it requests UAC when it starts. To do this, go to the Windows Build Settings Inspector and make sure the Architecture is set to *x86 64-bit* or *ARM 64-bit*. In the Advanced settings of the Inspector you can change the Security setting from "User" (the default) to either "Highest Available" or "Administrator". With "Administrator" selected your app will display the UAC prompt when it is launched. .. _/topics/windows/dealing_with_windows_security/32-bit_apps: 32-bit apps ----------- For 32-bit apps you need to use an alternate technique to prompt UAC. This method creates a Visual Basic Script (VBS) to launch your program with Elevated Access Rights. It does not bypass the security of Windows! It causes the UAC dialog to appear so the user can agree to or enter administrator credentials. The program parameter needs to be the NativePath of the executable file. This method can be used to launch Console apps to do things like update "protected" registry items and/or install programs. .. code:: xojo Sub ExecuteWithUAC(program As String, args As String) Var f As FolderItem Var t As TextOutputStream Var script As String = "Set objShell = CreateObject(""Shell.Application"")" + EndOfLine _ + "objShell.ShellExecute """", """", """", ""runas"", 1" + EndOfLine Var s As String f = FolderItem.TemporaryFile f = New FolderItem(f.NativePath + ".vbs") t = TextOutputStream.Create(f) s = script.ReplaceAll("", program) s = s.ReplaceAll("", ReplaceAll(args, Chr(34), Chr(34) + Chr(34))) t.WriteLine(s) t.Close Var sh As New Shell sh.Mode = 0 sh.TimeOut = 10000 sh.Execute("Wscript.exe " + f.NativePath) f.Remove End Sub *Thanks to long-time Xojo customer Wayne Golding for this code!* ============================================================== Creating an installer with the inno setup script (32-bit apps) ============================================================== `Inno Setup `_ is a free tool for creating Windows installers. It is a great way to create Windows installers for your desktop apps so that you can easily deploy them. The following script can be used with Inno Setup to create an installer for your 32-bit Windows desktop apps. To learn more about Inno Setup and other installer options: * `Creating Installers for Windows Apps `_ blog post * `Creating Windows Installers video `_ * :doc:`Creating an Installer with the Inno Setup Script (64-bit apps)` .. youtube:: OWpFc_PepgQ .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(32-bit_apps)/sample_script: Sample script ------------- The following script is included with Xojo and located here: Example Projects/Platform-Specific/Windows/Making Installers/XojoInstaller.iss .. code:: xojo ; Sample script for creating an installer for a 32-bit Xojo desktop app ; To use this script, specify the values for the two constants ; below with those for your app and project. ; XojoAppName is the name of the build app without the ".exe" extension. #define XojoAppName "EddiesElectronics" ; XojoProjectName is the name of the project file, including its extension. #define XojoProjectName "EEDesktop.xojo_binary_project" [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID from the menu.) AppId={{27EFF741-9A5C-4C19-8841-E9F4B7E6BCFC} AppName={#XojoAppName} AppVerName={#XojoAppName} 1.0 AppPublisher=Xojo, Inc. AppPublisherURL= AppSupportURL= AppUpdatesURL= DefaultDirName={pf}\\{#XojoAppName} DefaultGroupName={#XojoAppName} ; save installer file alongside this script OutputDir=. OutputBaseFilename=Setup{#XojoAppName} ; If you have an End User License Agreement (EULA) that you want the user to agree to before letting the install continue, ; put the path to it here. LicenseFile= Compression=lzma SolidCompression=yes ChangesAssociations=yes ; Require Windows Vista or later MinVersion=0,6.0.6001 [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked ; These directories will be created by the installer inside the DefaultDirName ; (defined above). [Dirs] Name: "{app}\\{#XojoAppName} Libs" Name: "{app}\\{#XojoAppName} Resources" ; These are the files to include. By default you want to include ; the EXE plus the Libs and Resources folders ; but you can include any other files you like as well. [Files] Source: ".\\{#XojoAppName}\\Desktop\\Builds - {#XojoProjectName}\\Windows\\{#XojoAppName}\\{#XojoAppName}.exe"; DestDir: "{app}"; Flags: ignoreversion Source: ".\\{#XojoAppName}\\Desktop\\Builds - {#XojoProjectName}\\Windows\\{#XojoAppName}\\{#XojoAppName} Libs\\*"; DestDir: "{app}\\{#XojoAppName} Libs"; Flags: ignoreversion recursesubdirs createallsubdirs Source: ".\\{#XojoAppName}\\Desktop\\Builds - {#XojoProjectName}\\Windows\\{#XojoAppName}\\{#XojoAppName} Resources\\*"; DestDir: "{app}\\{#XojoAppName} Resources"; Flags: ignoreversion recursesubdirs createallsubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files ; Creates icons/links in the Start Menu and/or the desktop if the user chooses during installation. [Icons] Name: "{group}\\{#XojoAppName}"; Filename: "{app}\\{#XojoAppName}.exe" Name: "{commondesktop}\\{#XojoAppName}"; Filename: "{app}\\{#XojoAppName}.exe"; Tasks: desktopicon ; Give the user the option to run the app after the installation is finished. [Run] Filename: "{app}\\{#XojoAppName}.exe"; Description: "{cm:LaunchProgram,{#XojoAppName}}"; Flags: nowait postinstall skipifsilent ; This specifies the Visual C++ Windows Runtime Redistributable to also install because ; it is required by Xojo apps made with 2016r1 or later. [Files] Source: "C:\\Program Files (x86)\\Xojo\\Xojo 2016r1.1\\Extras\\Windows Runtime\\Installers\\VC_redist.x86.exe"; DestDir: {tmp} [Run] Filename: {tmp}\\VC_redist.x86.exe; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing 32-bit Windows Universal runtime..."; Flags: waituntilterminated .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(32-bit_apps)/file_type_associations: File type associations ---------------------- In order for file type associates to work, you need to include steps in the installer to create the appropriate Registry entries. First change the [Setup] section directive "ChangesAssociations" to "yes". If you don't do this, the correct icon for the file type likely won't be displayed until the user logs off or restarts the computer. .. code:: xojo ChangesAssociations=yes Now you can add a [Registry] section with the entries as shown below. .. code:: xojo [Registry] Root: HKCR; Subkey: ".myType"; ValueType: string; ValueName: ""; ValueData: "MyAppName"; Flags: uninsdeletevalue Root: HKCR; Subkey: "MyAppName"; ValueType: string; ValueName: ""; ValueData: "My Program File"; Flags: uninsdeletekey Root: HKCR; Subkey: "MyAppName\\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\\MyAppFileIcons.ico" Root: HKCR; Subkey: "MyAppName\\shell\\open\\command"; ValueType: string; ValueName: ""; ValueData: """{app}\\XojoApp.exe"" ""%1""" .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(32-bit_apps)/notes: Notes ***** * Change ".myType" to the extension you are associating. * Change "MyAppName" to a unique internal name for the file type as stored in the registry. You must use a unique name for this so you don't inadvertently overwrite another application's registry key. A good way to create a unique name is to use reverse-DNS format, such as: com.example.myapp * "My Program File" above is the name for the file type as shown in Explorer. * "DefaultIcon" is the registry key that specifies the filename containing the icon to associate with the file type. For best results use an .ico file. Be sure to include this ico file in the main installer script so that it is available for use. There are free online services than can create an ico file for you from an initial image or icon. * "shell\\open\\command" is the registry key that specifies the program to execute when a file of the type is double-clicked in Explorer. The surrounding quotes are in the command line so it handles long filenames correctly. .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(32-bit_apps)/see_also: See also ******** * `Inno Setup FAQ `_ .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(32-bit_apps)/create_installer_automatically_with_ide_scripting: Create installer automatically with IDE scripting ------------------------------------------------- For a more automated build process you can use an IDE Script Build Step to run Inno Setup with your installer script after each build. To do this, add an IDE Script step to your project and drag it after the "Build" item in the Windows build settings. Paste this code into the IDE Script Build Step: .. code:: xojo ' Run Inno Setup Installer Script ' Paths Var innoSetupPath As String innoSetupPath = """C:\\Program Files (x86)\\Inno Setup 5/iscc""" Var innoSetupProject As String innoSetupProject = """C:\\PathToSetupScript\\XojoSetup.iss""" Var outputFile As String outputFile = """C:\\PathToScriptOutput\\buildoutput.txt""" ' Create Shell command Var command As String command = innoSetupPath + " " + _ innoSetupProject + _ " >" + outputFile ' Send Shell command Var result As String Var resultCode As Integer result = DoShellCommand(command, 20000, resultCode) ' Check for error If resultCode <> 0 Then Call ShowDialog("Inno Setup Error", "There was an error creating the installer. Refer to buildoutput.txt.", "OK") End If You will need to adjust the paths in this script to point to the correct locations on your computer. .. seealso:: :doc:`Creating an Installer with the Inno Setup Script (64-bit apps)` ============================================================== Creating an installer with the inno setup script (64-bit apps) ============================================================== `Inno Setup `_ is a free tool for creating Windows installers. It is a great way to create Windows installers for your desktop apps so that you can easily deploy them. The following script can be used with Inno Setup to create an installer for your 64-bit Windows desktop apps. To learn more about Inno Setup and other installer options: * :doc:`Information about the Windows Universal Runtime` * `Creating Installers for Windows Apps `_ blog post * `Creating Windows Installers video `_ * :doc:`Creating an Installer with the Inno Setup Script (32-bit apps)` .. youtube:: OWpFc_PepgQ .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(64-bit_apps)/sample_script: Sample script ------------- The following script is included with Xojo and located here: Example Projects/Platform-Specific/Windows/Making Installers/XojoInstaller64bit.iss .. code:: xojo ; Sample script for creating an installer for a 64-bit Xojo desktop app ; To use this script, specify the values for the two constants ; below with those for your app and project. ; XojoAppName is the name of the build app without the ".exe" extension. #define XojoAppName "EddiesElectronics" ; XojoProjectName is the name of the project file, including its extension. #define XojoProjectName "EEDesktop.xojo_binary_project" [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID from the menu.) AppId={{27EFF741-9A5C-4C19-8841-E9F4B7E6BCFC} AppName={#XojoAppName} AppVerName={#XojoAppName} 1.0 AppPublisher=Xojo, Inc. AppPublisherURL= AppSupportURL= AppUpdatesURL= DefaultDirName={pf}\\{#XojoAppName} DefaultGroupName={#XojoAppName} ; save installer file alongside this script OutputDir=. OutputBaseFilename=Setup{#XojoAppName} ; If you have an End User License Agreement (EULA) that you want the user to agree to before letting the install continue, ; put the path to it here. LicenseFile= Compression=lzma SolidCompression=yes ChangesAssociations=yes ArchitecturesInstallIn64BitMode=x64 ; Require Windows 7 SP1 or later MinVersion=6.1.7601 [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked ; These directories will be created by the installer inside the DefaultDirName ; (defined above). [Dirs] Name: "{app}\\{#XojoAppName} Libs" Name: "{app}\\{#XojoAppName} Resources" ; These are the files to include. By default you want to include ; the EXE plus the Libs and Resources folders ; but you can include any other files you like as well. [Files] Source: ".\\{#XojoAppName}\\Desktop\\Builds - {#XojoProjectName}\\Windows 64 bit\\{#XojoAppName}\\{#XojoAppName}.exe"; DestDir: "{app}"; Flags: ignoreversion Source: ".\\{#XojoAppName}\\Desktop\\Builds - {#XojoProjectName}\\Windows 64 bit\\{#XojoAppName}\\*"; DestDir: "{app}\\"; Flags: ignoreversion recursesubdirs createallsubdirs Source: ".\\{#XojoAppName}\\Desktop\\Builds - {#XojoProjectName}\\Windows 64 bit\\{#XojoAppName}\\{#XojoAppName} Libs\\*"; DestDir: "{app}\\{#XojoAppName} Libs"; Flags: ignoreversion recursesubdirs createallsubdirs Source: ".\\{#XojoAppName}\\Desktop\\Builds - {#XojoProjectName}\\Windows 64 bit\\{#XojoAppName}\\{#XojoAppName} Resources\\*"; DestDir: "{app}\\{#XojoAppName} Resources"; Flags: ignoreversion recursesubdirs createallsubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files ; Creates icons/links in the Start Menu and/or the desktop if the user chooses during installation. [Icons] Name: "{group}\\{#XojoAppName}"; Filename: "{app}\\{#XojoAppName}.exe"; Name: "{commondesktop}\\{#XojoAppName}"; Filename: "{app}\\{#XojoAppName}.exe"; Tasks: desktopicon; ; Give the user the option to run the app after the installation is finished. [Run] Filename: "{app}\\{#XojoAppName}.exe"; Description: "{cm:LaunchProgram,{#XojoAppName}}"; Flags: nowait postinstall skipifsilent ; This specifies the Visual C++ Windows Runtime Redistributable to also install because ; it is required by Xojo apps made with 2016r1 or later. [Files] Source: "C:\\Program Files (x86)\\Xojo\\Xojo 2016r1.1\\Extras\\Windows Runtime\\Installers\\VC_redist.x64.exe"; DestDir: {tmp} [Run] Filename: {tmp}\\VC_redist.x64.exe; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing 64-bit Windows Universal runtime..."; Flags: waituntilterminated .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(64-bit_apps)/file_type_associations: File type associations ---------------------- In order for file type associates to work, you need to include steps in the installer to create the appropriate Registry entries. First change the [Setup] section directive "ChangesAssociations" to "yes". If you don't do this, the correct icon for the file type likely won't be displayed until the user logs off or restarts the computer. .. code:: xojo ChangesAssociations=yes Now you can add a [Registry] section with the entries as shown below. .. code:: xojo [Registry] Root: HKCR; Subkey: ".myType"; ValueType: string; ValueName: ""; ValueData: "MyAppName"; Flags: uninsdeletevalue Root: HKCR; Subkey: "MyAppName"; ValueType: string; ValueName: ""; ValueData: "My Program File"; Flags: uninsdeletekey Root: HKCR; Subkey: "MyAppName\\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\\MyAppFileIcons.ico" Root: HKCR; Subkey: "MyAppName\\shell\\open\\command"; ValueType: string; ValueName: ""; ValueData: """{app}\\XojoApp.exe"" ""%1""" .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(64-bit_apps)/notes: Notes ***** * Change ".myType" to the extension you are associating. * Change "MyAppName" to a unique internal name for the file type as stored in the registry. You must use a unique name for this so you don't inadvertently overwrite another application's registry key. A good way to create a unique name is to use reverse-DNS format, such as: com.example.myapp * "My Program File" above is the name for the file type as shown in Explorer. * "DefaultIcon" is the registry key that specifies the filename containing the icon to associate with the file type. For best results use an .ico file. Be sure to include this ico file in the main installer script so that it is available for use. There are free online services than can create an ico file for you from an initial image or icon. * "shell\\open\\command" is the registry key that specifies the program to execute when a file of the type is double-clicked in Explorer. The surrounding quotes are in the command line so it handles long filenames correctly. .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(64-bit_apps)/file_type/associations/see_also: See also ******** * `Inno Setup FAQ `_ .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(64-bit_apps)/create_installer_automatically_with_ide_scripting: Create installer automatically with IDE scripting ------------------------------------------------- For a more automated build process you can use an IDE Script Build Step to run Inno Setup with your installer script after each build. To do this, add an IDE Script step to your project and drag it after the "Build" item in the Windows build settings. Paste this code into the IDE Script Build Step: .. code:: xojo ' Run Inno Setup Installer Script ' Paths Var innoSetupPath As String innoSetupPath = """C:\\Program Files (x86)\\Inno Setup 5/iscc""" Var innoSetupProject As String innoSetupProject = """C:\\PathToSetupScript\\XojoSetup.iss""" Var outputFile As String outputFile = """C:\\PathToScriptOutput\\buildoutput.txt""" ' Create Shell command Var command As String command = innoSetupPath + " " + _ innoSetupProject + _ " >" + outputFile ' Send Shell command Var result As String Var resultCode As Integer result = DoShellCommand(command, 20000, resultCode) ' Check for error If resultCode <> 0 Then Call ShowDialog("Inno Setup Error", "There was an error creating the installer. Refer to buildoutput.txt.", "OK") End If You will need to adjust the paths in this script to point to the correct locations on your computer. .. _/topics/windows/creating_an_installer_with_the_inno_setup_script_(64-bit_apps)/see_also: .. seealso:: :doc:`Creating an Installer with the Inno Setup Script (32-bit apps)`, :doc:`Coding Guidelines for 64-Bit Apps` topics =============================================== Information about the Windows Universal Runtime =============================================== Microsoft refactored their core app runtimes in 2015. They have made what they call a "Universal C Runtime" which has been distributed via Windows Update to all supported versions of Windows that stay up-to-date (which is the default behavior for Windows Update). Starting with Xojo 2016r1, the Xojo Windows framework has been updated to use the latest Microsoft tools. This allows Xojo to stay up to date and allow the Windows support to be improved in future releases. This means that Xojo now uses the new Universal Runtime and your built apps now require it. This Universal Runtime is distinct from Universal Applications. Xojo still creates Win32 (WinAPI) apps for 32-bit and 64-bit apps. As the Universal Runtime is distributed automatically via Windows Update and included with Windows 10, Microsoft considers it part of Windows. Because of this Xojo does not include the Universal Runtime DLLs when building Windows apps since most users will not need another copy. If your users do not have the Universal Runtime, your apps will not run on their systems. The best solution is to make sure the user is up-to-date with Windows Updates. If that is not feasible, then you can also manually supply the Windows Universal Runtime. .. _/topics/windows/information_about_the_windows_universal_runtime/supplying_the_windows_universal_runtime_with_your_apps: Supplying the Windows Universal Runtime with your apps ------------------------------------------------------ If your app is used with versions of Windows 8.1 that do not receive Windows Updates for some reason, you can choose to manually include the Universal Runtime. You can do this using the Visual C++ Redistributable for Visual Studio 2015 (which is what Microsoft recommends) or by manually including the 40 or so small DLLs alongside your app. .. _/topics/windows/information_about_the_windows_universal_runtime/using_the_visual_c++_redistributable: Using the Visual C++ redistributable ************************************ Per Microsoft, this is the recommended way to provide the Universal Runtime. To supply the Windows Universal Runtime on systems where it is not already installed, Microsoft recommends using the Visual C++ Redistributable for Visual Studio 2015. This redistributable is included with Xojo in the "Extras/Windows Runtime/Installers/" folder. Or you can download it directly from Microsoft: `Visual C++ Redistributable for Visual Studio 2015 `_. Additional updates to this redistributable may be available from Microsoft. * `Visual C++ Redistributable for Visual Studio 2015 Update 3 `_ You include and use VC_redist.x64.exe for 64-bit apps or VC_redist.x86.exe for 32-bit apps. These are the script additions to incorporate the redistributable into an Inno Setup installer script: .. code:: xojo [Files] Source: "VC_redist.x86.exe"; DestDir: {tmp} [Run] Filename: {tmp}\\VC_redist.x86.exe; Parameters: "/install /quiet /norestart" StatusMsg: "Installing 32-bit runtime..."; Flags: waituntilterminated Other installers tools can incorporate this runtime in a similar manner. When the Windows Universal Runtime is installed in this manner, further updates to it are handled automatically by the standard Windows Update mechanism. .. _/topics/windows/information_about_the_windows_universal_runtime/including_the_dlls_with_your_app: Including the DLLs with your app ******************************** For situations where you don't use an installer but your app needs to run on machines without the Universal Runtime, you can include a local copy of the DLLs instead. In the Windows Build Settings you can turn the "Include Windows Runtime DLLs" property to ON (available on the "Advanced" (gear) tab of the Inspector). When you Run or Build with the property on, the 40 or so necessary Universal Runtime DLLs are copied alongside your app executable. Additionally, when you turn this on a warning dialog appears to remind you that Windows Universal Runtime DLLs installed in the app folder do not get security updates. This is why Microsoft recommends installing the Universal Runtime by either Windows Update or the Visual C++ Redistributable when possible: further updates to it are handled automatically by the standard Windows Update mechanism. .. _/topics/windows/information_about_the_windows_universal_runtime/troubleshooting: Troubleshooting --------------- If you get a DLL or framework error when trying to run your app, it usually means that the Windows Runtime is not installed on the computer. If this occurs, be sure to install the appropriate Visual C++ Redistributable. It also helps to make sure that you have also applied all Windows updates. .. _/topics/windows/information_about_the_windows_universal_runtime/windows_error_messages: Windows error messages ********************** The following error may occur if you try to run a Xojo app when the Windows Universal Runtime is not installed: * "The program can't start because api-ms-win-crt-heap-l1-1-0.dll is missing from your computer. Try reinstalling the program to fix this problem." .. _/topics/windows/information_about_the_windows_universal_runtime/see_also: .. seealso:: :doc:`Desktop Apps`, :doc:`Creating an Installer with the Inno Setup Script (32-bit apps)`, :doc:`Creating an Installer with the Inno Setup Script (64-bit apps)` topics ================== Using XAML in Xojo ================== XAML is Microsoft's Extensible Application Markup Language. In Xojo it's used to access the controls that have the most modern look and feel on Windows 10/11. Microsoft collectively refers to these as WinUI. The way in which you use a :doc:`DesktopXAMLContainer` is very similar to the way you used an :doc:`DesktopOLEContainer` previously. Adding a WinUI control to a window is done via the :doc:`DesktopXAMLContainer` control. To add one via from a list of available controls: 1. In the Layout Editor, drag a :doc:`DesktopXAMLContainer` from the Library to your window. 2. On the :doc:`DesktopXAMLContainer` you just dragged to the layout, click on the Pencil icon. The Choose a XAML Control dialog box appears. .. image:: https://documentation.xojo.com/topics/windows/images/choose_xaml_control_dialog_new.png :scale: 25 % :align: center 3. Click on the XAML control you want from the list and then click the **OK button**. 4. A preview of the control appears in the Layout Editor. To add one by via XAML code: 1. In the Layout Editor, drag a :doc:`DesktopXAMLContainer` from the Library to your window. 2. In the Inspector, click on the **Pencil icon** next to the Content property. 3. Enter the XAML code that defines the XAML control or controls you wish displayed. 4. Click the **OK button**. 5. A preview of the control appears in the Layout Editor. Example XAML code ----------------- A XAML control is defined by the XAML code you include in the Content property of a :doc:`DesktopXAMLContainer` control. XAML code is very similar to XML. A button ******** .. code:: XML