<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Linux &#8211; Xojo Programming Blog</title>
	<atom:link href="https://blog.xojo.com/category/cross-platform/linux/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.xojo.com</link>
	<description>Blog about the Xojo programming language and IDE</description>
	<lastBuildDate>Wed, 07 Jan 2026 16:01:52 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Welcome to the Grid</title>
		<link>https://blog.xojo.com/2025/12/09/welcome-to-the-grid/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Tue, 09 Dec 2025 16:29:53 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[2025r3]]></category>
		<category><![CDATA[Grid Control]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=15663</guid>

					<description><![CDATA[Xojo 2025r3 introduces the first iteration of the DesktopGrid control. This long-awaited control allows you to display as many rows and columns as needed, making&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Xojo 2025r3 introduces the first iteration of the DesktopGrid control. This long-awaited control allows you to display as many rows and columns as needed, making it ideal for complex data layouts. Let’s take a closer look at some of its features.</p>



<p>Like other controls in the Xojo framework, DesktopGrid is a data-source-based control. Instead of manually adding cells, rows, or columns as you would with a ListBox, the DesktopGrid itself requests the data it needs when it needs it. This approach brings several benefits:</p>



<p><strong>ContainerControl-based cells:</strong> The cells in a DesktopGrid are full ContainerControls, just like the ones you’re already familiar with. You can use the same layout for all cells or customize different layouts for individual cells, rows, or columns. Each cell represents a live control and behaves accordingly.</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="741" src="https://blog.xojo.com/wp-content/uploads/2025/12/Captura-de-pantalla-2025-12-04-a-las-11.50.43-1024x741.png" alt="" class="wp-image-15666" srcset="https://blog.xojo.com/wp-content/uploads/2025/12/Captura-de-pantalla-2025-12-04-a-las-11.50.43-1024x741.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/12/Captura-de-pantalla-2025-12-04-a-las-11.50.43-300x217.png 300w, https://blog.xojo.com/wp-content/uploads/2025/12/Captura-de-pantalla-2025-12-04-a-las-11.50.43-768x556.png 768w, https://blog.xojo.com/wp-content/uploads/2025/12/Captura-de-pantalla-2025-12-04-a-las-11.50.43-1536x1112.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/12/Captura-de-pantalla-2025-12-04-a-las-11.50.43-2048x1483.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li><strong>Lower memory footprint:</strong> DesktopGrid keeps only the cells that are currently visible on screen “alive,” which significantly reduces memory usage, especially when working with very large data sets. For example, when you resize the containing window, the DesktopGrid will request additional content from its DataSource <strong>only if necessary</strong>. When cells move outside the visible area of the DesktopGrid, their corresponding ContainerControl instances are “closed,” triggering an event you can handle, and freeing the memory they were using.</li>



<li><strong>Faster execution</strong>: Large data sets are not preloaded into the control. This means that opening a window with a DesktopGrid or running an app with many cells happens almost instantly, providing a smooth and responsive experience even with extensive data.</li>
</ul>



<figure class="wp-block-video"><video height="1080" style="aspect-ratio: 1920 / 1080;" width="1920" controls src="https://blog.xojo.com/wp-content/uploads/2025/12/FastExecution.mp4"></video></figure>



<ul class="wp-block-list">
<li><strong>Layout Flexibility</strong>: Since the number of rows and columns is determined by the DataSource, a DesktopGrid instance can have a flexible layout rather than a rigid, static one. For example, you can adjust the number of columns based on the available width of the DesktopGrid. You can also set different widths for individual columns, while the row height remains fixed.</li>
</ul>



<figure class="wp-block-video"><video height="1080" style="aspect-ratio: 1920 / 1080;" width="1920" controls src="https://blog.xojo.com/wp-content/uploads/2025/12/FlexibleGridLayout.mp4"></video></figure>



<ul class="wp-block-list">
<li><strong>Data representation changes &#8220;on the fly&#8221;</strong>: Because the DataSource provides the content for each cell, you can dynamically change the data representation (the ContainerControl layout) at runtime or even pull data from a completely different source.</li>



<li><strong>A better separation of responsibilities</strong>: While you can implement the GridDataSource interface directly on the DesktopGrid or in its containing Window, using a dedicated DataSource class is the cleaner approach. This ensures a clearer separation between the view (the DesktopGrid), the model (the data feeding the cells), and the controller (which manages interactions between the two). A single DataSource can also feed multiple DesktopGrid instances, making your code more reusable.</li>



<li><strong>Headers everywhere:</strong> By default, DesktopGrid displays column and row headers. You can modify this in the Inspector Panel or via code. Depending on your scenario, you might choose to show both headers, just one, or hide them entirely. By default, the headers display simple numbering for each column and row. However, you can fully customize their content using the PaintHeaderContentForColumn and PaintHeaderContentForRow methods of your GridDataSource implementation, giving you complete control over how headers appear.</li>
</ul>



<h2 class="wp-block-heading">Is the DesktopGrid a replacement for DesktopListBox?</h2>



<p>While you can use DesktopGrid for many purposes, it is not a direct replacement for DesktopListBox in all scenarios. If your data is primarily textual (like simple labels) or relies on features specific to DesktopListBox, such as hierarchical data representation, then DesktopListBox is still the better choice.</p>



<p>DesktopGrid shines in scenarios where you need complex layouts in each cell using live controls rather than temporary pictures or workarounds. Each cell can have its own layout, including interactive controls such as ListBoxes, buttons, or other reactive elements that respond to user interactions. This makes DesktopGrid ideal for rich, interactive, and highly customizable grid layouts.</p>



<h2 class="wp-block-heading">Performance Caveats to Consider</h2>



<p>Although the first iteration of DesktopGrid was designed with performance in mind, there are some factors that may affect its redraw speed. The most significant is that each cell uses live controls, so performance will be similar to placing the same number of controls directly on a Window. While alternative approaches—such as reusable cells or backing picture representations—could improve speed, they come with their own trade-offs.</p>



<p>As a general rule, performance depends on cell size and the number of visible cells at any given time. A large DesktopGrid with many rows and columns, even if each cell contains just a simple label, may experience slower refresh rates. Additionally, actual performance can vary depending on the operating system and its version, as these affect the underlying drawing and refresh processes.</p>



<h2 class="wp-block-heading">Wiring the Grid</h2>



<p>Although this is just the first iteration of the DesktopGrid control, we are already working on the next set of features while further refining the existing ones. You can read more about this control in the <a href="https://documentation.xojo.com/api/user_interface/desktop/desktopgrid.html" target="_blank" rel="noreferrer noopener">Xojo Documentation</a>. We want to extend a big <strong>THANK YOU</strong> to everyone who provided feedback and dedicated their time testing the Grid during the beta cycle, you know who you are! Your input is invaluable, and the feature requests you submitted (combined with others already on our roadmap) will help shape a DesktopGrid that better fits your needs.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		<enclosure url="https://blog.xojo.com/wp-content/uploads/2025/12/FastExecution.mp4" length="432356" type="video/mp4" />
<enclosure url="https://blog.xojo.com/wp-content/uploads/2025/12/FlexibleGridLayout.mp4" length="2560805" type="video/mp4" />

			</item>
		<item>
		<title>Implementing a Non-Blocking Image Processor with Xojo Threads</title>
		<link>https://blog.xojo.com/2025/08/28/implementing-a-non-blocking-image-processor-with-xojo-threads/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Thu, 28 Aug 2025 14:56:33 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[FolderItem]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[Picture]]></category>
		<category><![CDATA[Threads]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=15302</guid>

					<description><![CDATA[Have you ever written an app that needs to do some heavy lifting, like processing a big batch of files? Maybe you click a button,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Have you ever written an app that needs to do some heavy lifting, like processing a big batch of files? Maybe you click a button, and suddenly your entire app just … stops. The cursor turns into a spinning wheel, the windows won’t respond, and your users are left wondering if it crashed. It’s a frustrating experience, but don’t worry, there’s a solution:&nbsp;<a href="https://documentation.xojo.com/api/language/threading/thread.html" target="_blank" rel="noreferrer noopener">Threads</a>!</p>



<p>Let&#8217;s build a simple but incredibly powerful Desktop app: a threaded image resizer. This tool will let you pick a folder of images and resize them all without ever locking up the app. It’s a perfect showcase of how easy and effective <a href="https://documentation.xojo.com/topics/threading/index.html" data-type="link" data-id="https://documentation.xojo.com/topics/threading/index.html" target="_blank" rel="noreferrer noopener">threading</a> can be in Xojo.</p>



<p>Let’s get started!</p>



<h4 class="wp-block-heading" id="step-1-designing-the-user-interface">Step 1: Designing the User Interface</h4>



<p>First things first, let’s lay out our app’s window. We’re going for a clean and simple design. All you need to do is create a new Xojo Desktop project and add the following controls from the Library onto your main window:</p>



<ol class="wp-block-list">
<li><strong>DesktopButton</strong>: This will be our “start” button. The user will click this to select a folder and begin the resizing process. Let’s set its&nbsp;<code>Caption</code>&nbsp;to “Select folder &amp;&amp; Start”.</li>



<li><strong>DesktopProgressBar</strong>: This will give the user visual feedback on the progress of the resizing operation.</li>



<li><strong>DesktopLabel</strong>: We’ll use this label to display status updates, like which file is currently being processed or when the job is complete. Let’s call it&nbsp;<code>StatusLabel</code>.</li>
</ol>



<p>Your app layout should look something like this:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="692" src="https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-1024x692.jpg" alt="" class="wp-image-15303" srcset="https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-1024x692.jpg 1024w, https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-300x203.jpg 300w, https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-768x519.jpg 768w, https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1.jpg 1525w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h4 class="wp-block-heading" id="step-2-adding-the-magic-ingredient-the-thread">Step 2: Adding the Magic Ingredient: The Thread</h4>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-ad2f72ca wp-block-group-is-layout-flex">
<p>Now for the star of the show! Go to the Library and find the&nbsp;<code>Thread</code>&nbsp;object. Drag and drop one onto your window. By default, it might be named&nbsp;<code>Thread1</code>, but I’ve renamed mine to&nbsp;<code>ResizeThread</code>&nbsp;to make its purpose crystal clear. This object is where our background work will happen.</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="298" height="287" src="https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_2.jpg" alt="" class="wp-image-15304" style="width:500px"/></figure>
</div>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Quick note on thread types: Cooperative threads (the default) share a single CPU core with the main/UI thread and other cooperative threads. Preemptive threads can run on separate CPU cores and deliver true parallelism for CPU-bound work. More info on this subject: <a href="https://blog.xojo.com/2024/10/01/cooperative-to-preemptive-weaving-new-threads-into-your-apps/" target="_blank" rel="noreferrer noopener">Cooperative to Preemptive: Weaving New Threads into your Apps</a></p>
</blockquote>



<p>Why this project uses Preemptive threads: resizing many images is CPU-bound and benefits from parallel cores without blocking the UI.</p>



<h4 class="wp-block-heading" id="step-3-kicking-off-the-process">Step 3: Kicking Off the Process</h4>



<p>With the UI and thread in place, let’s add the code to get things moving. Double-click the “Select folder &amp; Start” button to add its&nbsp;<code>Pressed</code>&nbsp;event. This is what will run when the user clicks it.</p>



<p>Here’s the code for the&nbsp;<code>Pressed</code>&nbsp;event:</p>



<pre class="wp-block-code"><code>' Ask the user for a source folder (contains images)
Var f As FolderItem = FolderItem.ShowSelectFolderDialog
If f = Nil Then Return ' user cancelled

mSourceFolder = f

' Reset UI
ProgressBar1.Value = 0
StatusLabel.Text = "Starting…"

' Kick off background work!
ResizeThread.Start</code></pre>



<p>Let’s break this down. First, we ask the user to select a folder. If they don’t pick one, we simply exit. If they do, we store the selected folder in a window property called&nbsp;<code>mSourceFolder</code>&nbsp;(<code>Public Property mSourceFolder As FolderItem</code>). We then reset our progress bar and status label and, most importantly, we call&nbsp;<code>ResizeThread.Start</code>. That one simple line tells our thread to wake up and get to work by running its&nbsp;<code>Run</code>&nbsp;event.</p>



<h4 class="wp-block-heading" id="step-4-the-heavy-lifting-in-the-background">Step 4: The Heavy Lifting (in the Background)</h4>



<p>The&nbsp;<code>Run</code>&nbsp;event of the&nbsp;<code>ResizeThread</code>&nbsp;is where the core logic lives. This is where we’ll find, load, resize, and save the images. Remember, the golden rule of threads is:&nbsp;never touch the UI directly from the Run event. Doing so can cause crashes and unpredictable behavior.</p>



<p>Instead, we perform our task and then&nbsp;<em>send a message</em>&nbsp;back to the main thread with an update. We do this using a method called&nbsp;<code>AddUserInterfaceUpdate</code>.</p>



<p>Here’s the&nbsp;<code>Run</code>&nbsp;event code:</p>



<pre class="wp-block-code"><code>' Quick validation up-front
If mSourceFolder = Nil Or Not mSourceFolder.Exists Or Not mSourceFolder.IsFolder Then
  Me.AddUserInterfaceUpdate(New Dictionary("progress":0, "msg":"No folder selected"))
  Return
End If

Try
  ' Ensure output subfolder "&lt;selected&gt;/resized" exists
  Var outFolder As FolderItem = mSourceFolder.Child("resized")
  If Not outFolder.Exists Then outFolder.CreateFolder
  
  ' Build a list of candidate image files.
  ' Note: Using Picture.Open to validate images is simple and robust.
  '       (For very large folders, you could pre-filter by extension first.)
  Var images() As FolderItem
  For Each it As FolderItem In mSourceFolder.Children
    If it = Nil Or it.IsFolder Then Continue ' Ignore subfolders
    Try
      Var p As Picture = Picture.Open(it)
      If p &lt;&gt; Nil Then images.Add(it)
    Catch e As RuntimeException
      ' Non-image or unreadable; skip
    End Try
  Next
  
  Var total As Integer = images.Count
  If total = 0 Then
    Me.AddUserInterfaceUpdate(New Dictionary("progress":0, "msg":"No images found"))
    Return
  End If
  
  ' Resize settings (simple “fit within 800x800” bounding box)
  Const kMaxW As Double = 800.0
  Const kMaxH As Double = 800.0
  
  ' Short delay after each file so the main thread can repaint the UI
  Const kDelayMS As Integer = 50
  
  For i As Integer = 0 To total - 1
    Var src As FolderItem = images(i)
    Try
      ' Load source (immutable)
      Var pic As Picture = Picture.Open(src)
      If pic = Nil Or pic.Width &lt;= 0 Or pic.Height &lt;= 0 Then Continue
      
      ' Compute proportional scale (never upscale)
      Var sW As Double = kMaxW / pic.Width
      Var sH As Double = kMaxH / pic.Height
      Var scale As Double = Min(Min(sW, sH), 1.0)
      
      Var newW As Integer = Max(1, pic.Width * scale)
      Var newH As Integer = Max(1, pic.Height * scale)
      
      ' Render into a new mutable bitmap of the target size
      Var outPic As New Picture(newW, newH)
      outPic.Graphics.DrawPicture(pic, 0, 0, newW, newH, 0, 0, pic.Width, pic.Height)
      
      ' Build a safe base name (strip the last extension; handle dotfiles)
      Var name As String = src.Name
      Var ext As String = name.LastField(".")
      Var baseName As String
      If ext = name Then
        ' No dot in the name
        baseName = name
      Else
        baseName = name.Left(name.Length - ext.Length - 1)
      End If
      If baseName.Trim = "" Then baseName = "image"
      
      ' Save JPEG (fallback PNG if JPEG export not supported)
      Var outFile As FolderItem = outFolder.Child(baseName + "_resized.jpg")
      If Picture.IsExportFormatSupported(Picture.Formats.JPEG) Then
        outPic.Save(outFile, Picture.Formats.JPEG, Picture.QualityHigh) ' adjust the quality of the jpeg here
      Else
        outPic.Save(outFolder.Child(baseName + "_resized.png"), Picture.Formats.PNG)
      End If
      
    Catch io As IOException
      ' Likely a write/permission/disk issue; skip this file
    Catch u As UnsupportedOperationException
      ' Unsupported format/operation on this platform; skip
    End Try
    
    ' Progress + message to the UI (safe handoff)
    Var pct As Integer = ((i + 1) * 100) / total
    Me.AddUserInterfaceUpdate(New Dictionary("progress":pct, _
    "msg":"Resized " + src.Name + " (" + pct.ToString + "%)"))
    
    ' Let the main thread paint the update
    Me.Sleep(kDelayMS, True) ' wakeEarly=True allows early resume when idle
  Next
  
  ' Final UI message
  Me.AddUserInterfaceUpdate(New Dictionary("progress":100, "msg":"Done"))
  
Catch e As RuntimeException
  ' Any unexpected failure: report a friendly error
  Me.AddUserInterfaceUpdate(New Dictionary("progress":0, "msg":"Error: " + e.Message))
End Try</code></pre>



<h4 class="wp-block-heading" id="step-5-receiving-updates-and-safely-changing-the-ui">Step 5: Receiving Updates and Safely Changing the UI</h4>



<p>So, how does the main thread “hear” these updates? Through the&nbsp;<code>UserInterfaceUpdate</code>&nbsp;event on the&nbsp;<code>ResizeThread</code>&nbsp;itself! This event fires on the main thread, making it the one and only safe place to update our controls.</p>



<p>Here’s the code for the&nbsp;<code>UserInterfaceUpdate</code>&nbsp;event:</p>



<pre class="wp-block-code"><code>' This event runs on the main thread – SAFE to update controls here.
If data.Count = 0 Then Return

Var latest As Dictionary = data(data.LastIndex)

If latest.HasKey("progress") Then ProgressBar1.Value = latest.Value("progress")
If latest.HasKey("msg") Then StatusLabel.Text = latest.Value("msg")

' A little bonus: when we are done, open the output folder!
If latest.HasKey("msg") And latest.Value("msg") = "Done" Then
  Var outFolder As FolderItem = mSourceFolder.Child("resized")
  If outFolder &lt;&gt; Nil And outFolder.Exists Then
    outFolder.Open
  End If
End If</code></pre>



<p>In this event, we receive an array of Dictionaries (all the updates that have queued up). We usually only care about the very latest one, so we grab&nbsp;<code>data(data.LastIndex)</code>. Then, we safely access the values from the dictionary and assign them to&nbsp;<code>ProgressBar1.Value</code>&nbsp;and&nbsp;<code>StatusLabel.Text</code>. That’s it!</p>



<h3 class="wp-block-heading" id="conclusion">Conclusion</h3>



<p>And there you have it! A fully functional, non-blocking image resizer. By moving the heavy work to a&nbsp;<code>Thread</code>, we’ve created an application that provides a smooth, professional user experience.</p>



<p>The pattern is simple:</p>



<ol class="wp-block-list">
<li><strong>Start</strong>&nbsp;the task from the main thread (<code>ResizeThread.Start</code>).</li>



<li><strong>Work</strong>&nbsp;inside the thread’s&nbsp;<code>Run</code>&nbsp;event.</li>



<li><strong>Communicate</strong>&nbsp;back to the UI with&nbsp;<code>AddUserInterfaceUpdate</code>.</li>



<li><strong>Update</strong>&nbsp;the UI safely in the&nbsp;<code>UserInterfaceUpdate</code>&nbsp;event.</li>
</ol>



<p>That’s pretty much it! Go ahead, <a href="https://github.com/xolabsro/Thread-Image-Resizer" data-type="link" data-id="https://github.com/xolabsro/Thread-Image-Resizer" target="_blank" rel="noreferrer noopener">download this project’s source code</a> from GitHub, play around with it, and think about how you can use threads in your own applications!</p>



<p>Happy coding!</p>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Xojo IDE on Pi: A Game-Changer in Embedded Development</title>
		<link>https://blog.xojo.com/2025/03/25/xojo-ide-on-pi-a-game-changer-in-embedded-development/</link>
		
		<dc:creator><![CDATA[William Yu]]></dc:creator>
		<pubDate>Tue, 25 Mar 2025 15:33:47 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[2025r1]]></category>
		<category><![CDATA[ARM64]]></category>
		<category><![CDATA[Embedded Development]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=14616</guid>

					<description><![CDATA[Prior to Xojo 2025r1, Raspberry Pi developers had to rely on a two-step process: programming on a separate computer that could run the Xojo IDE,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Prior to Xojo 2025r1, Raspberry Pi developers had to rely on a two-step process: programming on a separate computer that could run the Xojo IDE, then either remotely debugging the app on the Pi or manually copying the executable over. While this worked, it added extra steps and potential complexities, especially when dealing with debugging and deployment.</p>



<h2 class="wp-block-heading">Introducing the Xojo IDE for Linux ARM64</h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="548" src="https://blog.xojo.com/wp-content/uploads/2025/03/XojoIDEonRPi-1024x548.png" alt="" class="wp-image-14620" srcset="https://blog.xojo.com/wp-content/uploads/2025/03/XojoIDEonRPi-1024x548.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/03/XojoIDEonRPi-300x160.png 300w, https://blog.xojo.com/wp-content/uploads/2025/03/XojoIDEonRPi-768x411.png 768w, https://blog.xojo.com/wp-content/uploads/2025/03/XojoIDEonRPi-1536x821.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/03/XojoIDEonRPi-2048x1095.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">8GB Raspberry Pi 5, running Bookworm</figcaption></figure>



<p>With the release of Xojo 2025r1, however, everything has changed. Now, for the first time, developers can run the Xojo IDE directly on their Raspberry Pi 64-bit device. This means you can write, build, and test your applications all from within the same environment. The seamless integration allows for faster iteration and a smoother workflow, giving you more control over your development process. Not only can you now run the Xojo IDE on a Pi, but also on any Linux ARM64 distro.</p>



<h2 class="wp-block-heading">What is Supported?</h2>



<p>Nearly all the features you love about working in the Xojo IDE on other platforms are now available with the Linux ARM64 IDE, with the exception of building macOS and Mobile (Android/iOS) apps. Instead of remotely debugging your Raspberry Pi app using the Xojo IDE on Windows, you can now remotely debug your Windows app using the Xojo IDE on Pi!</p>



<p>For best results, we recommend a Raspberry Pi 5 or 500 with 8GB of RAM.</p>



<h2 class="wp-block-heading">Xojo Loves Pi!</h2>



<p>With the <a href="https://xojo.com/download/" target="_blank" rel="noreferrer noopener">Xojo</a> IDE now available for Linux ARM64, we&#8217;re making our case as the go-to solution for Raspberry Pi developers. The simplicity, cross-platform support, and power makes Xojo an attractive choice for those looking to build apps on the Pi, without the complexity of traditional programming environments.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>NOTE:</strong> You can build Linux apps with Xojo <a href="https://blog.xojo.com/2024/03/26/version-control-ready-xojo-lite-and-free-xojo-lite-linux-for-everyone/#free-linux" target="_blank" rel="noreferrer noopener">for free</a>, no license required. Just select Linux in Build Settings, then choose ARM or x86 architecture in the Inspector.</p>
</blockquote>



<p><em><em><em>William Yu grew up in Canada learning to program BASIC on a Vic-20. He is Xojo’s resident Windows and Linux engineer, among his many other skills. Some may say he has joined the dark side here in the USA, but he will always be a Canadian at heart.</em></em></em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Build a Xojo Plugin with GitHub Actions</title>
		<link>https://blog.xojo.com/2025/02/25/build-a-xojo-plugin-with-github-actions/</link>
		
		<dc:creator><![CDATA[Jürg Otter]]></dc:creator>
		<pubDate>Tue, 25 Feb 2025 22:30:00 +0000</pubDate>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Guest Post]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[#YearofCode]]></category>
		<category><![CDATA[cubeSQL]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Year of Code]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=14509</guid>

					<description><![CDATA[As part of Xojo&#8217;s 2025 Year of Code initiative, February&#8217;s theme is databases—a perfect fit for my recent work on the Open Source CubeSQLPlugin. This&#8230;]]></description>
										<content:encoded><![CDATA[
<p>As part of <a href="https://forum.xojo.com/t/2025-year-of-code-february/" target="_blank" rel="noreferrer noopener">Xojo&#8217;s 2025 Year of Code</a> initiative, <a href="https://forum.xojo.com/t/2025-year-of-code-february/" target="_blank" rel="noreferrer noopener">February&#8217;s theme is databases</a>—a perfect fit for my recent work on the <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">Open Source CubeSQLPlugin</a>. This article shares insights into automating its build process with <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions Workflows</a>.</p>



<p>Xojo plugins are add-ons that extend the functionality of the Xojo development environment, allowing developers to add additional features, integrate third-party libraries, and much more.</p>



<h2 class="wp-block-heading">Write a Xojo Plugin</h2>



<p>This article assumes familiarity with the <a href="https://documentation.xojo.com/topics/custom_controls/plugins_sdk.html" target="_blank" rel="noreferrer noopener">Xojo Plugin SDK</a> and focuses on automating plugin builds. If you&#8217;re new to plugin development, check out the resources in Xojo’s &#8220;Extras&#8221; folder, YouTube tutorials, and the Xojo Forum.</p>



<p>It builds on my previous blog post <a href="https://blog.xojo.com/2022/10/19/xojo-github-actions/" target="_blank" rel="noreferrer noopener">Xojo Github Actions</a> and involves <a href="https://www.docker.com/" target="_blank" rel="noreferrer noopener">Docker</a>, which has also been a part of a previous blog posts (<a href="https://blog.xojo.com/2024/06/03/docker-database-servers-and-xojo/" target="_blank" rel="noreferrer noopener">Docker, Database Servers and Xojo</a>, <a href="https://blog.xojo.com/2021/05/17/running-xojo-web-applications-in-docker/" target="_blank" rel="noreferrer noopener">Running Xojo Web Applications in Docker</a>). So read up there on what a <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions workflow</a> is, what the <code>.yaml</code> files in the Folder <code>.github/workflows</code> are used for.</p>



<h2 class="wp-block-heading"><strong>Build a Xojo Plugin</strong></h2>



<p>Since Xojo is a cross platform development environment, a Xojo plugin needs to be built for all the supported platforms <em>(Windows, macOS, Linux)</em> and all their architectures <em>(Intel/ARM, 32/64Bit)</em>.</p>



<p>All the plugin parts (the Windows <code>.dll</code>&#8216;s, .macOS <code>.dylib</code>&#8216;s and Linux <code>.so</code>&#8216;s) are being packaged into a <code>.xojo_plugin</code> file.</p>



<p>One of the challenges is to have an environment to build all that. At the first glance it seems that one would need a Mac, a Windows machine &#8211; as well as some Linux environment <em>(any of which could also just be a virtual machine)</em>. And one would first think that there will be a lot of manual steps involved &#8211; building the plugin source on various machines, copying all the built plugin parts together to finally assemble the resulting <code>.xojo_plugin</code>.</p>



<p>However &#8211; all that is not required. You can build a Xojo plugin without your own machine being involved, fully automated. GitHub Actions provides all we need.</p>



<h2 class="wp-block-heading"><strong>Example: CubeSQLPlugin</strong></h2>



<p>Let us have a look at this existing <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">Open Source Plugin</a>.</p>



<p><a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener"><em>CubeSQLPlugin</em></a><em> is an extension for the Xojo programming environment that allows developers to use </em><a href="https://sqlabs.com/cubesql" target="_blank" rel="noreferrer noopener"><em>CubeSQL</em></a><em>, a fully featured and high performance relational database management system built on top of the SQLite database engine.</em></p>



<p>The plugin is being used in the Open Sourced Administrations Tools for CubeSQL, which are both written in Xojo: <a href="https://github.com/cubesql/webadmin" target="_blank" rel="noreferrer noopener">CubeSQL Web Admin</a> and <a href="https://github.com/cubesql/cubeSQLAdmin" target="_blank" rel="noreferrer noopener">CubeSQL (Desktop) Admin</a>.</p>



<p>Internally the plugin uses <a href="https://www.libressl.org" target="_blank" rel="noreferrer noopener">LibreSSL</a> for TLS Connections of Database Clients to CubeSQL Server. This means that first the LibreSSL libs need to be built for all build targets. Only then can the plugin source be built again for all build targets. And finally, all the plugin parts need to be assembled into the Xojo plugin structure for the final <code>.xojo_plugin</code>.</p>



<p>We have automated the whole build process of the plugin using <a href="https://docs.github.com/de/actions" target="_blank" rel="noreferrer noopener">GitHub Actions</a> by having created three workflows. You&#8217;ll find their source in the folder <a href="https://github.com/marcobambini/cubesqlplugin/tree/master/.github/workflows" target="_blank" rel="noreferrer noopener">.github/workflows</a>. All workflow executions you&#8217;ll find on GitHub in the tab named <a href="https://github.com/marcobambini/cubesqlplugin/actions" target="_blank" rel="noreferrer noopener">Actions</a> <em>(as long as they have not been deleted or removed)</em>.</p>



<p><strong>LibreSSL</strong><br>This workflow will download the <a href="https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/" target="_blank" rel="noreferrer noopener">LibreSSL Source</a>, and build it for all needed build targets. It needs to be run only once, e.g. if a new <a href="https://www.libressl.org" target="_blank" rel="noreferrer noopener">LibreSSL</a> version is available that should be included for the next plugin build. That&#8217;s why it&#8217;s a workflow that can be run manually. It asks which LibreSSL version should be built and whether the built libraries should be committed and pushed to the repository.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="367" src="https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-1024x367.png" alt="" class="wp-image-14514" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-1024x367.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-300x108.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-768x276.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-1536x551.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/01_Workflow_LibreSSL-2048x735.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>CubeSQLPlugin</strong><br>This workflow will build the <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">CubeSQL Xojo Plugin</a> for all build targets using the currently committed LibreSSL libs in the repository. It will commit and push all the built <code>.dll</code>&#8216;s, .<code>dylib</code>&#8216;s and <code>.so</code>&#8216;s to the repository.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="496" src="https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-1024x496.png" alt="" class="wp-image-14515" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-1024x496.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-300x145.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-768x372.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-1536x744.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/02_Workflow-Plugin-2048x992.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p><strong>Create Release</strong><br>That one is pretty obvious, right? It builds the structure of the final <code>.xojo_plugin</code> and creates a new <a href="https://github.com/marcobambini/cubesqlplugin/releases" target="_blank" rel="noreferrer noopener">Release</a> in the repository with the final Xojo plugin &#8211; ready to be downloaded.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="535" src="https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-1024x535.png" alt="" class="wp-image-14516" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-1024x535.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-300x157.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-768x401.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-1536x802.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/03_Plugin-Release-2048x1069.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading"><strong>Build for all Platforms and Architectures</strong></h3>



<p>The two workflows &#8220;LibreSSL&#8221; and &#8220;CubeSQLPlugin&#8221; are similar in their approach to building for all the supported platforms <em>(Windows, macOS, Linux)</em> and all their architectures <em>(Intel/ARM, 32/64Bit)</em>.</p>



<p>GitHub offers hosted <a href="https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners" target="_blank" rel="noreferrer noopener">virtual machines to run workflows</a>. All virtual machines contain an environment of tools, packages, and settings available for <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions workflows</a> to use.</p>



<p>So our workflows can simply use these environments. The workflows consist of several steps, each of which can be executed on a different runner. Remember that I&#8217;ve written: &#8220;You can build a Xojo plugin without your own machine involved&#8221;?</p>



<p>In the following ScreenShot we&#8217;re seeing that the Linux-ARM64 plugin part is being created on a Ubuntu 24.04.1 ARM machine.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="475" src="https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-1024x475.png" alt="" class="wp-image-14517" srcset="https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-1024x475.png 1024w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-300x139.png 300w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-768x356.png 768w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-1536x712.png 1536w, https://blog.xojo.com/wp-content/uploads/2025/02/04_Build-Linux-ARM64-2048x949.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h4 class="wp-block-heading"><strong>macOS</strong></h4>



<ul class="wp-block-list">
<li>Runs on a GitHub macOS virtual machine.</li>



<li>Uses the preinstalled Xcode command line tools to build &#8220;Intel x86_64&#8221; and &#8220;ARM64&#8221; and combine the two to a universal binary.</li>



<li>CodeSigns with credentials added as repository secrets.</li>
</ul>



<h4 class="wp-block-heading"><strong>Linux</strong></h4>



<ul class="wp-block-list">
<li>ARM32, ARM64
<ul class="wp-block-list">
<li>Runs on a GitHub Linux virtual ARM machine.</li>



<li>To always have exactly the same build toolset <em>(e.g. so that it doesn&#8217;t matter if GitHub updates to a newer Ubuntu version)</em> it leverages a Docker container (e.g. <a href="https://hub.docker.com/r/arm64v8/gcc/" target="_blank" rel="noreferrer noopener">arm64v8/gcc</a>) &#8211; and actually builds the source inside the Docker container.</li>
</ul>
</li>



<li>i386, x86_64
<ul class="wp-block-list">
<li>Runs on a GitHub Linux virtual Intel machine.</li>



<li>Also uses a <a href="https://hub.docker.com/_/gcc" target="_blank" rel="noreferrer noopener">gcc Docker Image</a> to build the source.</li>
</ul>
</li>
</ul>



<h4 class="wp-block-heading"><strong>Windows</strong></h4>



<ul class="wp-block-list">
<li>Runs on a GitHub Windows virtual machine.</li>



<li>Uses the preinstalled Visual Studio environment to build all &#8220;win32&#8221;, &#8220;x64&#8221; and &#8220;ARM64&#8221;.</li>
</ul>



<h2 class="wp-block-heading"><strong>How to Build your Xojo Plugin</strong></h2>



<p>I&#8217;m very well aware that this article can&#8217;t help you build your plugin. Each one is individual and needs its own steps and processes.</p>



<p>However, should you consider automating your plugin build process. You might want to look in more detail in the open-source plugin source and the <a href="https://github.com/marcobambini/cubesqlplugin/tree/master/.github/workflows" target="_blank" rel="noreferrer noopener">workflows</a> which are building <a href="https://github.com/marcobambini/cubesqlplugin" target="_blank" rel="noreferrer noopener">CubeSQLPlugin</a> for all required targets and architectures.</p>



<h2 class="wp-block-heading"><strong>How to Transition from Manual to Automated</strong></h2>



<p>While creating a plugin, one doesn&#8217;t think of its build process in the early stages, maybe not even for the first few released versions.&nbsp;</p>



<p>Plugin building is usually a manual process, requiring manual execution of a lot of steps and commands on various machines. It is tedious and prone to errors. It takes time, which adds up the more you build. There&#8217;s a point when one should think about streamlining and automate the process, allowing for building new versions without too much effort.<br>In addition, it&#8217;s certainly a good idea to have a consistent build environment other than your own development machine as you could install and change things on there which could impact the builds. That&#8217;s why building inside a Docker container is an interesting approach &#8211; it will remain exactly the same each and every time, even if the host machine is changed <em>(e.g. by being updated to a new operating system version)</em>.</p>



<p>All that used to be like that before having written the GitHub workflow.</p>



<p>How can you transition from a manual to an automated process for your own project? I won&#8217;t be able to provide all answers. But, I&#8217;d like to give a few ideas of possible steps, with no intention of them being complete or the best choice for your project.</p>



<ul class="wp-block-list">
<li>Use a version control system.
<ul class="wp-block-list">
<li>Git and GitHub are (for many use cases) free.</li>



<li>Using a Cloud solution serves as a backup should something happen to your machines.</li>



<li>You can additionally benefit from other services such as the GitHub Actions workflows mentioned in this article.</li>
</ul>
</li>



<li>Everything that is only in your head should go into some written form. Once you don&#8217;t need to do something for some time, you might forget or miss something.
<ul class="wp-block-list">
<li>Start with documenting all steps, commands, and actions.</li>
</ul>
</li>



<li>Try to get rid of manual steps. Are you copy-and-pasting several commands to a terminal each and every time?
<ul class="wp-block-list">
<li>Put them in a shell script, so that you only need to run one command.</li>



<li>You can use these scripts later when going to a fully-automated system.</li>
</ul>
</li>



<li>When initial development is finished and you&#8217;re ready to release:
<ul class="wp-block-list">
<li>Don&#8217;t do releases from your development machine. It might be defunct, broken, stolen, or you&#8217;re installing some new cool stuff and suddenly notice issues with other projects. Having a dedicated build machine is one idea. </li>



<li>Another is going the &#8220;GitHub Actions&#8221; way which also has its benefits.
<ul class="wp-block-list">
<li>Runners are virtual machines that are provisioned and &#8220;fresh&#8221; every time. It&#8217;s always the very same environment to start with for each build, no matter what you&#8217;re doing during a build.</li>



<li>You don&#8217;t need to maintain many different build machines yourself. This can even solve some security issues, as it&#8217;s probably not your main interest of keeping everything up to date. I prefer to focus on programming and not on system maintenance.</li>



<li>However, the runners might be outdated in a few years &#8211; and new runners (with newer operating systems) will be available.
<ul class="wp-block-list">
<li>That&#8217;s usually not a very big issue on macOS and Windows since their build tools are usually backwards compatible for quite a long time.</li>



<li>Especially for Linux we have decided not to rely on the provided host but use a Docker Image and run the builds inside a Docker container. So you can, for example, use a Docker container with an image that perfectly fits your lowest compatibility needs, while the host itself might only have (too) new libraries installed.</li>
</ul>
</li>
</ul>
</li>



<li>Start putting all your steps and scripts into a workflow &#8211; be it just on your dedicated build environment, or using GitHub Actions.
<ul class="wp-block-list">
<li>Certainly separate them per required host (macOS / Windows / Linux).</li>



<li>Split a long script into smaller parts, e.g: Download LibreSSL, Build x86_64, Build arm64, Build Universal.
<ul class="wp-block-list">
<li>In case of an error, it&#8217;s easier to see that &#8220;macOS: Build arm64 has failed&#8221;, compared to &#8220;Build for macOS has failed&#8221; (because it&#8217;s all in a lengthy script).</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>



<li>Running the workflows manually is great and likely more than good enough.<br>Once the project activity grows (e.g. you&#8217;re receiving a lot of pull request or have many developers working on it):
<ul class="wp-block-list">
<li>Ensure that the build and test workflows are running on each pull request, require them to complete successfully before being able to merge. That by the way has been explained in the previous blog post <a href="https://blog.xojo.com/2022/10/19/xojo-github-actions/">Xojo GitHub Actions</a>. After all, no one wants to find out after many commits that the build is no longer working, and then have to search the history for what might have caused the problem.</li>
</ul>
</li>



<li>You&#8217;ll notice that once it&#8217;s all automated you no longer miss it. It causes no additional effort for you, instead bringing a lot of benefits <em>(such as noticing early when code changes cause oddities in tests and builds)</em>.</li>
</ul>



<h3 class="wp-block-heading"><strong>A Fun Fact</strong></h3>



<p>In the early days a build of the CubeSQLPlugin often took a couple of hours &#8211; and with personal attention needed so that one couldn&#8217;t work on much else during that time.</p>



<p>The first iteration of the workflows reduced the entire build (LibreSSL + Plugin) to about 25 minutes. With no attention and manual interaction needed &#8211; which means more time for other work. What took the longest was the LibreSSL ARM builds &#8211; they have been run on Intel hardware with QEMU for ARM emulation.</p>



<p>Back to the beginning of this article and <a href="https://forum.xojo.com/t/2025-year-of-code-february/" target="_blank" rel="noreferrer noopener">Xojo&#8217;s 2025 Year of Code</a> initiative &#8211; in February I have improved the workflows to use the newly available GitHub Actions runners that run on ARM hardware. Which means that no more emulation is required &#8211; and the build time has now come down to about 4 minutes for LibreSSL Libs <em>(required only once per new LibreSSL version)</em> and 2.5 minutes for the Xojo database plugin for all its supported platforms and architectures!</p>



<h2 class="wp-block-heading"><strong>That&#8217;s All Folks!</strong></h2>



<p>I hope this brief introduction of how to use <a href="https://docs.github.com/en/actions/writing-workflows" target="_blank" rel="noreferrer noopener">GitHub Actions workflows</a> and <a href="https://www.docker.com" target="_blank" rel="noreferrer noopener">Docker</a> to build a <a href="https://documentation.xojo.com/topics/custom_controls/plugins_sdk.html" target="_blank" rel="noreferrer noopener">Xojo plugin</a> has been helpful to some, food for thought to others.</p>



<p><em><em>Jürg Otter is a long term user of Xojo and working for </em><a href="https://www.cmiag.ch/"><em>CM Informatik AG</em></a><em>. Their Application </em><a href="https://cmi-bildung.ch/"><em>CMI LehrerOffice</em></a><em> is a Xojo Design Award Winner 2018. In his leisure time Jürg provides some </em><a href="https://www.jo-tools.ch/xojo/"><em>bits and pieces for Xojo Developers</em></a><em>.</em></em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Working with PDFTableDataSource from Sources Other Than RowSet</title>
		<link>https://blog.xojo.com/2024/11/25/working-with-pdftabledatasource-from-sources-other-than-rowset/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Mon, 25 Nov 2024 14:00:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[PDF]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=14035</guid>

					<description><![CDATA[Some users on the Xojo Forum recently asked how to render tables on a PDFDocument using the PDFTableDataSource class interface when the dat source does&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Some users on the <a href="https://forum.xojo.com/t/pdftable-created-manually-not-from-rowset-web/83000">Xojo Forum recently</a> asked how to render tables on a PDFDocument using the <a href="https://documentation.xojo.com/api/pdf/pdftabledatasource.html#pdftabledatasource">PDFTableDataSource</a> class interface when the dat source does not comes from a RowSet. Well, the truth is that it doesn&#8217;t differ too much from when it does! Continue reading and I will show you how using a simple example.</p>



<span id="more-14035"></span>



<p>The trick is to handle the data coming from any source with the right data structure, for example, an Array. This example uses a plain text file with several lines and columns separated by the Tab character as the source of the data.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>In order to follow this tutorial, <a href="https://drive.google.com/file/d/14Ve5TllFLGK2TE4MTr4fjutpI4aGV0UE/view?usp=sharing">download the example project</a>.</p>
</blockquote>


<div class="wp-block-image">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1816" height="2378" src="https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-12.27.22.png" alt="" class="wp-image-14041" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-12.27.22.png 1816w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-12.27.22-229x300.png 229w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-12.27.22-782x1024.png 782w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-12.27.22-768x1006.png 768w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-12.27.22-1173x1536.png 1173w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-12.27.22-1564x2048.png 1564w" sizes="auto, (max-width: 1816px) 100vw, 1816px" /></figure>
</div>


<h2 class="wp-block-heading">Getting the Data</h2>



<p>As you can see in the example project, the text file has been added to the project using a Build > Copy Files Step (the file name is &#8220;Data.txt&#8221;), so it is copied to the Resources folder of the compiled app and available for both debugging and release builds.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="2224" height="1464" src="https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.18.01.png" alt="" class="wp-image-14037" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.18.01.png 2224w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.18.01-300x197.png 300w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.18.01-1024x674.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.18.01-768x506.png 768w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.18.01-1536x1011.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.18.01-2048x1348.png 2048w" sizes="auto, (max-width: 2224px) 100vw, 2224px" /></figure>
</div>


<p>Then, in the Pressed event of the Button1 control placed on the default Window1, we will access the contents of the file in order to store each of the lines in the Data Array of strings:</p>



<pre class="wp-block-code"><code>Var f As FolderItem = SpecialFolder.Resource("data.txt")
If f &lt;&gt; Nil And f.Exists Then
  Var tis As TextInputStream = TextInputStream.Open(f)
  Var s As String = tis.ReadAll
  data = s.ToArray(EndOfLine.macOS)
End If</code></pre>



<h2 class="wp-block-heading">PDFRenderer Subclass</h2>



<p>The next step is to create a custom class that will implement the methods provided by the PDFTableDataSource Class Interface. We will name the class &#8220;PDFRenderer&#8221;. Then, from the Inspector Panel, click on the Interfaces button, check the PDFTableDataSource interface and confirm the selection.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1076" height="1004" src="https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.03.21.png" alt="" class="wp-image-14036" srcset="https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.03.21.png 1076w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.03.21-300x280.png 300w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.03.21-1024x955.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/11/Screenshot-2024-11-20-at-11.03.21-768x717.png 768w" sizes="auto, (max-width: 1076px) 100vw, 1076px" /></figure>
</div>


<p>Once done, the methods from the class interface will be automatically added to the class:</p>



<ul class="wp-block-list">
<li>AddNewRow</li>



<li>Completed</li>



<li>HeaderHeight</li>



<li>MergeCellsForRow</li>



<li>PaintCell</li>



<li>PaintHeaderContent</li>



<li>RowHeight</li>
</ul>



<p>Over these we will add three more methods:</p>



<h4 class="wp-block-heading">The class Constructor:</h4>



<pre class="wp-block-code"><code>Public Sub Constructor(document As PDFDocument)
  // Simple constructor… just assign the received object
  // to the "Document" property
  
  Self.document = document
  proxy = New Picture(document.Graphics.Width, document.Graphics.Height)
End Sub</code></pre>



<p>Assign the received PDFDocument instance to the document property. It also creates a graphics instance that will be used as a proxy for calculating the height of the processed text from the data (that won&#8217;t be necessary starting with Xojo 2024r4 where some PDFDocument bugs related with that are already fixed).</p>



<h4 class="wp-block-heading">DrawTable:</h4>



<p>This method is the one that will be called to start drawing the table. It will initialize some properties from the received Data (yeah, you can see a property named as &#8220;DrawingFromRowSet&#8221;, but I left that one on purpose because this project is based on the one dealing with the data coming from a RowSet, so you can see how similar all the process is):</p>



<pre class="wp-block-code"><code>Public Sub DrawTable(data() As String, headers() As String)
  // This is the method we call on the Class to draw
  // a table on a PDFDocument based on an Array of Strings
  
  If document &lt;> Nil And data.LastIndex &lt;> -1 Then
    
    // Assign the received Array to the "Data" property
    Self.data = data
    
    // Assign the received headers to the "Headers" property
    Self.Headers = Headers
    
    // Hey! We are going to create a table from an Array
    // so we use a Boolean property as a flag for that
    // (Yeah, we can do it using other techniques, but this
    // is easy enough for this purpose… while leave this
    // helper class "open" enough for drawing tables based on other
    // data sources).
    Self.DrawingFromRowSet = True
    
    // How many columns are we going to draw?
    // Well… as many as columns (TABs) in any of the Array items (for example, the first one).
    Var totalColumns As Integer = data(0).CountFields(Chr(9))
    
    // This is going to be the "drawable area" on the page
    // for the table = total page width less the left and right margins
    Var totalWidth As Double = document.Graphics.Width - 40 // 40 = 20 points left/right margins
    
    // Creating the PDFTable object here!
    Var table As New PDFTable
    
    // We want to repeat the headers on every page
    table.HasRepeatingHeader = True
    
    // Setting the column count for the table
    table.ColumnCount = totalColumns
    
    // …and the width for every column.
    table.ColumnWidths = CalculateColumnWidths(totalWidth, totalColumns)
    
    // The object from this class will be the responsible
    // of handling all the methods associated with the
    // PDFTableDataSouce Class Interface
    table.DataSource = Self
    
    // Setting the Top and Bottom margins for the drawing
    // of the table on every PDF page
    table.TopMargin = 20
    table.BottomMargin = 20
    
    // …and finally we instruct the PDFDocument object
    // to draw the table!
    document.AddTable(table, 20, 0)
    
    // Lastly, clearing the flag
    Self.DrawingFromRowSet = False
    
  End If
End Sub</code></pre>



<h4 class="wp-block-heading">CalculateColumnWidths Method</h4>



<p>This one will calculate the widths for each column to be rendered in the final PDF document.</p>



<pre class="wp-block-code"><code>Protected Function CalculateColumnWidths(TotalWidth As Double, NumberOfColumns As Double) As String
  // Helper method to get a string for the column widths
  
  ColumnWidth = TotalWidth / NumberOfColumns
  Var s() As String
  
  For n As Integer = 0 To NumberOfColumns - 1
    s.Add(Str(ColumnWidth))
  Next
  
  Return String.FromArray(s, ",")
End Function</code></pre>



<h3 class="wp-block-heading">Class Properties</h3>



<p>Because we need to track things like what the current row or column is to be rendered, the headers, etc., our class needs to add some properties:</p>



<ul class="wp-block-list">
<li>Protected Property ColumnWidth As Integer</li>



<li>Public Property CurrentColumn As Integer</li>



<li>Protected Property CurrentHeight As Integer</li>



<li>Protected Property CurrentRow As Integer</li>



<li>Protected Property data() As String</li>



<li>Protected Property document As PDFDocument</li>



<li>Protected Property DrawingFromRowSet As Boolean</li>



<li>Public Property Headers() As String</li>



<li>Protected Property LastRow As Integer</li>



<li>Protected Property proxy As Picture</li>
</ul>



<h3 class="wp-block-heading">PDFTableDataSouce Methods</h3>



<p>Time to visit the methods added by the PDFTableDataSouce class interface. These are the methods called by the <a href="https://documentation.xojo.com/api/pdf/pdftable.html#pdftable">PDFTable</a> object each time it needs to retrieve a piece of information during the rendering on the PDFDocument, as for example if it needs to add a new row, the height for the Header or the current row… and the painting of the row itself!</p>



<h4 class="wp-block-heading">AddNewRow Method</h4>



<p>This one is called by the PDFTable object in order to know if it needs to render a new row in the table:</p>



<pre class="wp-block-code"><code>Protected Function AddNewRow(rowCount As Integer) As Boolean
  // Part of the PDFTableDataSource interface.
  
  If DrawingFromRowSet And data.LastIndex &lt;> -1 then
    
    // We are going to draw as many rows as rows are in the
    // "data" array
    Return rowCount &lt;= data.LastIndex
    
  End If
End Function</code></pre>



<h4 class="wp-block-heading">Completed method</h4>



<p>This is the method invoked by the PDFTable object when it finished drawing the table, so it is your chance to take additional operations, for example, numbering every page of the PDFDocument (we can&#8217;t anticipate that before drawing the table itself):</p>



<pre class="wp-block-code"><code>Protected Sub Completed(x As Double, y As Double)
  // Part of the PDFTableDataSource interface.
  
  // This method is called once the table has been drawed
  // so let's "print" the page number on every page
  // of the PDF Document
  
  Static pageNumber As String = "Page: "
  
  If document &lt;> Nil Then
    Var g As Graphics = document.Graphics
    
    For n As Integer = 1 To document.PageCount
      document.CurrentPage = n
      g.DrawText(pageNumber + Str(n), g.Width - g.TextWidth(pageNumber + Str(n)) - 20, g.Height - g.FontAscent)
    Next
    
  End If
End Sub</code></pre>



<h4 class="wp-block-heading">HeaderHeight Method</h4>



<p>This is the method called by the PDFTable object the returns the height we want to use for rendering the header on the first page and, optionally, on every new page required (added to the document) by rendering the table. In this case we will return a fixed value:</p>



<pre class="wp-block-code"><code>Protected Function HeaderHeight() As Double
  // Part of the PDFTableDataSource interface.
  
  // Returning a fixed height value for the headers
  Return 20
End Function</code></pre>



<h4 class="wp-block-heading">MergeCellsForRow Method</h4>



<p>The PDFTableDataSouce class interface also has the ability to merge cells in the current row, so you can create more elaborated tables. We are not going to use that feature in this example project so it will be empty, no code to be executed.</p>



<h4 class="wp-block-heading">PaintHeaderContent Method</h4>



<p>This is the method in charge of rendering the header for the table, so it receives its own Graphic context with the provided height from the HeaderHeight method and the width calculated from the total of columns (headers) needed to be rendered:</p>



<pre class="wp-block-code"><code>Protected Sub PaintHeaderContent(g As Graphics, column As Integer)
  // Part of the PDFTableDataSource interface.
  
  // Painting the headers for the table
  If column &lt;= Self.Headers.LastIndex Then
    
    Var s As String = headers(column)
    g.DrawingColor = Color.Black
    g.FillRectangle(0, 0, g.Width, g.Height)
    g.DrawingColor = Color.White
    g.DrawText(s, g.Width / 2 - g.TextWidth(headers(column)) / 2, g.Height / 2 + g.FontAscent / 2)
    
  End If
End Sub</code></pre>



<h4 class="wp-block-heading">RowHeight Method</h4>



<p>As the class interface provides a method so we can provide the height for the header, we also have a RowHeight method that the PDFTable object will call before it calls the method in charge of doing the row drawing itself. That way we can have rows of different height based on the height of the own text to be rendered!</p>



<pre class="wp-block-code"><code>Protected Function RowHeight() As Double
  // Part of the PDFTableDataSource interface.
  
  // We need to calculate the height for every row in the Table
  // so let's calculate that based on the taller of the texts (columns)
  // based on text wrapping
  
  If CurrentRow &lt;= data.LastIndex Then
    CurrentHeight = 0
    Var s() As String = data(CurrentRow).Split(Chr(9))
    Var g As Graphics = proxy.Graphics
    Var itemHeight As Integer
    For Each item As String In s
      itemHeight = g.TextHeight(item, ColumnWidth - 40)
      If itemHeight > CurrentHeight Then
        CurrentHeight = itemHeight
      End If
    Next
    
    CurrentRow = CurrentRow  + 1 
    
    Return CurrentHeight + 20
  End If
End Function</code></pre>



<h4 class="wp-block-heading">PaintCell Method</h4>



<p>Lastly, this is the method called by the PDFTable object when it is time to render a given cell for the current row in the table. It receives as parameters both a <a href="https://documentation.xojo.com/api/pdf/pdfgraphics.html#notes">graphic context</a> with the width and height already suited for the data to be rendered, and also the row and column values so you can get the appropriate data for a given cell from the data source:</p>



<pre class="wp-block-code"><code>Protected Sub PaintCell(g As Graphics, row As Integer, column As Integer)
  // Part of the PDFTableDataSource interface.
  
  // Here is where we really do the drawing
  // the received "g" parameter is for a particula table cell
  // based on the row / column parameters
  // so the origin for X/Y coordinates is at 0,0
  
  If DrawingFromRowSet And data.LastIndex >= row Then
    // Drawing the outer rectangle for the cell
    g.DrawRectangle(0, 0, g.Width, g.Height)
    
    // retrieving the text to be drawn from the Array,
    // using the row and column parameters for that.
    Var s As String = data(row).NthField(Chr(9), column + 1)
    
    // Centering vertically the text on the table cell
    // while the X offset is fixed at 5 points.
    g.DrawText(s, 5, g.Height / 2 + g.FontAscent / 2, ColumnWidth - 20)
    
    // Let's keep track of the last row drawed
    LastRow = row
    
  End If
End Sub</code></pre>



<h2 class="wp-block-heading">Time to push the Start button!</h2>



<p>Everything is setup now in our PDFRenderer class, so let&#8217;s get back to the Pressed event of the Button1 control on the default Window1 window. You will remember from the beginning of this tutorial that this is the place where we retrieved the data from the text file. So let&#8217;s add the code to create a PDFDocument instance, a new PDFRenderer instance and do the rendering of the table itself from the data:</p>



<pre class="wp-block-code"><code>If data.LastIndex &lt;&gt; -1 Then
  
  // Creating a new PDFDocument Instance
  Var d As New PDFDocument
  
  // Creating the PDFTable renderer helper object (we pass the PDFDocument object to it in the Constructor)
  Var PDFRender As New PDFRenderer(d)
  
  // These will be the headers drawed in the Table
  Var headers() As String = Array("Name", "Surname", "Address", "City", "Email")
  
  // And let's instruct the PDFTable renderer helper to draw the table
  // based in the SQLite database rowset we got in the previous step
  PDFRender.DrawTable(data, headers)
  
  // …and save the resulting PDF File to the Desktop
  Var out As FolderItem = SpecialFolder.Desktop.Child("TableFromTextFile.pdf")
  
  If out &lt;&gt; Nil Then
    d.Save(out)
    out.Open
  End If
End If</code></pre>



<h2 class="wp-block-heading">Conclusion</h2>



<p>As you see, and if this tutorial is compared with <a href="https://blog.xojo.com/2023/01/17/pdftable-from-a-rowset/">PDFTable from a RowSet</a>, creating a PDFTable doesn&#8217;t differ too much when using a data source other than a RowSet. All you need, basically, is to keep track of the current row, know if you need to render more rows and a base data structure that can be used to retrieve the data to be rendered in a given Row/Column.</p>



<p>As always, you can learn more about other features provided by <a href="https://documentation.xojo.com/api/pdf/pdfdocument.html#pdfdocument">PDFDocument in the Xojo Documentation</a>.</p>



<p>Happy Coding!</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Android Tablet Support</title>
		<link>https://blog.xojo.com/2024/10/01/android-tablet-support/</link>
		
		<dc:creator><![CDATA[Paul Lefebvre]]></dc:creator>
		<pubDate>Tue, 01 Oct 2024 15:31:26 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[2024r3]]></category>
		<category><![CDATA[Cats]]></category>
		<category><![CDATA[Layout Editor]]></category>
		<category><![CDATA[Tablet]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=13552</guid>

					<description><![CDATA[Android apps made with Xojo have always worked on tablets, but until Xojo 2024r3 there has been no easy way to design your screens for&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Android apps made with Xojo have always worked on tablets, but until Xojo 2024r3 there has been no easy way to design your screens for tablets. Below are the new features that should make it easier to create Android tablet apps.</p>



<h2 class="wp-block-heading">Layout Editor</h2>



<p>The Layout Editor has been enhanced with two settings that can adjust the layout.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="288" height="62" src="https://blog.xojo.com/wp-content/uploads/2024/08/image-4.png" alt="" class="wp-image-13553"/></figure>



<p>The command bar has two toggle buttons. The first one already existed and it is for displaying the layout in portrait (the default) or landscape.</p>



<p>The new toggle button displays the layout in a phone size (the default) or a tablet size. When you switch between any of these toggles, the locking is applied to the controls on the layout.</p>



<p>The layout editor also remembers the selections so that the next time you visit the layout, it will be where you left it. Here you can see a landscape tablet layout:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="793" src="https://blog.xojo.com/wp-content/uploads/2024/08/image-5-1024x793.png" alt="" class="wp-image-13554" srcset="https://blog.xojo.com/wp-content/uploads/2024/08/image-5-1024x793.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/08/image-5-300x232.png 300w, https://blog.xojo.com/wp-content/uploads/2024/08/image-5-768x595.png 768w, https://blog.xojo.com/wp-content/uploads/2024/08/image-5-1536x1189.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/08/image-5-2048x1585.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">Default Screen</h2>



<p>The App Inspector has an additional Default Tablet Screen property. When your app is launched on a phone or tablet-sized device, the specified screen is what gets displayed.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="634" height="218" src="https://blog.xojo.com/wp-content/uploads/2024/08/image-6.png" alt="" class="wp-image-13555" srcset="https://blog.xojo.com/wp-content/uploads/2024/08/image-6.png 634w, https://blog.xojo.com/wp-content/uploads/2024/08/image-6-300x103.png 300w" sizes="auto, (max-width: 634px) 100vw, 634px" /></figure>



<p>You can also choose &#8220;None&#8221; for a default screen if you just want to share a single screen across both types of devices.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>If you choose &#8220;None&#8221; for the Default Phone Screen, then the app won&#8217;t launch on a phone. If you choose &#8220;None&#8221; for a Default Tablet Screen, the app will launch on the tablet using the Default Phone Screen, which matches prior behavior.</p>
</blockquote>



<h2 class="wp-block-heading">Design Considerations</h2>



<p>Xojo for Android uses locking to automatically adjust controls on the layout. Locking will work for certain types of layouts that you want to run on both phones and tablets, but may not be sufficient for more complex layouts. In these situations, you are better served by designing completely separate layouts for phone and tablet and then using the above Default Screen properties to show the correct one.</p>



<p>When you need to show additional screens, you can show the phone or tablet screen in code by checking the device using DeviceData.Orientation and DeviceData.UserInterfaceType.</p>



<pre class="wp-block-code"><code>If System.Device.UserInterfaceType = SystemDeviceData.UserInterfaceTypes.Tablet Then
  // Show tablet screen
  myTabletScreen.Show
Else
  // Show phone screen
  myPhoneScreen.Show
End If</code></pre>



<p>When you have multiple screens, it often makes sense to put the UI in containers and then use the containers on the different screens.</p>



<p>You can run your apps on a Tablet emulator for testing (you can create one using Android Studio) or you can debug directly on device. I enabled on-device debugging for a Lenovo Tab M10 Plus 3rd Gen with these steps:</p>



<ul class="wp-block-list">
<li>Go to Settings.</li>



<li>Select About tablet.</li>



<li>Tap 7 times in a row on the build number. This enabled Developer options. You may be prompted to enter your PIN.</li>



<li>Select System and then Developer Options.</li>



<li>Turn on USB debugging and give it permission.</li>
</ul>



<p>The tablet will now appear as a debug device in Xojo, allowing you to run your Android projects on it.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="3552" height="2893" src="https://blog.xojo.com/wp-content/uploads/2024/09/D0D09B0C-7135-4067-A893-29D1420CAA14.png" alt="" class="wp-image-13724" srcset="https://blog.xojo.com/wp-content/uploads/2024/09/D0D09B0C-7135-4067-A893-29D1420CAA14.png 3552w, https://blog.xojo.com/wp-content/uploads/2024/09/D0D09B0C-7135-4067-A893-29D1420CAA14-300x244.png 300w, https://blog.xojo.com/wp-content/uploads/2024/09/D0D09B0C-7135-4067-A893-29D1420CAA14-1024x834.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/09/D0D09B0C-7135-4067-A893-29D1420CAA14-768x626.png 768w, https://blog.xojo.com/wp-content/uploads/2024/09/D0D09B0C-7135-4067-A893-29D1420CAA14-1536x1251.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/09/D0D09B0C-7135-4067-A893-29D1420CAA14-2048x1668.png 2048w" sizes="auto, (max-width: 3552px) 100vw, 3552px" /></figure>



<h2 class="wp-block-heading">What about Chromebook?</h2>



<p><a href="https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps/" target="_blank" rel="noreferrer noopener">Some Chromebooks</a> are <a href="https://support.google.com/chromebook/answer/7021273" target="_blank" rel="noreferrer noopener">able to run Android apps</a> and <a href="https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps/" target="_blank" rel="noreferrer noopener">those that can</a> are also able to run Xojo&#8217;s Android apps. Android apps can be installed via the Play Store (give <a href="https://play.google.com/store/apps/details?id=com.xojo.catsup&amp;hl=en_US&amp;pli=1" target="_blank" rel="noreferrer noopener">CatsUp a try</a>) or manually, although doing it manually is rather difficult (I have not yet gotten that working on my Chromebook). The Play Store options works well, however. Here&#8217;s CatsUp from the Play Store running on my Lenovo 300e Chromebook:</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="768" src="https://blog.xojo.com/wp-content/uploads/2024/08/F98FE354-8315-4BCA-B204-C9D24EE0B86B_1_102_o-1024x768.jpeg" alt="" class="wp-image-13576" style="width:913px;height:auto" srcset="https://blog.xojo.com/wp-content/uploads/2024/08/F98FE354-8315-4BCA-B204-C9D24EE0B86B_1_102_o-1024x768.jpeg 1024w, https://blog.xojo.com/wp-content/uploads/2024/08/F98FE354-8315-4BCA-B204-C9D24EE0B86B_1_102_o-300x225.jpeg 300w, https://blog.xojo.com/wp-content/uploads/2024/08/F98FE354-8315-4BCA-B204-C9D24EE0B86B_1_102_o-768x576.jpeg 768w, https://blog.xojo.com/wp-content/uploads/2024/08/F98FE354-8315-4BCA-B204-C9D24EE0B86B_1_102_o-1536x1152.jpeg 1536w, https://blog.xojo.com/wp-content/uploads/2024/08/F98FE354-8315-4BCA-B204-C9D24EE0B86B_1_102_o.jpeg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>When run on the Chromebook, Android apps can have a size of Phone, Tablet or Resizable. You can see that as an option in the title bar of the above screenshot. For best results you probably want to design your apps for a landscape tablet.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Xojo for Android does not have direct support for Chromebook features at this time, but if there is something you&#8217;d like to see, be sure to create an Issue so that we can look into it.</p>
</blockquote>



<p>Since Chromebooks are just (low-end) Linux machines at their core, they can technically run Xojo Linux apps (x86 or ARM depending on the Chromebook), but this usually requires some sort of <a href="https://support.google.com/chromebook/answer/9145439" target="_blank" rel="noreferrer noopener">additional Linux installation</a>.</p>



<p>And of course, Chromebooks are typically meant to run web apps, which Xojo can also do quite well.</p>



<h2 class="wp-block-heading">Eddie&#8217;s Electronics Example Project</h2>



<p>The Android Eddie&#8217;s Electronics example project has been updated to run on both phones and tablets. It makes use of Containers to contain the UI and displays them using screens for the appropriate device type.</p>



<p>Here you can see the Customer screen for a tablet displays the list of customers on the left, with details and invoices on the right.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="676" src="https://blog.xojo.com/wp-content/uploads/2024/08/image-7-1024x676.png" alt="" class="wp-image-13557" srcset="https://blog.xojo.com/wp-content/uploads/2024/08/image-7-1024x676.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/08/image-7-300x198.png 300w, https://blog.xojo.com/wp-content/uploads/2024/08/image-7-768x507.png 768w, https://blog.xojo.com/wp-content/uploads/2024/08/image-7-1536x1014.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/08/image-7.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Compare that to the phone version which has you navigate between that information using separate screens.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="483" height="1024" src="https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncsc-483x1024.png" alt="" class="wp-image-13564" srcset="https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncsc-483x1024.png 483w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncsc-142x300.png 142w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncsc-768x1628.png 768w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncsc-725x1536.png 725w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncsc.png 850w" sizes="auto, (max-width: 483px) 100vw, 483px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="485" height="1024" src="https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncyb-485x1024.png" alt="" class="wp-image-13565" srcset="https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncyb-485x1024.png 485w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncyb-142x300.png 142w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncyb-768x1620.png 768w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncyb-728x1536.png 728w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-ncyb.png 856w" sizes="auto, (max-width: 485px) 100vw, 485px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="482" height="1024" src="https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-nddd-482x1024.png" alt="" class="wp-image-13566" srcset="https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-nddd-482x1024.png 482w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-nddd-141x300.png 141w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-nddd-768x1632.png 768w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-nddd-723x1536.png 723w, https://blog.xojo.com/wp-content/uploads/2024/08/SCR-20240829-nddd.png 852w" sizes="auto, (max-width: 482px) 100vw, 482px" /></figure>
</div>
</div>



<p>Tablet supports opens up a whole other category of users for your Android apps and should be particularly useful to those making bespoke apps for clients.</p>



<p><em>Paul learned to program in BASIC at age 13 and has programmed in more languages than he remembers, with Xojo being an obvious favorite. When not working on Xojo, you can find him talking about retrocomputing at <a href="https://goto10.substack.com" target="_blank" rel="noreferrer noopener">Goto 10</a> and </em>on Mastodon @lefebvre@hachyderm.io.</p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Enhanced Emoji Support: A Look into Graphics and String.Character Improvements</title>
		<link>https://blog.xojo.com/2024/06/26/enhanced-emoji-support-a-look-into-graphics-and-string-character-improvements/</link>
		
		<dc:creator><![CDATA[William Yu]]></dc:creator>
		<pubDate>Wed, 26 Jun 2024 15:00:00 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[20242r2]]></category>
		<category><![CDATA[Emoji]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[String]]></category>
		<category><![CDATA[Unicode]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=13136</guid>

					<description><![CDATA[Now in Xojo 2024r2, we've enhanced emojis with vivid colors!]]></description>
										<content:encoded><![CDATA[
<p>In the past, people used a two-character symbol : ) to add a smiley to their messages. Nowadays, we have full-color emojis for that purpose, so it&#8217;s time for Xojo to catch up unless we want to be seen as old <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f995.png" alt="🦕" class="wp-smiley" style="height: 1em; max-height: 1em;" />.</p>



<h2 class="wp-block-heading">Enhanced Emoji Rendering</h2>



<p>While Xojo&#8217;s Graphics class supported rendering emojis on Windows and Linux, their appearance was less appealing.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex"><div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="454" height="377" data-id="13137" src="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r1.png" alt="" class="wp-image-13137" srcset="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r1.png 454w, https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r1-300x249.png 300w" sizes="auto, (max-width: 454px) 100vw, 454px" /></figure>
</div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="303" height="263" data-id="13143" src="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r1Linux-1.png" alt="" class="wp-image-13143" srcset="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r1Linux-1.png 303w, https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r1Linux-1-300x260.png 300w" sizes="auto, (max-width: 303px) 100vw, 303px" /></figure>
</div></figure>



<p>Now in Xojo 2024r2, we&#8217;ve enhanced emojis with vivid colors!</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex"><div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="454" height="377" data-id="13138" src="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r2.png" alt="" class="wp-image-13138" srcset="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r2.png 454w, https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r2-300x249.png 300w" sizes="auto, (max-width: 454px) 100vw, 454px" /></figure>
</div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="303" height="263" data-id="13142" src="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r2Linux-1.png" alt="" class="wp-image-13142" srcset="https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r2Linux-1.png 303w, https://blog.xojo.com/wp-content/uploads/2024/06/Emoji2024r2Linux-1-300x260.png 300w" sizes="auto, (max-width: 303px) 100vw, 303px" /></figure>
</div></figure>



<p>You’ll notice that other parts of the Xojo framework that utilize the Graphics class, such as the ListBox, have also benefited from this update.</p>



<h2 class="wp-block-heading">Understanding Emojis at a Deeper Level</h2>



<p>Understanding how emojis are encoded is crucial for grasping how Xojo&#8217;s String type works and how we interpret them. Any text or emoji displayed on the screen can be considered a Unicode sequence or a series of Unicode sequences. At a deeper level, a single Unicode sequence can be constructed from multiple Unicode code points (i.e., bytes of data). For clarity, we&#8217;ll describe one character<strong> </strong>(whether simple text or an emoji) as being composed of Unicode sequence(s).</p>



<p>To demonstrate, let&#8217;s try to understand the woman <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f469.png" alt="👩" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img loading="lazy" decoding="async" width="913" height="628" src="https://blog.xojo.com/wp-content/uploads/2024/06/Woman1.png" alt="" class="wp-image-13149 size-full" srcset="https://blog.xojo.com/wp-content/uploads/2024/06/Woman1.png 913w, https://blog.xojo.com/wp-content/uploads/2024/06/Woman1-300x206.png 300w, https://blog.xojo.com/wp-content/uploads/2024/06/Woman1-768x528.png 768w" sizes="auto, (max-width: 913px) 100vw, 913px" /></figure><div class="wp-block-media-text__content">
<p>This particular woman is not very complex, it is made up of one single Unicode sequence (with corresponding code points).  Our String type can easily handle this one Unicode sequence and reports correctly that the Length of the String is 1 (even though the entire emoji is made up of 4 bytes of code points).</p>
</div></div>



<p>Now let&#8217;s take a look at a more complicated woman <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f469-200d-1f9b0.png" alt="👩‍🦰" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<div class="wp-block-media-text is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img loading="lazy" decoding="async" width="916" height="631" src="https://blog.xojo.com/wp-content/uploads/2024/06/Woman2-1.png" alt="" class="wp-image-13148 size-full" srcset="https://blog.xojo.com/wp-content/uploads/2024/06/Woman2-1.png 916w, https://blog.xojo.com/wp-content/uploads/2024/06/Woman2-1-300x207.png 300w, https://blog.xojo.com/wp-content/uploads/2024/06/Woman2-1-768x529.png 768w" sizes="auto, (max-width: 916px) 100vw, 916px" /></figure><div class="wp-block-media-text__content">
<p>The only difference visually is that this woman has fiery red hair.  This subtle change in appearance means that to construct this emoji requires not 1, not 2, but 3 distinct Unicode sequences (with a grand total of 11 bytes of code points).</p>
</div></div>



<p>Note that the reported length of the String is 3 Unicode sequences, even though we visually see only one character. This distinction is very important in understanding how String functions work. Xojo&#8217;s String functions inherently deal with Unicode sequences, not individual characters. When you do need to work with complex emojis like these, you will want to utilize our String.Characters API which effectively manages this complexity.</p>



<h2 class="wp-block-heading">An Optimized String.Characters</h2>



<p>Given the prevalence of emojis in everyday text and messages, properly handling them within Xojo&#8217;s String type is crucial. While we initially introduced this feature as String.Characters, it functioned as an iterator until Xojo 2024r2. Now, String.Characters returns a String array, which can still be iterated, ensuring existing code continues to work as before. Additionally, this update offers the added advantage of significantly improved speed, especially on Windows and Linux. Along with this update, we introduced String.CharacterCount, which is useful when you need to know the number of characters in a string without storing them all in an array.</p>



<p>To further understand emojis and their corresponding Unicode sequences, check out this <a href="https://www.unicode.org/emoji/charts/full-emoji-list.html">Full Emoji List</a>.</p>



<p><em><em><em>William Yu grew up in Canada learning to program BASIC on a Vic-20. He is Xojo’s resident Windows and Linux engineer, among his many other skills. Some may say he has joined the dark side here in the USA, but he will always be a Canadian at heart.</em></em></em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Popovers for Xojo Desktop, Web and iOS</title>
		<link>https://blog.xojo.com/2024/03/26/popovers-for-xojo-desktop-web-and-ios/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Tue, 26 Mar 2024 15:27:47 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[2024r1]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[Popovers]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[User Interface]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=12707</guid>

					<description><![CDATA[Starting with Xojo 2024r1 there's a new Window type in Xojo's UI/UX bag: Popovers. Popovers are kind of a Modal window with a more transient behavior and associated with a Parent control. A Popover will display the chosen content or layout, the same as when you design the user interface of a window or a Container control that will be included as part of another more complex design or displayed at runtime.]]></description>
										<content:encoded><![CDATA[
<p>Starting with Xojo 2024r1 there&#8217;s a new Window type in Xojo&#8217;s UI/UX bag: Popovers. Popovers are kind of a Modal window with a more transient behavior and associated with a Parent control. A Popover will display the chosen content or layout, the same as when you design the user interface of a window or a Container control that will be included as part of another more complex design or displayed at runtime.</p>



<span id="more-12707"></span>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="558" src="https://blog.xojo.com/wp-content/uploads/2024/03/Popover-1024x558.png" alt="" class="wp-image-12708" srcset="https://blog.xojo.com/wp-content/uploads/2024/03/Popover-1024x558.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/03/Popover-300x164.png 300w, https://blog.xojo.com/wp-content/uploads/2024/03/Popover-768x419.png 768w, https://blog.xojo.com/wp-content/uploads/2024/03/Popover-1536x837.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/03/Popover-2048x1116.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>And what do we mean by the transitory character of Popover windows? Well, and here comes one of the first differences from Modal windows. When a Popover is visible and the user clicks or taps on any other element of the graphical interface not contained in the Popover itself it will close, hence transitory. So, you don&#8217;t have to include the typical &#8220;Close window&#8221; button in a Popover (though you can), since it will close when the user clicks outside of it, i.e. when the Popover loses focus.</p>



<h3 class="wp-block-heading">Where are the Popover Window Contents?</h3>



<p>Where do you define the content displayed in a Popover window? While in your Web app, you can do it on any Container control added to the project. In the case of Desktop and iOS apps, you can do it on either the designs of a Container Control or any Window or Screen added to the project.</p>



<p>For example, here you can see that the content shown as a Popover in the previous screenshot is actually that of a Window (Window2) added to the project:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="558" src="https://blog.xojo.com/wp-content/uploads/2024/03/PopoverUI-1024x558.png" alt="" class="wp-image-12709" srcset="https://blog.xojo.com/wp-content/uploads/2024/03/PopoverUI-1024x558.png 1024w, https://blog.xojo.com/wp-content/uploads/2024/03/PopoverUI-300x163.png 300w, https://blog.xojo.com/wp-content/uploads/2024/03/PopoverUI-768x418.png 768w, https://blog.xojo.com/wp-content/uploads/2024/03/PopoverUI-1536x837.png 1536w, https://blog.xojo.com/wp-content/uploads/2024/03/PopoverUI-2048x1116.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>This introduces additional flexibility allowing your app&#8217;s logic to decide when to display a Window or Container instance as you have been doing until now, or display it as a Popover.</p>



<h3 class="wp-block-heading">Showing a Popover</h3>



<p>Displaying a Popover is very straightforward, whether it is created from a Container or a Window, the method is the same:</p>



<pre class="wp-block-preformatted">DesktopContainer.ShowPopover(parentControl As DesktopUIControl, facing As DesktopWindow.DisplaySides = DesktopWindow.DisplaySides.Bottom, detachable As Boolean = True)</pre>



<p>The explanation of the expected parameters is as follows:</p>



<ul class="wp-block-list">
<li><strong>ParentControl:</strong> The control that will act as the parent, or on which the Popover will be pointing to.</li>



<li><strong>Facing:</strong> The direction in which the Popover will point on the parent control; by default it will appear under the parent control, but you can use any of the values available in the DesktopWindow.DisplaySides enumerator (or the iOS equivalent).</li>



<li><strong>Detachable:</strong> This value only applies to macOS Popovers, since said operating system has the ability for the user to &#8220;detach&#8221; the Popover from the parent control as long as this value is defined as True. To detach, drag the Popover with the mouse cursor.</li>
</ul>



<p>Let&#8217;s say we have a window called Window2 and a button called MyButton. In the Pressed event of MyButton we can use the following lines of code to display Window2 as a Popover:</p>



<pre class="wp-block-preformatted">Var w As New Window2
w.ShowPopover(me)</pre>



<p>And that&#8217;s all!</p>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="1280" height="720" src="https://blog.xojo.com/wp-content/uploads/2024/03/PopoverDettach.gif" alt="" class="wp-image-12710"/></figure>



<h3 class="wp-block-heading">Platform Differences</h3>



<p>In the previous section we already pointed out the first: macOS is the only operating system that acts on the value indicated in the Detachable parameter. But there are some other considerations to keep in mind so you don&#8217;t get caught off guard:</p>



<ul class="wp-block-list">
<li><strong>Beyond the limits.</strong> Although in Windows and macOS Popovers can exceed the limits of the window on which the parent control is located, the position of the popover in Linux is restricted to the limits of the window that contains the parent control. This is the same for iOS and for Web pages.</li>



<li><strong>iPhone and iPad.</strong> When a Popover is displayed on an iPhone, it will look like a modal window; while if the same application is run on an iPad, the appearance will be the typical one expected from Popovers: the window pointing to the parent control in the indicated direction.</li>



<li><strong>On the Web.</strong> The contents of a Popover are those defined in a Container control, while on the rest of the platforms you can use both Container controls and Windows / Screens.</li>
</ul>



<h3 class="wp-block-heading">Why the Popover doesn&#8217;t &#8220;point&#8221; in the configured direction?</h3>



<p>By default it will try to do so according to the value that has been set in the &#8220;facing&#8221; parameter, but if it does not find enough space on the margins of the screen, then it will be displayed on the next side of the parent control in which there is available space for display the Popover in its full width and height; This is true on macOS, Linux, Web and iOS.</p>



<h3 class="wp-block-heading">Can I reuse a Popover?</h3>



<p>No, You can not. Whether you use a Container control or a window as the source of the UI design to be displayed by the Popover, do not lose sight of the fact that the Popover is just another type of window; and therefore behaves as such. That is, once it is closed either because it has lost focus or because you have closed it explicitly through code invoking the Close method, all the displayed controls and the rest of the internal structures will be closed / invalidated, so that although the instance If a Popover is not Nil (because, for example, you keep a reference to the window or container instance used as the UI source), you will not be able to invoke the ShowPopover method again on said instance but you will have to create a new instance of the Container/ Window used as the source of the UI to be displayed by the Popover.</p>



<h3 class="wp-block-heading">How can I communicate with the UI elements contained in the Popover?</h3>



<p>You would do so just as you would do with any other type of window that you have been using so far in your Xojo projects.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1280" height="720" src="https://blog.xojo.com/wp-content/uploads/2024/03/PopoverComm.gif" alt="" class="wp-image-12712"/></figure>
</div>


<h3 class="wp-block-heading">Conclusion</h3>



<p>Without a doubt, Popovers are a new way to improve the design of your apps&#8217; user interfaces, providing a more direct relationship between the user interface elements displayed in the Popover and the &#8220;parent&#8221; control in charge of displaying it. Furthermore, since you can use any window or container you want as the source of the UI to be displayed by the Popover, you will have greater flexibility about when you want to display them in a Popover or display them using any other of the available window types or, in the case of Containers, embedding them as part of other UI elements at runtime.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Free Xojo Linux and Raspberry Pi for Everyone</title>
		<link>https://blog.xojo.com/2024/03/26/free-linux-builds/</link>
		
		<dc:creator><![CDATA[Xojo]]></dc:creator>
		<pubDate>Tue, 26 Mar 2024 15:26:18 +0000</pubDate>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Source Control]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[2024r1]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[Beginner Tips]]></category>
		<category><![CDATA[Binary]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[GitLab]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Version Control]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=12631</guid>

					<description><![CDATA[Now with Xojo 2024r1 you can use Xojo's free IDE to build Linux desktop and console apps from Linux, macOS, or Windows, no license required.]]></description>
										<content:encoded><![CDATA[
<p>Beginning with Xojo 2024r1 you can use Xojo&#8217;s free IDE to build Linux desktop and console apps from Linux, macOS, or Windows, no license required.</p>



<h2 class="wp-block-heading" id="free-linux">Xojo Linux is Free</h2>



<p>We have made building for Linux free and included in the Xojo IDE! This means that on Linux you get a version control ready IDE, along with the ability to build desktop and console Linux apps (including Raspberry Pi), all for free. Launch Xojo 2024r1 (or any later release of <a href="https://xojo.com/download/" target="_blank" rel="noreferrer noopener">Xojo</a>) and open any desktop or console project, go to Build Settings, select Linux and in the Inspector choose the appropriate Architecture that matches the Linux OS you want your app to work on, either ARM or x86. Click Build to compile your project to a standalone app that you can then run on Linux &#8211; no license required.</p>



<h2 class="wp-block-heading">Wrapping Up</h2>



<p>All these changes were made based on feedback from the Xojo community and to better match the changing ways that people develop and share software projects. With this change Xojo continues its dedication to supporting Linux.</p>



<p><a href="https://xojo.com/download/">Download Xojo</a> to get started.</p>



<p>Edited 07/2025 to reflect changes to licensing. </p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Performance Improvements in Xojo Web</title>
		<link>https://blog.xojo.com/2023/12/12/performance-improvements-in-xojo-web/</link>
		
		<dc:creator><![CDATA[Ricardo Cruz]]></dc:creator>
		<pubDate>Tue, 12 Dec 2023 14:30:00 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[2023r4]]></category>
		<category><![CDATA[Optimization]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[webdev]]></category>
		<category><![CDATA[Xojo IDE]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=12313</guid>

					<description><![CDATA[The last big Xojo release in 2023 comes with a lot of performance improvements everywhere. The Xojo IDE itself is faster, which gives you Time to Reflect on Stack Optimization. In this post, I want to detail those improvements in Xojo 2023r4 that target Xojo Web including faster HandleURL responses, faster transfer rates in Windows and Linux, static assets cache control, Events improvements and a new experimental option.]]></description>
										<content:encoded><![CDATA[
<p>The last big Xojo release in 2023 comes with a lot of performance improvements everywhere. The Xojo IDE itself is <a href="https://blog.xojo.com/2023/12/12/small-and-simple-changes-to-speed-up-the-xojo-ide/">faster</a>, which gives you <a href="https://blog.xojo.com/2023/12/12/time-to-reflect-on-stack-optimization/">Time to Reflect on Stack Optimization</a>. In this post, I want to detail those improvements in Xojo 2023r4 that target Xojo Web including faster HandleURL responses, faster transfer rates in Windows and Linux, static assets cache control, Events improvements and a new experimental option.</p>



<span id="more-12313"></span>



<h3 class="wp-block-heading">Faster HandleURL Responses</h3>



<p>With Xojo Web you can create APIs backing your Mobile or Desktop applications. Just by updating to Xojo 2023r4, your web services will get the benefit of much faster responses and the ability to handle many more requests per second.</p>



<p>Just to compare between different Xojo versions, we’ve created some benchmarks using Xojo Web 1 (with Xojo 2019r3.2) and some Xojo Web 2 releases, 2023r3.1 and the latest one, 2023r4.</p>



<p>Here are the results, returning plain text:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="897" height="1024" src="https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-897x1024.png" alt="" class="wp-image-12314" srcset="https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-897x1024.png 897w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-263x300.png 263w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-768x877.png 768w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-1346x1536.png 1346w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms.png 1726w" sizes="auto, (max-width: 897px) 100vw, 897px" /></figure>
</div>


<p>And the same test, but this time returning a string generated by JSONItem:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="916" height="1024" src="https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-1-916x1024.png" alt="" class="wp-image-12315" srcset="https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-1-916x1024.png 916w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-1-268x300.png 268w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-1-768x859.png 768w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-1-1374x1536.png 1374w, https://blog.xojo.com/wp-content/uploads/2023/12/Avg-Latency-ms-1.png 1764w" sizes="auto, (max-width: 916px) 100vw, 916px" /></figure>
</div>


<p>Xojo 2023r4 is capable of serving around 20x more requests per second compared to Web 1, and around 4x compared to Xojo 2023r3.1. And thanks to the reduced latency time, your API will return the contents much faster.</p>



<h3 class="wp-block-heading">Faster Transfer Rates in Windows and Linux</h3>



<p>While working on performance, we’ve found that applications deployed on Windows and Linux were serving large files much slower than those deployed on macOS servers.</p>



<p>William improved the low level code for sockets on those targets, allowing the server to transfer large files at a much faster speed. During my tests, I’ve seen 10x faster transfer rates.</p>



<p>But this doesn’t affect only our Web target! If you are using a ServerSocket with TCPSocket classes, your Console and Desktop applications will also benefit from these improved transfer rates.</p>



<h3 class="wp-block-heading">Static Assets Cache Control</h3>



<p>In this release, we’ve also improved how the framework interacts with the browser. When your user visits your web application for the first time, it will store all of the required JavaScript and CSS files.</p>



<p>This was already happening in previous releases but with a subtle difference. From now on, the browser won’t even need to check with the server if the file has changed since the last visit. This translates to faster load times for your users and less server load for you since the server won’t need to handle all those requests anymore.</p>



<h3 class="wp-block-heading">Improved Compile Time</h3>



<p>In this release, Xojo Web will compile your projects a few seconds faster. The first time you compile your project, it will cache the result of some processes that are only changing between Xojo releases.</p>



<p>Here is a comparison between 2023r3.1 and 2023r4, both are running a debug session of a blank project:</p>



<figure class="wp-block-video aligncenter"><video height="672" style="aspect-ratio: 2558 / 672;" width="2558" controls src="https://blog.xojo.com/wp-content/uploads/2023/12/web-blank-project-compile-time.mp4"></video></figure>



<p>That’s around 3-4 seconds faster every time a project compiles.</p>



<h3 class="wp-block-heading">Events Improvements</h3>



<p>Some web controls were sending information from the browser to the server more frequently than needed. For example, a WebContainer.Scrolled event was being sent for every scrolled pixel, causing unnecessary server load.</p>



<p>We’ve been reviewing these controls and have made some improvements:</p>



<ul class="wp-block-list">
<li>WebCombobox</li>



<li>WebContainer</li>



<li>WebSearchField</li>



<li>WebTextArea</li>



<li>WebTextField</li>



<li>WebUploader</li>
</ul>



<p>All of them will continue communicating their state updates, but the framework will focus on delivering the latest value available.</p>



<p>For example, in a WebSearchField, if the user writes &#8220;hello&#8221;, the framework will send the first key press (&#8220;h&#8221;) as soon as it happens. The next event it will send will depend on how fast the user types, so it can be either &#8220;hel&#8221; or maybe directly &#8220;hello&#8221;. What is guaranteed is the TextChanged event will be fired for the final value (&#8220;hello&#8221; in this example).</p>



<h3 class="wp-block-heading">New Experimental Option</h3>



<p>WebSession has a new experimental option available, disabled by default. “<a href="https://documentation.xojo.com/api/web/websession.html#websession-sendeventsinbatches">Send Events in Batches</a>”:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="596" height="714" src="https://blog.xojo.com/wp-content/uploads/2023/12/Interfaces.png" alt="" class="wp-image-12317" srcset="https://blog.xojo.com/wp-content/uploads/2023/12/Interfaces.png 596w, https://blog.xojo.com/wp-content/uploads/2023/12/Interfaces-250x300.png 250w" sizes="auto, (max-width: 596px) 100vw, 596px" /></figure>
</div>


<p>Turning it on will make our JavaScript framework group different event notifications in a single request. Projects that contain complex layouts that show and hide several controls at once may benefit from snappier GUIs.</p>



<p>As a side effect, it will also ensure the events arrive to the server in the correct order. Think about a login screen where the user has an email field, a password and a “Login” button with the Default setting turned on (to allow the user to trigger the Pressed event by pressing the Enter key). When sending the events in different requests, because of network latencies, the WebButton.Pressed event could arrive at the server before the last WebTextField.TextChanged.</p>



<p>We would love to hear your experiences when turning on this experimental option. If you find an issue, <a href="https://tracker.xojo.com/xojoinc/xojo/-/issues/new" target="_blank" rel="noreferrer noopener">please report it</a>.</p>



<h3 class="wp-block-heading">Wrapping Up</h3>



<p>As always, while Xojo 2023r4 comes with a lot of performance improvements, it also comes with bug fixes and feature requests. I want to again thank everyone who took the time to create new Bug reports and Feature Requests, and tested the pre-releases.</p>



<p>We’re looking forward to see what you build with it!</p>



<p><em>Ricardo has always been curious about how things work. Growing up surrounded by computers</em> he became interested in <em>web technologies in the dial-up connections era. Xojo has been his secret weapon and language of preference since 2018. When he’s not online, chances are he will be scuba diving … or crocheting amigurumis. Find Ricardo on Twitter <a href="https://web.archive.org/web/20220805000833/https://www.twitter.com/piradoiv" target="_blank" rel="noreferrer noopener">@piradoiv</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		<enclosure url="https://blog.xojo.com/wp-content/uploads/2023/12/web-blank-project-compile-time.mp4" length="538605" type="video/mp4" />

			</item>
		<item>
		<title>Simplicity and Security, Xojo Cloud is Ideal Hosting for Xojo Web Apps</title>
		<link>https://blog.xojo.com/2023/10/30/simplicity-and-security-xojo-cloud-is-ideal-hosting-for-xojo-web-apps/</link>
		
		<dc:creator><![CDATA[Xojo]]></dc:creator>
		<pubDate>Mon, 30 Oct 2023 16:00:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[App Hosting]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Security-Enhanced Linux]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLite]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[SSL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[webdev]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=11553</guid>

					<description><![CDATA[Xojo Cloud is the premier hosting solution for developers looking for a reliable, secure and high-performance hosting environment for Xojo web applications. Xojo Cloud is developed specifically for Xojo web applications and offers a range of benefits for you and your applications that make it worth the investment.]]></description>
										<content:encoded><![CDATA[
<p>Xojo Cloud is the premier hosting solution for developers looking for a reliable, secure and high-performance hosting environment for Xojo web applications. Xojo Cloud is developed specifically for Xojo web applications and offers a range of benefits for you and your applications that make it worth the investment.</p>



<p>Xojo Cloud is <strong>optimized for Xojo applications</strong>. It is specifically designed to provide the best possible performance and stability for Xojo web applications. We optimize Xojo Cloud for performance, so your web apps will run smoothly and seamlessly. You can even monitor server stats from within the Xojo IDE.</p>



<p>Xojo Cloud is <strong>focused on simplicity and ease of use</strong> and requires zero configuration. Designed with the Xojo developer in mind, Xojo Cloud&#8217;s Control Panel makes it easy to deploy and manage web applications. Purchase a server, open a web project in Xojo and click Deploy to upload and install to Xojo Cloud. Not just for web apps, Xojo Cloud includes Apple Push Notification server (APNs) support for your iOS apps. Set up SSL, PostrgeSQL, MySQL, SFTP and a SSH Tunnel with a click in the Xojo Cloud Control Panel (the Control Panel itself is a Xojo web app). The administration of a web server is a significant and constant task; Xojo Cloud allows you to leave that behind and focus on your projects.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="386" data-id="12082" src="https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Apps-Tab-1024x386.png" alt="Xojo Cloud Control Panel Apps Tab" class="wp-image-12082" srcset="https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Apps-Tab-1024x386.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Apps-Tab-300x113.png 300w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Apps-Tab-768x289.png 768w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Apps-Tab-1536x579.png 1536w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Apps-Tab-2048x772.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="384" data-id="12083" src="https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Options-Tab-1024x384.png" alt="Xojo Cloud Control Panel Options Tab" class="wp-image-12083" srcset="https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Options-Tab-1024x384.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Options-Tab-300x112.png 300w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Options-Tab-768x288.png 768w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Options-Tab-1536x576.png 1536w, https://blog.xojo.com/wp-content/uploads/2023/09/Xojo-Cloud-Control-Panel-Options-Tab-2048x768.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<p>We take security very seriously. Xojo Cloud <strong>offers advanced security features</strong> to protect your data from cyberthreats, including a smart firewall, intrusion and hacking detection and Security-Enhanced Linux. Unlike most hosting solutions that provide little to no security, each Xojo Cloud server is built with our state-of-the-art, industrial-strength, multi-tiered security system woven into its very core.&nbsp;</p>



<p>Additional features and benefits include daily automatic backups, load balancing and support from the Xojo team. With nine global hosting locations, you are able to host your Xojo web applications close to your users, for speed and an ideal overall experience. </p>



<div class="wp-block-columns are-vertically-aligned-top is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-top is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="576" src="https://blog.xojo.com/wp-content/uploads/2023/09/Add-a-heading-1024x576.png" alt="Xojo Cloud offers 3 packages offering options on RAM, storage and vCPU starting at $49/month. " class="wp-image-12078" srcset="https://blog.xojo.com/wp-content/uploads/2023/09/Add-a-heading-1024x576.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/09/Add-a-heading-300x169.png 300w, https://blog.xojo.com/wp-content/uploads/2023/09/Add-a-heading-768x432.png 768w, https://blog.xojo.com/wp-content/uploads/2023/09/Add-a-heading-1536x864.png 1536w, https://blog.xojo.com/wp-content/uploads/2023/09/Add-a-heading.png 1600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>
</div>



<p>In addition to the <a href="http://xojo.com/store/#cloud">standard options</a>, Xojo Cloud servers with more RAM, storage and Virtual CPUs are available. Contact&nbsp;<a href="mailto:hello@xojo.com">customer support</a>&nbsp;for details about personalized plans.&nbsp;</p>



<p>Whether you are a seasoned Xojo developer or just getting started, Xojo Cloud provides an intuitive and user-friendly hosting solution for Xojo web applications. Today is a good day to start using Xojo Cloud, visit our <a href="https://xojo.com/cloud/">website</a> to learn more or see the Xojo <a href="https://xojo.com/store/#cloud">Store</a> to pick your package and location.</p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Graphic Charts for Xojo Desktop and Mobile</title>
		<link>https://blog.xojo.com/2023/03/28/graphic-charts-for-xojo-desktop-and-mobile/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Tue, 28 Mar 2023 11:00:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Charts]]></category>
		<category><![CDATA[Data Visualization]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[Graphing]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Xojo Code]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=11320</guid>

					<description><![CDATA[Graphic Charts support is new in Xojo Desktop and Xojo Mobile (iOS) in Xojo 2023r1. Graphic Charts offer a similar look and behavior to what you find when using Xojo's WebChart control in web projects. Let's review all the things you can do with the new DesktopChart in your desktop projects, and new MobileChart in your iOS projects.]]></description>
										<content:encoded><![CDATA[
<p>Graphic chart support is new in Xojo Desktop and Xojo Mobile (iOS) in Xojo 2023r1. The new DesktopChart and MobileChart offer a similar look and behavior to what you find when using Xojo&#8217;s WebChart. Let&#8217;s review all the things you can do with the new DesktopChart and MobileChart in your Xojo projects.</p>



<span id="more-11320"></span>



<p>Just as with Xojo&#8217;s <a href="https://documentation.xojo.com/api/user_interface/web/webchart.html#webchart">WebChart</a>, <a href="https://documentation.xojo.com/api/user_interface/desktop/desktopchart.html">DesktopChart</a> and <a href="https://documentation.xojo.com/api/user_interface/mobile/mobilechart.html">MobileChart</a> offer the following chart types: Line, Bar, Pie, Doughnut, PolarArea, Scatter, Bubble and Radar. You can access these charts using the Inspector Panel or via code through the Mode property. You can even change the Mode (or chart type) at runtime on Desktop and Mobile.</p>


<div class="wp-block-image size-large wp-image-11322 is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="641" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeDesktop-1024x641.png" alt="" class="wp-image-11322" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeDesktop-1024x641.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeDesktop-300x188.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeDesktop-768x481.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeDesktop-1536x962.png 1536w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeDesktop.png 2032w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">DesktopChart Mode set in the Inspector Panel.</figcaption></figure>
</div>

<div class="wp-block-image size-large wp-image-11323 is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="832" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeiOS-1024x832.png" alt="" class="wp-image-11323" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeiOS-1024x832.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeiOS-300x244.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeiOS-768x624.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeiOS-1536x1248.png 1536w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartModeiOS.png 1758w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">MobileChart Mode set in the Inspector Panel.</figcaption></figure>
</div>


<p>Line and Bar types are similar; both display sets of data that have been set to Line or Bar Modes. Display as many data sets as you need in your DesktopChart or MobileChart by setting the Mode to Line or Bar.&nbsp;The only difference is how these will be displayed along the x axis.</p>



<p>To better understand, let&#8217;s write some code in the Opening Event Handler of a DesktopChart control added to a Desktop project:</p>



<pre class="wp-block-code xojo"><code><code>Var labels() As String
Var firstSetOfData() As Double
Var secondSetOfData() As Double
Var thirdSetOfData() As Double

For n As Integer = 0 To 9
  labels.add "LB"+n.ToString
  firstSetOfData.add System.Random.InRange(0,1000)
  secondSetOfData.add System.Random.InRange(0,1000)
  thirdSetOfData.add System.Random.InRange(0,1000)
Next

Me.mode = DesktopChart.Modes.Line
Me.Title = "Chart Example With Two Datasets"
Me.AddLabels labels

Var DS1 As New ChartLinearDataset("DS1", Color.Blue, True, firstSetOfData)
DS1.ChartType = ChartLinearDataset.ChartTypes.Line

Var DS2 As New ChartLinearDataset("DS2", Color.Red, True, secondSetOfData)
DS2.ChartType = ChartLinearDataset.ChartTypes.Bar

Var DS3 As New ChartLinearDataset("DS2", Color.Green, True, thirdSetOfData)
DS3.ChartType = ChartLinearDataset.ChartTypes.Bar

Me.AddDatasets DS1, DS2, DS3</code></code></pre>



<p>This is how it will look when the Chart has been set to Line, displaying three data sets: one of which has been set to Line and the other two to Bar:</p>


<div class="wp-block-image size-large wp-image-11321 is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="777" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToLine-1024x777.png" alt="" class="wp-image-11321" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToLine-1024x777.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToLine-300x228.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToLine-768x582.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToLine.png 1424w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Chart instance set to Line Mode.</figcaption></figure>
</div>


<p>As you can see, when the ChartType is Line, the data sets that are set to Line start drawing at the origin of the X axis, where the labels are also drawn for each column of data. This also happens when drawing data sets whose ChartType is set to Bar; thus, the first bar on the first column has half the width.</p>



<p>Below shows the same data sets but the ChartType is Bar. Now the Bar data sets overlay the position for the labels drawn along the X axis (they are centered on each column), and also have full the width. However, because of that, the data set whose ChartType is set to Line starts drawing at half the width of the column.</p>


<div class="wp-block-image wp-image-11324 size-large is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="777" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToBar-1024x777.png" alt="" class="wp-image-11324" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToBar-1024x777.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToBar-300x228.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToBar-768x582.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartSetToBar.png 1424w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Chart instance set to Bar Mode.</figcaption></figure>
</div>


<p>You can add as many new data sets as you want, remove them, update their values, color, etc. The chart will update to reflect the changes, including the minimum and maximum values displayed in the Y axis. Keep in mind, if a chart&#8217;s Mode has been set to Line or Bar it will only display data sets created as ChartLinearDataset (by default they are set to the Line ChartType). WebChart will behave the same, starting with Xojo 2023r1.</p>



<p>One difference to note when using ChartLinearDataset is that you can set the Transparency property ranging from 0 (fully opaque) to 100 (fully transparent), as well as the CornerSize when the ChartLinearDataset ChartType is set to Bar.</p>



<h3 class="wp-block-heading">ChartDataset and Chart Modes</h3>



<p>I already showed you that a chart set to Line or Bar only displays data sets created from the ChartLinearDataset class. A similar rule applies when the chart is set to other Modes. For example, Pie, Doughnut, PolarArea or Radar charts only display data sets created from the ChartCircularDataset class.</p>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="1024" height="232" src="https://blog.xojo.com/wp-content/uploads/2023/02/CircularDataset-1024x232.png" alt="" class="wp-image-11325" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/CircularDataset-1024x232.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/CircularDataset-300x68.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/CircularDataset-768x174.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/CircularDataset-1536x349.png 1536w, https://blog.xojo.com/wp-content/uploads/2023/02/CircularDataset-2048x465.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>In the same vein, Scatter and Bubble charts only display data sets created from the ChartScatteredDataset class.</li>
</ul>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="300" height="277" src="https://blog.xojo.com/wp-content/uploads/2023/02/Scatter-Bubble-300x277.png" alt="" class="wp-image-11326" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/Scatter-Bubble-300x277.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/Scatter-Bubble-1024x945.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/Scatter-Bubble-768x709.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/Scatter-Bubble.png 1330w" sizes="auto, (max-width: 300px) 100vw, 300px" /></figure>
</div>


<h3 class="wp-block-heading">DesktopChart and MobileChart, additional Properties</h3>



<p>DesktopChart and MobileChart offer additional customisation properties. For example, you can set the font, font size and font color for the title, legend, labels and values on the Y axis.</p>



<p>When setting the maximum font size used for the drawing of the Y axis values based on the chart control instance size (height), if the height of the control is not enough to display the values at the specified font size, it automatically adjusts.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="484" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartFontSize-1024x484.png" alt="" class="wp-image-11327" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartFontSize-1024x484.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartFontSize-300x142.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartFontSize-768x363.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartFontSize-1536x727.png 1536w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartFontSize-2048x969.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>Other properties to further customize the look of the chart include BackgroundColor and GridColor.</p>



<p><em>Tip: You may want to set the color to Color.Clear if you want your charts to be drawn or &#8220;visible&#8221;, like if you plan to layout the chart control on top of other UI controls.</em></p>



<p>In addition, you can control whether or not to display that popup that shows the underlying value as the user moves the cursor (or while dragging their finger, on iOS). This is done through the AllowPopover property (True, by default).</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="840" height="392" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartPopoverCustom.png" alt="" class="wp-image-11328" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartPopoverCustom.png 840w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartPopoverCustom-300x140.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartPopoverCustom-768x358.png 768w" sizes="auto, (max-width: 840px) 100vw, 840px" /></figure>
</div>


<p>Set the background and text color in the Popover using the PopoverBackgroundColor and PopoverTextColor properties. These expect a ColorGroup instance, so they can adjust when the control is in Light or Dark Mode.</p>



<h3 class="wp-block-heading">Getting informed</h3>



<p>As you may expect from their WebChart counterpart, DesktopChart and MobileChart offer a Pressed event handler to receive the index and dataset instance over which the user clicked/tapped.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="206" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartPressed-1024x206.png" alt="" class="wp-image-11329" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartPressed-1024x206.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartPressed-300x60.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartPressed-768x154.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartPressed.png 1194w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>Because the chart control can handle different ChartDatasets you&#8217;ll probably want to cast the received ChartDataset instance received in the Pressed handler to the right one before proceeding with any actions that are not common to all kind of ChartDatasets.</p>



<h3 class="wp-block-heading">Draw it!</h3>



<p>You can call the DrawInto method on a chart, provide the expected graphic context and also the left and top values to proceed with the drawing. For example, this is what a DesktopChart looks like when drawn on a Picture of the same size:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="560" height="322" src="https://blog.xojo.com/wp-content/uploads/2023/02/ToPicture.png" alt="" class="wp-image-11330" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ToPicture.png 560w, https://blog.xojo.com/wp-content/uploads/2023/02/ToPicture-300x173.png 300w" sizes="auto, (max-width: 560px) 100vw, 560px" /></figure>
</div>


<p>It is also possible to render the chart to PDF in a vectorial way using the AddChart method on a PDFDocument instance. The parameters this method expects are the chart instance itself, the X and Y values (where it will be placed in the current PDF page), and the width and height values for the rendering.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="573" src="https://blog.xojo.com/wp-content/uploads/2023/02/ChartToPDF-1024x573.png" alt="" class="wp-image-11331" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/ChartToPDF-1024x573.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartToPDF-300x168.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartToPDF-768x429.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/ChartToPDF.png 1452w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>For example, this code will create a PDF file in the Desktop with the same height value of the chart instance, with the width set to the width of the page, minus the margins:</p>



<pre class="wp-block-code xojo"><code><code>Var d As New PDFDocument</code>
<code><span style="font-family: inherit; font-size: inherit;">Var g As Graphics = d.Graphics</span>
d.AddChart(chart1, 20, 20, g.Width-40, chart1.Height )
d.Save(SpecialFolder.Desktop.Child("ChartPDF.PDF"))</code></code></pre>



<h3 class="wp-block-heading">In summary</h3>



<p>As you can see, now it is possible to add Graphical Charts to your Desktop and iOS projects with the same ease and in the same way it is already possible on web projects, plus some more customization options. Watch a <a href="https://youtu.be/zO_UNDlERwE">quick video</a> on this topic. No license is required to learn, develop and test your projects in the latest Xojo release! <a href="https://xojo.com/download/">Download Xojo</a> 2023r1 today!</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Algorithm: Equally Spaced Ranges</title>
		<link>https://blog.xojo.com/2023/02/13/algorithm-equally-spaced-ranges/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Mon, 13 Feb 2023 20:52:06 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=11217</guid>

					<description><![CDATA[There are some situations where you may need a range of numbers that is equally spaced. For example, when given a minimum and a maximum&#8230;]]></description>
										<content:encoded><![CDATA[
<p>There are some situations where you may need a range of numbers that is equally spaced. For example, when given a minimum and a maximum value within which a set of numbers that need to be equally spaced. This is useful if you are designing an UI control displaying &#8220;ticks&#8221; or for the axis of a graphic chart displaying cartesian values. Continue reading to see a technique to do just this.</p>



<span id="more-11217"></span>



<p>This technique is strongly based in the NiceLabels algorithm that can be found in <a href="https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks">StackOverflow</a> ported to several programming languages, so…&nbsp;why not to Xojo too? In fact, it was a piece of cake porting it from JavaScript to Xojo due to the similarities of both languages,</p>



<p>Of course, if you just want to jump into the class itself, you can <a href="https://drive.google.com/file/d/1RVyXLoe_R0IA01HKAla9Vc-dknYn2Oql/view?usp=share_link">download the project from this link</a>.</p>



<p>Let&#8217;s start creating the class itself. I&#8217;ve named it NiceScale with all the required properties. To begin, add a new Class to a Xojo project. Then, with the class selected in the Navigator, add the following properties:</p>



<ul class="wp-block-list">
<li>MaxPoint As Double, setting the Scope to Protected</li>



<li>MinPoint As Double, setting the Scope to Protected</li>



<li>Spacing As Double, setting the Scope to Public</li>



<li>NiceMaximum As Double, setting the Scope to Public</li>



<li>NiceMinimum As Double</li>
</ul>



<p>Next, add the methods starting with the Constructor.</p>



<ul class="wp-block-list">
<li><strong>Method Name:</strong> Constructor</li>



<li><strong>Parameters:</strong> min As Double, max As Double</li>



<li><strong>Scope:</strong> Public</li>
</ul>



<p>And type the following snippet of code in the associated Code Editor:</p>



<pre class="wp-block-code"><code><code>If Min = Max Then Max = Min + 1

Self.MinPoint = Min(Min, Max)
Self.MaxPoint = Max(Max, Min)
Self.Calculate</code></code></pre>



<p>As you can see here, the Constructor calls the Calculate method; so let&#8217;s add that one:</p>



<ul class="wp-block-list">
<li><strong>Method Name:</strong> Calculate</li>



<li><strong>Scope:</strong> Protected</li>
</ul>



<p>And type this code in the associated Code Editor:</p>



<pre class="wp-block-code xojo"><code><code>Var range As Double

range = niceNum(MaxPoint - MinPoint, False)

Spacing = niceNum(range / 9, True)

NiceMinimum = Floor(MinPoint / Spacing) * Spacing
NiceMaximum = Ceiling(MaxPoint / Spacing) * Spacing</code></code></pre>



<p>Once again, this method is calling the NiceNum method,&nbsp;so let&#8217;s add that one to the<br>NiceScale class:</p>



<ul class="wp-block-list">
<li><strong>Method Name:</strong> NiceNum</li>



<li><strong>Parameters:</strong> range As Double, round As Boolean</li>



<li><strong>Return Type:</strong> Double</li>



<li><strong>Scope:</strong> Protected</li>
</ul>



<p>And type the following snippet in the associated Code Editor:</p>



<pre class="wp-block-code"><code><code>Var exponent As Double
Var fraction As Double
Var niceFraction As Double

exponent = Floor(Log(range) / Log(10))
fraction = range / Pow(10, exponent)

If (round) Then
  If (fraction &lt; 1.5) Then
    niceFraction = 1
  ElseIf (fraction &lt; 3) Then
    niceFraction = 2
  ElseIf (fraction &lt; 7) Then
    niceFraction = 5
  Else
    niceFraction = 10
  End If
Else
  If (fraction &lt;= 1) Then
    niceFraction = 1
  ElseIf (fraction &lt;= 2) Then
    niceFraction = 2
  ElseIf (fraction &lt;= 5) Then
    niceFraction = 5
  Else
    niceFraction = 10
  End If
End If

Return niceFraction * Pow(10, exponent)</code></code></pre>



<p>Lastly, let&#8217;s add a couple of useful methods for the class: GetValues and SetMinMaxPoints. The first one will let us get the Array of the already equally spaced Double values, while the second one will let us set a new pair of minimum and maximum values without needing to create a new instance of the class:</p>



<ul class="wp-block-list">
<li><strong>Method Name:</strong> GetValues</li>



<li><strong>Return Type:</strong> Double()</li>



<li><strong>Scope:</strong> Public</li>
</ul>



<p>Typing the following snippet in the associated Code Editor:</p>



<pre class="wp-block-code"><code><code>Var values() As Double

For n As Double = Self._NiceMinimum To Self._NiceMaximum Step Self._mSpacing
  values.add n
Next

If values(0) &gt; 0 Then
  While values(0) &lt;&gt; 0
    values.AddAt(0, values(0) - Self._mSpacing)
  Wend
End If

Return values</code></code></pre>



<ul class="wp-block-list">
<li><strong>Method Name:</strong> SetMinMaxPoints</li>



<li><strong>Parameters:</strong> min As Double, max As Double</li>



<li><strong>Scope:</strong> Public</li>
</ul>



<p>And type these lines of code in the associated Code Editor:</p>



<pre class="wp-block-code xojo"><code><code>If Min = Max Then Max = Min + 1

MinPoint = Min(Min, Max)
MaxPoint = Max(Max, Min)
Calculate</code></code></pre>



<h3 class="wp-block-heading">&nbsp;</h3>



<h3 class="wp-block-heading">NiceScale in Practice</h3>



<p>Let&#8217;s create the UI for a Desktop project so we can put our NiceScale class in practice; and because we are going to need some custom drawing, drag a Canvas from the Library to the project Navigator. That will create a canvas subclass. With the just added Canvas subclass selected in the Navigator use the associated Inspector Panel to set the following values:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> ScaleDrawing</li>
</ul>



<p>Then, add the following property to it:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> range</li>



<li><strong>Type:</strong> NiceScale</li>



<li><strong>Scope:</strong> Private</li>
</ul>



<p>Add a new Method to our ScaleDrawing subclass using the following values:</p>



<ul class="wp-block-list">
<li><strong>Method Name:</strong> Constructor</li>
</ul>



<p>And typing these lines of code in the associated Code Editor:</p>



<pre class="wp-block-code"><code><code>Super.Constructor
Self.range = New NiceScale(0, 1)</code></code></pre>



<p>Then, add a second method using the following values:</p>



<ul class="wp-block-list">
<li><strong>Method Name:</strong> Redraw</li>



<li><strong>Parameters:</strong> minValue As Double, maxValue As Double</li>
</ul>



<p>And typing these two lines of code in the associated Code Editor:</p>



<pre class="wp-block-code xojo"><code><code>range.SetMinMaxPoints(minValue, maxValue)

Self.Refresh</code></code></pre>



<p>After that, let&#8217;s do the drawing itself! So we need to add the Paint event to our ScaleDrawing class. When done, add the following lines of code to the associated Code Editor:</p>



<pre class="wp-block-code"><code><code>g.DrawRectangle(0, 0, g.Width, g.Height)

Var values() As Double = range.GetValues
Var offset As Double = (( g.Width ) / values.LastIndex) - 10
Var x As Double = 20
Var y As Double = g.Height / 2 - 5
Var tx As Double
Var Val As Double

For n As Integer = 0 To values.LastIndex
  Val = values(n)

  g.DrawLine( x, y, x, y + 10 )

  tx = x - g.TextWidth(values(n).ToString) / 2

  g.DrawText(values(n).ToString, tx, y + 10 + g.FontAscent)

  x = x + offset
Next

g.DrawLine(20, y, x - offset, y)</code></code></pre>



<h3 class="wp-block-heading">&nbsp;</h3>



<h3 class="wp-block-heading">Creating the NiceScale Example UI</h3>



<p>Select the Window1 window in the Navigator so it is displayed in the Layout Editor. Next drag the ScaleDrawing class from the Navigator and drop it into the Layout Editor so it looks like like this (locking the four locks for ScaleDrawing1 in the Inspector Panel):</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="743" src="https://blog.xojo.com/wp-content/uploads/2023/02/CanvasSubclassUI-1024x743.png" alt="" class="wp-image-11222" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/CanvasSubclassUI-1024x743.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/CanvasSubclassUI-300x218.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/CanvasSubclassUI-768x557.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/CanvasSubclassUI.png 1364w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>Next, add a couple of Labels, two TextFields and a Button below ScaleDrawing1 in the Layout Editor so our final UI looks like this:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="736" src="https://blog.xojo.com/wp-content/uploads/2023/02/CompletedUI-1024x736.png" alt="" class="wp-image-11223" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/CompletedUI-1024x736.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/CompletedUI-300x215.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/CompletedUI-768x552.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/CompletedUI.png 1356w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>Rename Label1 as MinimumLabel, Label2 as MaximumLabel, TextField1 as MinimumTF, TextField2 as MaximumTF, and Button1 as DrawBT. Then, add the Pressed event to DrawBT and type the following code in the associated Code Editor:</p>



<pre class="wp-block-code xojo"><code><code>ScaleDrawing1.Redraw(MinimumTF.Text.ToDouble, maximumtf.Text.ToDouble)</code></code></pre>



<p>That&#8217;s all!</p>



<h3 class="wp-block-heading">Running the app!</h3>



<p>Everything is in place, so run the app and put some values in the Minimum and Maximum textfields, clicking the button to refresh the drawing. These are some examples you will get for the following range values:</p>



<ul class="wp-block-list">
<li>By Default:</li>
</ul>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="974" height="142" src="https://blog.xojo.com/wp-content/uploads/2023/02/DefaultRange.png" alt="" class="wp-image-11218" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/DefaultRange.png 974w, https://blog.xojo.com/wp-content/uploads/2023/02/DefaultRange-300x44.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/DefaultRange-768x112.png 768w" sizes="auto, (max-width: 974px) 100vw, 974px" /></figure>



<ul class="wp-block-list">
<li>Minimum: 12, Maximum: 112</li>
</ul>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="950" height="116" src="https://blog.xojo.com/wp-content/uploads/2023/02/12-112Range.png" alt="" class="wp-image-11221" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/12-112Range.png 950w, https://blog.xojo.com/wp-content/uploads/2023/02/12-112Range-300x37.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/12-112Range-768x94.png 768w" sizes="auto, (max-width: 950px) 100vw, 950px" /></figure>



<ul class="wp-block-list">
<li>Minimum: -345, Maximum: 835</li>
</ul>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="1024" height="129" src="https://blog.xojo.com/wp-content/uploads/2023/02/345-835Range-1024x129.png" alt="" class="wp-image-11219" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/345-835Range-1024x129.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/02/345-835Range-300x38.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/345-835Range-768x97.png 768w, https://blog.xojo.com/wp-content/uploads/2023/02/345-835Range.png 1050w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Minimum: -816, Maximum: 25</li>
</ul>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="1000" height="154" src="https://blog.xojo.com/wp-content/uploads/2023/02/816-25Range.png" alt="" class="wp-image-11220" srcset="https://blog.xojo.com/wp-content/uploads/2023/02/816-25Range.png 1000w, https://blog.xojo.com/wp-content/uploads/2023/02/816-25Range-300x46.png 300w, https://blog.xojo.com/wp-content/uploads/2023/02/816-25Range-768x118.png 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></figure>



<p>As you can see, having these classes in your developer bag is a good thing if you need to draw this kind of scale! Use the ScaleDrawing class as a start point for your own needs.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Quick Tip: Opening, Shown, Closing and Closed</title>
		<link>https://blog.xojo.com/2023/02/09/quick-tip-opening-shown-closing-and-closed/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Thu, 09 Feb 2023 17:38:29 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Beginner Tips]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=11215</guid>

					<description><![CDATA[Knowing the firing order of the Opening event in Xojo apps, and the Shown event for Xojo Web apps, is important to using these correctly for the control's initialization. The same can be said for Closing and Closed events. Read to for a breakdown of this order for Desktop, Web and Mobile apps developed in Xojo. ]]></description>
										<content:encoded><![CDATA[
<p>Knowing the firing order of the Opening event in Xojo Desktop and Mobile apps, and the Shown event for Xojo Web apps, is important to using these correctly for a control&#8217;s initialization. The same can be said for Closing and Closed events. Read on for a breakdown of this order for Desktop, Web and Mobile apps developed in Xojo. </p>



<span id="more-11215"></span>



<h3 class="wp-block-heading">Opening in Desktop and Mobile Apps</h3>



<ul class="wp-block-list">
<li>First, the App Opening event will be fired first. </li>



<li>Next, the Opening event on every control of the default Window assigned to the App project. In this case, it will follow the same order in which the controls were added to the Window in the Layout Editor. It is possible to change that order using the Order buttons in the <a href="https://documentation.xojo.com/getting_started/using_the_ide/layout_editor.html#command-bar">Command Bar</a> in the Layout Editor. </li>



<li>Last, the Opening event will be fired on the default Window containing the controls.</li>
</ul>



<h3 class="wp-block-heading">Closing in Desktop Apps</h3>



<p>When a Window is closed in a Desktop app, the Closing event will be fired following this order:</p>



<ul class="wp-block-list">
<li>First, the Closing event will be raised on every control added to the Window, following the same order used when adding the controls to the Window in the Layout Editor. It is possible to change that order using the Order buttons in the Layout Editor toolbar.</li>



<li>Next, the Closing event will be raised in the Window itself.</li>



<li>Last, the Closing event will be raised in the App object of the Desktop app when the user quits the application.</li>
</ul>



<h3 class="wp-block-heading">Opening in Web Apps</h3>



<p>This is the event firing order for Xojo Web apps:</p>



<ul class="wp-block-list">
<li>First, the Opening event will be raised in the WebApp object.</li>



<li>Next, the Opening event in the WebSession object.</li>



<li>Next, the Opening event on every control added to the by default WebPage set in the WebApp object. In this case, it will follow the same order used when adding the controls to the web page in the Layout Editor. (It is possible to change that order using the Order buttons in the Layout Editor toolbar.)</li>



<li>Next, the Opening event will be raised on the default web page itself.</li>



<li>Next, the app will start to raise the Shown event on every one of the webpage controls following the same order used when they have been added to the page. (It is possible to change that order using the Order buttons in the Layout Editor toolbar.)</li>



<li>Last, the Shown event will be raised in the default webpage.</li>
</ul>



<h3 class="wp-block-heading">Closing in Web Apps</h3>



<p>Regarding the Closing and Closed events, the Web App will follow this order:</p>



<ul class="wp-block-list">
<li>First, the Closing event will be raised in the WebSession object.</li>



<li>Next, the Closed event will be raised on every one of the default webpages (or the pages shown), following the same order used when adding the controls to the page in the Layout Editor. (It is possible to change that order using the Order buttons in the Layout Editor toolbar.)</li>



<li>Last, the Closed event will be fired in the webpage itself.</li>
</ul>



<h3 class="wp-block-heading">Summary</h3>



<p>As you can see, Xojo follows the firing same regardless of the target you choose for your project &#8211; Desktop, Web or Mobile apps. Obviously, Web apps do change a bit, because they deal with the WebSession object and also with the Shown event in addition to the Opening event. In Web projects, the Opening event happens on the server side, while the Shown event happens in the client (browser) side.</p>



<p>See related <a href="https://youtu.be/E3iFTJt2V1Y">video</a> at the Xojo Programming Language YouTube Channel.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PDFTable from a RowSet</title>
		<link>https://blog.xojo.com/2023/01/17/pdftable-from-a-rowset/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Tue, 17 Jan 2023 16:00:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Eddie&#039;s Electronics]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Tables]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=11146</guid>

					<description><![CDATA[Xojo recently introduced the ability to add (aka draw) tables in PDFDocument using the PDFTable class and the PDFTableDataSource class interface together. Perhaps you already know how to use it, but what if you want to add a table using data from a RowSet? Continue reading and I will show you a technique that will have you doing just that!]]></description>
										<content:encoded><![CDATA[
<p>Xojo recently introduced the ability to add (aka draw) tables in <a href="https://documentation.xojo.com/api/pdf/pdfdocument.html#pdfdocument">PDFDocument</a> using the <a href="https://documentation.xojo.com/api/pdf/pdftable.html#pdftable">PDFTable</a> class and the <a href="https://documentation.xojo.com/api/pdf/pdftabledatasource.html#pdftabledatasource">PDFTableDataSource</a> class interface together. Perhaps you already know how to use it, but what if you want to add a table using data from a <a href="https://documentation.xojo.com/api/databases/rowset.html#rowset">RowSet</a>? Continue reading and I will show you a technique that will have you doing just that!</p>



<span id="more-11146"></span>



<p>But before we dive into the code,&nbsp;this example project requires the &#8220;EddiesElectronics.sqlite&#8221; SQLite database file. You can find the Eddie&#8217;s Electronics example, along with the other example projects provided with Xojo, in the Examples section of the Project Chooser. Because I am such a kind person, you can also <a href="https://drive.google.com/file/d/1zts_ZL2ZQujW7fWD1m2qr_XpQGq3dbHD/view?usp=share_link">download it directly from this link</a>.</p>



<p>And, if you want, it is also possible to download the resulting <a href="https://drive.google.com/file/d/1Z5Eurwxm7UXlCLU_aj270SNKnlDmpT7Z/view?usp=share_link">Xojo Project file (for Desktop) from this link</a>.</p>



<p>I&#8217;m pretty sure you will be able to adapt the example project so it works for you with other databases too!</p>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="809" height="1024" src="https://blog.xojo.com/wp-content/uploads/2023/01/GeneratedPDF-809x1024.png" alt="" class="wp-image-11149" srcset="https://blog.xojo.com/wp-content/uploads/2023/01/GeneratedPDF-809x1024.png 809w, https://blog.xojo.com/wp-content/uploads/2023/01/GeneratedPDF-237x300.png 237w, https://blog.xojo.com/wp-content/uploads/2023/01/GeneratedPDF-768x972.png 768w, https://blog.xojo.com/wp-content/uploads/2023/01/GeneratedPDF-1214x1536.png 1214w, https://blog.xojo.com/wp-content/uploads/2023/01/GeneratedPDF.png 1342w" sizes="auto, (max-width: 809px) 100vw, 809px" /></figure>



<h3 class="wp-block-heading">Helper class to the rescue!</h3>



<p>All the work of drawing the table in the PDF document will be done by a helper class. That is, a class that we are going to use to &#8220;delegate&#8221; the work. With Xojo open and a new Desktop project created, the first thing we are going to do is add a new Class to the project (Insert &gt; Class).</p>



<p>With the added Class selected in the Navigator, go to the associated Inspector Panel and use the following values:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> PDFRenderer</li>



<li><strong>Interfaces:</strong> Click on the &#8220;Choose&#8221; button and select the PDFTableDataSource class interface</li>
</ul>



<p>The just added Class will be populated with all the methods defined by the PDFTableDataSource class interface. Do not worry about them now.</p>



<p>Next, let&#8217;s add a few properties to our PDFRenderer class:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> data</li>



<li><strong>Type:</strong> RowSet</li>



<li><strong>Scope:</strong> Protected</li>
</ul>



<ul class="wp-block-list">
<li><strong>Name:</strong> document</li>



<li><strong>Type:</strong> PDFDocument</li>



<li><strong>Scope:</strong> Protected</li>
</ul>



<ul class="wp-block-list">
<li><strong>Name:</strong> DrawingFromRowSet</li>



<li><strong>Type:</strong> Boolean</li>



<li><strong>Scope:</strong> Protected</li>
</ul>



<ul class="wp-block-list">
<li><strong>Name:</strong> Headers()</li>



<li><span style="font-size: revert;"><strong>Type:</strong> String</span></li>



<li><span style="font-size: revert;"><strong>Scope:</strong> Protected</span></li>
</ul>



<p>We are going to use the DrawingFromRowSet as a flag, so the PDFRenderer knows when the table is going to be created from a RowSet. You could use any other data source, you are limited to RowSet. That&#8217;s just something you will need to implement yourself! Take it as an exercise.</p>



<p>Next, add a new Method to the PDFRenderer class (while still selected in the Navigator) using the following values in the associated Inspector Panel:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> Constructor</li>



<li><strong>Parameters:</strong> document As PDFDocument</li>



<li><strong>Scope:</strong> Public</li>
</ul>



<p>And type the following line of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted xojo"><code>Self.Document = document</code></pre>



<p>Yep, it is truly that simple. Assign the received PDFDocument object to the &#8220;Document&#8221; property of the object created from the PDFRenderer class.</p>



<p>Add a second method using the following values:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> DrawTable</li>



<li><strong>Parameters:</strong> rs As RowSet, headers() As String</li>



<li><strong>Scope:</strong> Public</li>
</ul>



<p>And type the following code in the associated Code Editor:</p>



<pre class="wp-block-preformatted"><code>// This is the method we call on the Class to draw
// a table on a PDFDocument based on a RowSet

If document &lt;&gt; Nil And rs &lt;&gt; Nil Then

  // Assign the received RowSet to the "Data" property
  Self.data = rs

  // Assign the received headers to the "Headers" property
  Self.Headers = Headers

  // Hey! We are going to create a table from a RowSet
  // so we use a Boolean property as a flag for that
  // (Yeah, we can do it using other techniques, but this
  // is easy enough for this purpose…&nbsp;while leave this
  // helper class "open" enough for drawing tables based on other
  // data sources).
  Self.DrawingFromRowSet = True

  // How many columns are we going to draw?
  // Well… as many as columns in the RowSet.
  Var totalColumns As Integer = rs.ColumnCount

  // This is going to be the "drawable area" on the page
  // for the table = total page width less the left and right margins
  Var totalWidth As Double = document.Graphics.Width - 40 // 40 = 20 points left/right margins

  // Creating the PDFTable object here!
  Var table As New PDFTable

  // We want to repeat the headers on every page
  table.HasRepeatingHeader = True

  // Setting the column count for the table
  table.ColumnCount = totalColumns

  // …and the width for every column.
  table.ColumnWidths = CalculateColumnWidths(TotalWidth, totalColumns)

  // The object from this class will be the responsible
  // for handling all the methods associated with the
  // PDFTableDataSouce Class Interface
  table.DataSource = Self

  // Setting the Top and Bottom margins for the drawing
  // of the table on every PDF page
  table.TopMargin = 20
  table.BottomMargin = 20

  // …and finally we instruct the PDFDocument object
  // to draw the table!
  document.AddTable(table, 20, 0)

  // Lastly, clearing the flag
  Self.DrawingFromRowSet = False

End If</code></pre>



<p>As you can see here, we are calling the &#8220;CalculateColumnWidths&#8221; method in order to get the expected string for the column widths for the PDFTable.ColumnWidths property; so add a new method to the PDFRenderer class using the following values:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> CalculateColumnWidths</li>



<li><strong>Parameters:</strong> TotalWidth As Double, NumberOfColumns As Double</li>



<li><strong>Return Type:</strong> String</li>



<li><strong>Scope:</strong> Protected</li>
</ul>



<p>And type the following snippet of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted"><code>Var ColumnWidth As Integer = TotalWidth / NumberOfColumns
Var s() As String

For n As Integer = 0 To NumberOfColumns-1
  s.Add(Str(ColumnWidth))
Next

Return String.FromArray(s, ",")</code></pre>



<h3 class="wp-block-heading">PDFTableDataSource Methods with RowSet data!</h3>



<p>Let&#8217;s fill the required methods from the PDFTableDataSource class interface so they work in combination with the received RowSet.</p>



<h4 class="wp-block-heading">AddNewRow Method</h4>



<p>Add this snippet of code for the &#8220;AddNewRow&#8221; method:</p>



<pre class="wp-block-preformatted"><code>If DrawingFromRowSet And data &lt;&gt; Nil Then

  // We are going to draw as many rows as rows are in the
  // "data" rowset
  Return rowCount &lt;&gt; data.RowCount

End If</code></pre>



<p>As you can see, that fragment of code will only run when both conditions are meet. That is, when the &#8220;DrawingFromRowSet&#8221; is set to True and the &#8220;data&#8221; property is set to a not niled RowSet. If this is the case, we then instruct our PDFTable to set the number of rows to be drawn to the same number of rows in the RowSet.</p>



<h4 class="wp-block-heading">Completed Method</h4>



<p>This is the method that will be called once the drawing of the PDFTable has been completed. We will use it to draw the page numbers at the bottom of every page in the PDFDocument. As you probably already know, it is really easy to change the active graphic context to a given page of the PDF document using the CurrentPage property, so we will use that to iterate every one of our PDF document pages, adding the page number to it as a footer.</p>



<p>This is the snippet of code responsible for doing just that:</p>



<pre class="wp-block-preformatted"><code>// This method is called once the table has been drawn
// so let's "print" the page number on every page
// of the PDF Document

Static pageNumber As String = "Page: "

If document &lt;&gt; Nil Then
  Var g As Graphics = document.Graphics

  For n As Integer = 1 To document.PageCount
    document.CurrentPage = n
    g.DrawText(pageNumber + Str(n), g.Width - g.TextWidth(pageNumber+Str(n))-20, g.Height-g.FontAscent)
  Next

End If</code></pre>



<h4 class="wp-block-heading">HeaderHeight Method</h4>



<p>This is the method called so we can set the PDFTable the desired height for the drawing of the Header. In this case we are going to use a fixed value: 20 points. The code for that method is:</p>



<pre class="wp-block-preformatted"><code>// Fixed header height
Return 20</code></pre>



<h4 class="wp-block-heading">PaintCell Method</h4>



<p>This is the method responsible for drawing every cell of the PDFTable. It receives as parameters the Graphic context for a particular cell (so its origin is at 0,0) and the row and column values corresponding for such cell.</p>



<p>In this case, draw the outline of a rectangle for the cell plus the text itself retrieved from the RowSet based on the received Column value. The text will be draw vertically centered in the cell:</p>



<pre class="wp-block-preformatted"><code>If DrawingFromRowSet And data &lt;&gt; Nil Then
  // Drawing the outer rectangle for the cell
  g.DrawRectangle(0, 0, g.Width, g.Height)

  // retrieving the text to be drawn from the RowSet,
  // using the column parameter for that.
  Var s As String = data.ColumnAt(column)

  // Centering vertically the text on the table cell
  // while the X offset is fixed at 5 points.
  g.DrawText(s, 5, g.Height / 2 + g.FontAscent / 2)

  // Have we drawn all the columns for this row?
  If column = data.ColumnCount - 1 Then

    // if that is the case, then move to the next row
    // in the RowSet!
    data.MoveToNextRow
  End If

End If</code></pre>



<h4 class="wp-block-heading">PaintHeaderContent Method</h4>



<p>This is the method called in order to draw the Header of the table itself. It is a more simple version of the previous method. In this case, the text will be centered both vertically and horizontally on every header cell:</p>



<pre class="wp-block-preformatted"><code>If column &lt;= Self.Headers.LastIndex Then

  Var s As String = headers(column)
  g.DrawingColor = Color.Black
  g.FillRectangle(0, 0, g.Width, g.Height)
  g.DrawingColor = Color.White
  g.DrawText(s, g.Width / 2 - g.TextWidth(headers(column)) / 2, g.Height / 2 + g.FontAscent / 2)

End If</code></pre>



<h4 class="wp-block-heading">RowHeight Method</h4>



<p>As it happen with the HeaderHeight method, this is the method called to set the height for a given row. As we did for the HeaderHeight method we are going to use a fixed value of 20 points, so type the following line of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted"><code>Return 20</code></pre>



<h3 class="wp-block-heading">Testing The PDFRenderer class</h3>



<p>In order to test our PDFRenderer class in combination with a RowSet object, we need to do some additional work. First, select the Window1 window in the project Navigator and add a property using the following values:</p>



<ul class="wp-block-list">
<li><strong>Name:</strong> db</li>



<li><strong style="font-size: revert;">Type:</strong><span style="font-size: revert;"> SQLiteDatabase</span></li>



<li><strong style="font-size: revert;">Scope:</strong><span style="font-size: revert;"> Private</span></li>
</ul>



<p>Next, click the Window1 window in the Navigator so it is displayed in the Layout Editor. Then, drag a DesktopLabel from the Library and drop it on the left/top edge of the layout observing the aligning guides so it leaves the expected margin over the left and top edges of the Window. Use the dragging handlers of the DesktopLabel on the layout to resize it to the maximum width (once again, less the expected margins). Use the following values in the associated Inspector Panel:</p>



<ul class="wp-block-list">
<li><strong>Locking:</strong> Left, Top and Right locked. Bottom, unlocked.</li>



<li><strong>Multiline:</strong> on.</li>



<li><strong>Text:</strong> This example project needs to use the EddiesElectronics.sqlite database file. Click on the button in order to select it!</li>
</ul>



<p>The layout at this point should look like this:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="331" src="https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.17.36-1024x331.png" alt="" class="wp-image-11147" srcset="https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.17.36-1024x331.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.17.36-300x97.png 300w, https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.17.36-768x248.png 768w, https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.17.36.png 1274w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>Now, drag a DesktopButton from the Library and drop it below the previous Label so it is horizontally centered, using the following values in the associated Inspector Panel:</p>



<ul class="wp-block-list">
<li><strong>Caption:</strong> Select EddiesElectronics database file</li>
</ul>



<p>The finished layout should look like this:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="747" src="https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.19.13-1024x747.png" alt="" class="wp-image-11148" srcset="https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.19.13-1024x747.png 1024w, https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.19.13-300x219.png 300w, https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.19.13-768x561.png 768w, https://blog.xojo.com/wp-content/uploads/2023/01/Screenshot-2023-01-10-at-15.19.13.png 1422w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<p>Double click the button to add the &#8220;Pressed&#8221; Event Handler. Then, add the following code in the associated Code Editor:</p>



<pre class="wp-block-preformatted"><code>Try

  // Assigning SQLite object to property
  db = New SQLiteDatabase

  // Setting the database file to the SQLite object
  db.DatabaseFile = FolderItem.ShowOpenFileDialog(".sqlite")

  // Just a simple check to see if this is the expected
  // file based on the name = "EddiesElectronics.sqlite"

  If db.DatabaseFile = Nil Or db.DatabaseFile.Name &lt;&gt; "EddiesElectronics.sqlite" Then Return

  //…and "connecting" to it!
  db.Connect

  // Let's select all the rows from the Customers table
  Var rs As RowSet = db.SelectSQL("SELECT FirstName,LastName,Address FROM Customers")

  // Creating a new PDFDocument Instance
  Var d As New PDFDocument

  // Creating the PDFTable renderer helper object (we pass the PDFDocument object to it in the Constructor)
  Var PDFRender As New PDFRenderer(d)

  // These will be the headers drawn in the Table
  Var headers() As String = Array("Name", "Surname", "Address")

  // And let's instruct the PDFTable renderer helper to draw the table
  // based in the SQLite database rowset we got in the previous step
  PDFRender.DrawTable(rs, headers)

  // …and save the resulting PDF File to the Desktop
  Var f As FolderItem = SpecialFolder.Desktop.Child("TableFromRowSet.pdf")

  If f &lt;&gt; Nil Then
    d.Save(f)
    f.Open
  End If

Catch e As DatabaseException
  System.DebugLog(e.Message)
Catch e As NilObjectException
  System.DebugLog(e.Message)
End Try</code></pre>



<p>As you can see, all this does is create a new SQLiteDatabase object and assign it to the &#8220;db&#8221; property. Then, we set the &#8220;DataBaseFile&#8221; property of that object to the file selected by the user. The code will do a simple check to see if the selected file is the expected &#8220;EddiesElectronics&#8221; database file. If that is the case, then it will connect to the database, getting a RowSet from a simple query that retrieves the &#8220;name&#8221;, &#8220;surname&#8221; and &#8220;address&#8221; columns from the &#8220;Customers&#8221; database table.</p>



<p>After creating the PDFDocument instance, the code creates a new instance from our PDFRenderer class and calls the &#8220;DrawTable&#8221; method on that object passing along both the RowSet and the Headers we want to use for drawing the table.</p>



<p>Lastly, the PDFDocument will be saved to disk and opened in the by default PDF viewer app.</p>



<p>Run the app, select the &#8220;EddiesElectronics&#8221; SQLite database file&nbsp;and the resulting PDF file will be opened after a few seconds. It will display the table generated from a RowSet data source!</p>



<h3 class="wp-block-heading">In Summary</h3>



<p>It is quite possible to draw tables in a PDFDocument when the data to render comes from a RowSet. Nearly everything was done through the use of a helper class. As always, this can be improved in several ways! For example, we are using a fixed height for every row of the table, but in a more realistic scenario may be using variable heights for the table rows based in the height / amount of data to be drawn. That is something that is doable too!</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Running Xojo Web projects on Raspberry Pi 64-bit ARM boards</title>
		<link>https://blog.xojo.com/2022/12/13/running-xojo-web-projects-on-raspberry-pi-64-bit-arm-boards/</link>
		
		<dc:creator><![CDATA[Ricardo Cruz]]></dc:creator>
		<pubDate>Tue, 13 Dec 2022 15:22:45 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[64-bit]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[Console]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[Raspbian]]></category>
		<category><![CDATA[Remote Debugging]]></category>
		<category><![CDATA[Single Board Computer]]></category>
		<category><![CDATA[Web 2.0]]></category>
		<category><![CDATA[Xojo API 2.0]]></category>
		<category><![CDATA[Xojo Forum]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=11065</guid>

					<description><![CDATA[Xojo 2022r4 opens the door for running projects on 64-bit Raspberry Pi ARM boards. Console, Desktop and Web are supported.]]></description>
										<content:encoded><![CDATA[
<p>Xojo 2022r4 opens the door for running projects on 64-bit Raspberry Pi ARM boards. Console, Desktop and Web are supported.</p>



<p>Xojo Web is an excellent way to expose the features of your electronics projects without having to attach any screen to them. It&#8217;s never been easier to build a Web GUI for your smart clocks, drones, POS, domotics, robots or just some automation services. Thinking about building a solar powered server to automate your tweets? Why not!</p>



<p>If you prefer, you can just expose a Web API instead that can be remotely consumed from another device.</p>



<h2 class="wp-block-heading">What Raspberry Pi boards support ARM 64-bit Linux OS?</h2>



<ul class="wp-block-list">
<li>Raspberry Pi Zero 2</li>



<li>Raspberry Pi 3</li>



<li>Raspberry Pi 4</li>



<li>Raspberry Pi 400</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="763" src="https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-zero-2-1024x763.png" alt="" class="wp-image-11066" srcset="https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-zero-2-1024x763.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-zero-2-300x224.png 300w, https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-zero-2-768x573.png 768w, https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-zero-2.png 1336w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">One of my Raspberry Pi Zero 2, running a 64-bit Xojo Web application.</figcaption></figure>



<h2 class="wp-block-heading">Preparing your Raspberry Pi</h2>



<p>The easiest way to get your operating system ready is by using the <a href="https://www.raspberrypi.com/software/" data-type="URL" data-id="https://www.raspberrypi.com/software/" target="_blank" rel="noreferrer noopener">Raspberry Pi Imager</a>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="690" src="https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-imager-1024x690.png" alt="" class="wp-image-11067" srcset="https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-imager-1024x690.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-imager-300x202.png 300w, https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-imager-768x518.png 768w, https://blog.xojo.com/wp-content/uploads/2022/12/raspberry-pi-imager.png 1510w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Raspberry Pi Imager v1.7.3.</figcaption></figure>



<p>At the moment of writing this article, it selects the 32-bit flavor by default. If you have one of the supported boards, go ahead and try Raspberry Pi OS (64-bit) or Raspberry Pi OS Lite (64-bit). The later won&#8217;t include any desktop environment, you&#8217;ll have to access by SSH.</p>



<p>Select your SD Card storage and press on Write. Optionally, if you want to set a hostname for your board, enable SSH, or even configure your Wifi, press on the gears icon.</p>



<p>In just a few minutes you will have a ready to boot SD card for your Raspberry Pi.</p>



<h2 class="wp-block-heading">Remotely debugging your projects</h2>



<p>This is one of my favorite features of Xojo.</p>



<p>In your Xojo installation Extras folder, you will find a &#8220;Remote Debugger Console&#8221; and &#8220;Remote Debugger Desktop&#8221;, that also works for your Linux 64-bit OS.</p>



<p>Depending on which flavor of Linux you&#8217;ve selected in the previous step, send the Console or Desktop ZIP to your board, run it and configure the settings.</p>



<p>Inside Xojo, go to Preferences and press on the Debugging tab.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="599" src="https://blog.xojo.com/wp-content/uploads/2022/12/xojo-preferences-debugging-1024x599.png" alt="" class="wp-image-11068" srcset="https://blog.xojo.com/wp-content/uploads/2022/12/xojo-preferences-debugging-1024x599.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/12/xojo-preferences-debugging-300x176.png 300w, https://blog.xojo.com/wp-content/uploads/2022/12/xojo-preferences-debugging-768x449.png 768w, https://blog.xojo.com/wp-content/uploads/2022/12/xojo-preferences-debugging.png 1480w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Xojo Debugging Preferences panel.</figcaption></figure>



<p>That&#8217;s it. Now you will be able to remotely run and debug your applications. Add a few breakpoints and the execution will stop as soon as it reaches it, allowing you to explore the current state.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="667" src="https://blog.xojo.com/wp-content/uploads/2022/12/xojo-remote-debug-1024x667.png" alt="" class="wp-image-11070" srcset="https://blog.xojo.com/wp-content/uploads/2022/12/xojo-remote-debug-1024x667.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/12/xojo-remote-debug-300x195.png 300w, https://blog.xojo.com/wp-content/uploads/2022/12/xojo-remote-debug-768x500.png 768w, https://blog.xojo.com/wp-content/uploads/2022/12/xojo-remote-debug-1536x1000.png 1536w, https://blog.xojo.com/wp-content/uploads/2022/12/xojo-remote-debug.png 1910w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Running your project remotely, to debug.</figcaption></figure>



<h2 class="wp-block-heading">Deploying the final version</h2>



<p>Once you&#8217;re ready to build the final version of your application, all the wires attached and components soldered, the last step is to &#8220;Deploy&#8221; your application.</p>



<p>There is a tutorial covering this step in detail: <a href="https://blog.xojo.com/2021/05/28/tutorial-deploying-web-apps-on-linux/" data-type="post" data-id="8552">Deploying Web Apps on Linux</a>.</p>



<p>If you create a Raspberry Pi project, please make sure you open a <a href="https://forum.xojo.com" data-type="URL" data-id="https://forum.xojo.com" target="_blank" rel="noreferrer noopener">Forum thread</a>. The Xojo community loves seeing this kind of projects!</p>



<p>What will you build?</p>



<p><em>Ricardo has always been curious about how things work. Growing up surrounded by computers</em> he became interested in <em>web technologies in the dial-up connections era. Xojo has been his secret weapon and language of preference since 2018. When he’s not online, chances are he will be scuba diving … or crocheting amigurumis. Find Ricardo on Twitter <a href="https://web.archive.org/web/20220805000833/https://www.twitter.com/piradoiv" target="_blank" rel="noreferrer noopener">@piradoiv</a>.</em></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Linux Joins the Dark Side</title>
		<link>https://blog.xojo.com/2022/12/13/linux-joins-the-dark-side/</link>
		
		<dc:creator><![CDATA[William Yu]]></dc:creator>
		<pubDate>Tue, 13 Dec 2022 14:33:00 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[DarkMode]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Xojo API 2.0]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=11050</guid>

					<description><![CDATA[While we've been hard at working getting Linux ARM 64 support ready for Xojo 2022r4, released earlier today, we also updated Xojo's Linux support for Dark Mode. Color.IsDarkMode now functions properly and our Linux IDE has now officially joined the dark side.]]></description>
										<content:encoded><![CDATA[
<p>While we&#8217;ve been hard at working getting Linux ARM 64 support ready for Xojo 2022r4, released earlier today, we also updated Xojo&#8217;s Linux support for Dark Mode. Color.IsDarkMode now functions properly and our <a href="https://documentation.xojo.com/topics/user_interface/desktop/supporting_dark_mode.html#supporting-dark-mode">Linux IDE</a> has now officially joined the dark side.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="623" src="https://blog.xojo.com/wp-content/uploads/2022/12/LinuxIDEInDarkMode-1024x623.png" alt="" class="wp-image-11051" srcset="https://blog.xojo.com/wp-content/uploads/2022/12/LinuxIDEInDarkMode-1024x623.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/12/LinuxIDEInDarkMode-300x182.png 300w, https://blog.xojo.com/wp-content/uploads/2022/12/LinuxIDEInDarkMode-768x467.png 768w, https://blog.xojo.com/wp-content/uploads/2022/12/LinuxIDEInDarkMode.png 1217w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Depending on your flavor of Linux (in this example we&#8217;re using Gnome on Ubuntu 20.04), you can change themes from your Settings/Appearance sidebar:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="625" src="https://blog.xojo.com/wp-content/uploads/2022/12/Settings-1024x625.png" alt="" class="wp-image-11055" srcset="https://blog.xojo.com/wp-content/uploads/2022/12/Settings-1024x625.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/12/Settings-300x183.png 300w, https://blog.xojo.com/wp-content/uploads/2022/12/Settings-768x469.png 768w, https://blog.xojo.com/wp-content/uploads/2022/12/Settings.png 1216w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>And for those interested in other themes, you can also change themes at the command line:</p>



<pre class="wp-block-code"><code>gsettings set org.gnome.desktop.interface gtk-theme 'Yaru-dark'</code></pre>



<p>Xojo will be able to determine which themes are dark mode savvy.  Now you are ready to enjoy the dark side of Linux along with Xojo!</p>



<p><em><em><em>William Yu grew up in Canada learning to program BASIC on a Vic-20. He is Xojo’s resident Windows and Linux engineer, among his many other skills. Some may say he has joined the dark side here in the USA, but he will always be a Canadian at heart.</em></em></em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>2022: Welcome Back!?</title>
		<link>https://blog.xojo.com/2022/12/13/2022-welcome-back/</link>
		
		<dc:creator><![CDATA[Alyssa Foley]]></dc:creator>
		<pubDate>Tue, 13 Dec 2022 11:00:00 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Community]]></category>
		<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[XDC]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[Code Assistants]]></category>
		<category><![CDATA[Code Editor]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[webdev]]></category>
		<category><![CDATA[Xojo API 2.0]]></category>
		<category><![CDATA[Xojo Framework]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10967</guid>

					<description><![CDATA[Does anyone else feel like 2022 is the first year in many years where we have gotten back together, in-person, for big events, small get-togethers, travel and even the daily office grind? Whether that is the right thing to have done or not, it seems that is what many of us did in 2022. Though we all tried new things to stay connected over the past few years (Zoom-fatigue anyone?), there's nothing like seeing an old friend after years apart.]]></description>
										<content:encoded><![CDATA[
<p>Does anyone else feel like 2022 is the first year in many years where we have gotten back together, in-person, for big events, small get-togethers, travel and even the daily office grind? Whether that is the right thing to have done or not, it seems that is what many of us did in 2022. Though we all tried new things to stay connected over the past few years (Zoom-fatigue anyone?), there&#8217;s nothing like seeing an old friend after years apart.</p>



<p>Xojo&#8217;s team was all vaxxed-up and so happy to get together in Nashville for #XDC2022. Tennessee&#8217;s Music City was a delightful backdrop for the first in-person Xojo event since 2019. Check out the short highlights <a href="https://youtu.be/t8xpWVL5pEM">video</a> from Nashville and then join us for #XDC2023 in London, UK. You can <a href="http://xojo.com/xdc/london/index.html">register</a> here.</p>



<p>This year the Xojo team and community welcomed a new Xojo engineer, Ricardo Cruz. Ricardo has applied his expertise to advance and improve Xojo Web. To quote one of Xojo&#8217;s engineers, &#8220;Ricardo coming in has been awesome,&#8221; a sentiment echoed on the Forum and the community in Nashville. </p>



<p>Xojo Web has come so far this year. Control reliability has improved, IDE previews have improved and reconnections have really improved. The web framework can now more often reconnect back to the same web session if it still exists. This is one of the things Ricardo is really happy about, &#8220;You can switch to your email app in your mobile, copy something to the clipboard, come back to the web app and it is still there, ready to continue.&#8221; Under the hood, some dependencies received updates, like jQuery. As a user, you don&#8217;t have to do anything, just use the new Xojo version! Try to tell that to another web developer, that&#8217;s sci-fi outside of Xojo. Also, thanks to the community, the memory and CPU usage continues to be improved release after release. &#8220;Honestly from my perspective it is just fun to play with little projects in it because it has really matured,&#8221; says Xojo&#8217;s Director of Engineering, Travis Hill. </p>



<p>Speaking of technical achievements, this year we added two new targets to Xojo: Windows on ARM 64 and Linux on ARM 64. On Windows, you can natively deploy to ARM-based tablets and laptops that run Windows. On Linux, you can deploy to 64-bit Raspberry Pi which can address significantly more RAM than 32-bit versions. </p>



<p>We built the <a href="https://www.xojo.com/cloud/">Xojo Cloud</a> Control Panel with an updated version of Xojo, and thanks to some Web framework improvements the controls now stay more responsive under load. Plus we&#8217;ve added Xojo Cloud servers in Sydney, Australia for web developers in Australia and New Zealand. We also continued improving and testing our upcoming support for Android, which is now feature complete and awaiting some remaining bug fixes before we make it available to everyone.</p>



<p>In addition, we want to brag about a few technical improvements, including the numerous <a href="https://blog.xojo.com/tag/pdf/">PDF updates</a> implemented by Xojo Engineer and now PDF expert, Javier Menéndez. Also there is the option to generate&nbsp;Program Database <a href="https://blog.xojo.com/2022/07/25/what-the-pdb-is-this/">(PDB)</a>&nbsp;files on Windows and improved <a href="https://blog.xojo.com/2022/10/12/improving-multi-monitor-support-on-windows/">multi-monitor support</a> shepherded by Xojo Engineer William Yu. The list goes on: <a href="https://blog.xojo.com/2022/04/05/using-the-new-user-code-assistants-feature/">Code Assistants</a>, <a href="https://blog.xojo.com/2022/04/05/on-device-ios-debugging-in-xojo/">on-device debugging in iOS</a>, new <a href="https://documentation.xojo.com/">Xojo Documentation</a> site, adding <a href="https://blog.xojo.com/2022/01/07/desktop-adding-controls-at-runtime/">Controls at runtime</a>, loading speed faster for big projects, and the time team-Lefebvre built a <a href="https://blog.xojo.com/2022/03/03/a-web-app-to-calculate-combat-rolls-in-twilight-imperium/">web app</a> to Calculate Combat Rolls in Twilight Imperium&#8230;</p>



<p>Inside Xojo we improved a lot too. Besides welcoming Ricardo and his beard to the team, we migrated Xojo&#8217;s bug and feature reporting platform from the internally managed Feedback tool to the much loved <a href="https://tracker.xojo.com">Issues</a>. This has increased productivity, streamlined work flow, made it easier to search and find, comment, edit. The team couldn&#8217;t be happier!</p>



<p>This year also included Xojo&#8217;s first Bug Bash in a long time! During the Bug Bash, Xojo&#8217;s <a href="https://blog.xojo.com/2022/10/12/bountiful-bug-bashing/">Code Editor</a> got some much needed improvements resulting in noticeably faster updates. Plus updates to the Syntax Help Area thanks to Xojo Engineer and retro-computing expert, Paul Lefebvre. Besides the very real smashing of 100 bugs, the bash had the unintended consequence of giving the engineers an opportunity to work on things they don&#8217;t normally work on, giving engineers opportunities to collaborate and better solve things.</p>



<p>All in all, 2022 was a year of forward movement for Xojo. Which has us leaning into 2023 with a lot of excitement and expectation. Our team is committed to continuing to make Xojo the best cross-platform development tool and we thank you for being part of Xojo&#8217;s continued success!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Adding Tables to PDFDocument</title>
		<link>https://blog.xojo.com/2022/10/12/adding-tables-to-pdfdocument/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Wed, 12 Oct 2022 13:45:45 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10756</guid>

					<description><![CDATA[Starting with Xojo 2022r3 it is possible to create and add tables to PDF documents using Xojo&#8217;s PDFTable class and the PDFTableDataSource class interface. Among&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Starting with Xojo 2022r3 it is possible to create and add tables to PDF documents using Xojo&#8217;s PDFTable class and the PDFTableDataSource class interface. Among other things, your code will not need to be in charge of calculating rows sizes in order to decide if it needed to add a new page or not, or even complex calculations in order to draw the content on every cell of every row/column pair in the table.</p>



<p>You can download the example project <a href="https://www.dropbox.com/s/bkdpoem0vn3vqcx/PDFTable-Example.zip?dl=1">from this link</a>.</p>



<h3 class="wp-block-heading">Features of Tables in PDFDocument</h3>



<p>The main features available with tables in PDFDocument are:</p>



<ul class="wp-block-list"><li>PDFTable / PDFTableDataSource is compatible with Desktop, Web, Console and RaspberryPi projects</li><li>Variable height rows</li><li>Automatically add new pages to the PDF</li><li>The ability to repeat (or not) the table header at the start of every new page added</li></ul>



<p>In order to achieve this, the PDFTable class works side by side with the PDFTableDataSource class interface. This class interface declares the methods that will be called by a given PDFTable instance in order to generate the table on the fly.</p>



<ul class="wp-block-list"><li>PDFTableDataSource</li></ul>



<p>Because it is a class interface, you will be able to add it to any window/web page or any class/subclass you  create, so it can work as the provider of the data in the drawing of a given PDF table.</p>



<p>The PDFTableDataSource has these five methods:</p>



<ul class="wp-block-list"><li><strong>AddNewRow(rowCount As Integer) As Boolean</strong>&#8211; The rowCount parameter contains the value of the rows already added to the table. Returning True from this method will add a new row to the table; otherwise it will end the creation of the associated table.</li><li><strong>HeaderHeight As Double</strong>&#8211; In the implementation of this method you will set the height value used in order to draw the table header.</li><li><strong>PaintCell(g As Graphics, row As Integer, column As Integer)</strong>&#8211; This is the method called by the PDFTable instance in order to draw the cell for the row/column pair stored in the associated parameters. The method receives its own graphic context in the &#8216;g&#8217; variable, so you can use any of the supported methods and properties of the Graphic class to draw the contents in the table cell.</li><li><strong>PaintHeaderContent(g As Graphics, column As Integer)</strong>&#8211; This is the method called by the PDFTable instance every time it needs to draw the header of the table. As you can see, it receives as parameters the column number and its own graphic context.</li><li><strong>RowHeight As Double</strong>&#8211; This is the method called by the PDFTable instance so your code can set the height for the current row.</li></ul>



<h3 class="wp-block-heading">PDFTable</h3>



<p>This is a really simple class exposing a total of four properties:</p>



<ul class="wp-block-list"><li><strong>ColumnCount As Integer</strong>&#8211; This is the property that sets the number of columns for the table to be drawn.</li><li><strong>ColumnWidths As String</strong>&#8211; This is the property that sets as a string the width of every column in the table, separated by the comma character.</li><li><strong>DataSource As PDFTableDataSource</strong>&#8211; This is the property that sets the object acting as the data source object for the table. Remember that such an object needs to implement the methods declared in the PDFTableDataSource class interface.</li><li><strong>HasRepeatingHeader As Boolean</strong>&#8211; Use this property to instruct the PDFTable instance if it needs to repeat the header on every new page that is added to the document as the table is generated.</li></ul>



<p>For example, the following fragment of code will create a table with a total of three columns that have the same size. The table will be drawn horizontally centered in the page. The object acting as the data source for the table will be the same where this snippet of code has been added, that is: &#8216;Self&#8217;:</p>



<pre class="wp-block-preformatted"><code>Var d As New PDFDocument

Var tTable As New PDFTable
tTable.ColumnCount = 3
tTable.ColumnWidths = "150,150,150"
tTable.DataSource = Self
tTable.HasRepeatingHeader = True</code></pre>



<p>The way to add a PDFTable instance to the current page of the PDF document is through the AddTable(table As PDFTable, x As Double, y As Double) method; for example:</p>



<pre class="wp-block-preformatted"><code>Var x As Double = d.PageWidth/2 - 450/2

d.addTable(tTable,x,20)</code></pre>



<h3 class="wp-block-heading">&nbsp;</h3>



<h3 class="wp-block-heading">PDFTable in Practice</h3>



<p>In this example, you can see how to create a PDF document with a table added to it (horizontally centered on the pages). This table will have three columns equally sized and a total of 100 rows with a variable —random— height—, and giving the option to the user so they can choose to repeat the table header on every new page added to the document or not.</p>



<p>The cell contents of the table will be chosen randomly, varying between filling the cell with a random color, drawing text with the row and column numbers for the cell, rendering an image that has been added to the project, or keeping the cell empty.</p>



<p>Create a new Desktop project. With the Window1 selected in the Navigator, click on the &#8220;Choose…&#8221; button found in the associated Inspector Panel under the Interface section. This action will open a new window displaying all the available Class Interfaces. Select the PDFTableDataSource entry and confirm the selection click the Ok button.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="927" height="1024" src="https://blog.xojo.com/wp-content/uploads/2022/10/Selector-PDFDataTableSource-927x1024.png" alt="" class="wp-image-10757" srcset="https://blog.xojo.com/wp-content/uploads/2022/10/Selector-PDFDataTableSource-927x1024.png 927w, https://blog.xojo.com/wp-content/uploads/2022/10/Selector-PDFDataTableSource-272x300.png 272w, https://blog.xojo.com/wp-content/uploads/2022/10/Selector-PDFDataTableSource-768x848.png 768w, https://blog.xojo.com/wp-content/uploads/2022/10/Selector-PDFDataTableSource.png 1012w" sizes="auto, (max-width: 927px) 100vw, 927px" /></figure>
</div>


<p>As result, the methods declared by the PDFTableDataSource are now added to the Window1 item.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="505" height="270" src="https://blog.xojo.com/wp-content/uploads/2022/10/PDFTableDataSource-Methods.png" alt="" class="wp-image-10758" srcset="https://blog.xojo.com/wp-content/uploads/2022/10/PDFTableDataSource-Methods.png 505w, https://blog.xojo.com/wp-content/uploads/2022/10/PDFTableDataSource-Methods-300x160.png 300w" sizes="auto, (max-width: 505px) 100vw, 505px" /></figure>
</div>


<p>Grab a CheckBox control from the Library and drop it in the upper-left corner of the window in the Layout Editor. Use the associated Inspector Panel to change the following values:</p>



<ul class="wp-block-list"><li><strong>Name:</strong> RepeatHeaderCB</li><li><strong>Visual State:</strong> Checked</li></ul>



<p>In order to complete this basic user interface, drag a Button control from the Library and drop it in the upper-right corner of the window in the Layout Editor. With Button1 selected in the Navigator, choose the Add to &#8220;Button1&#8221; &gt; Event Handler…&nbsp;option from the contextual menu. Next, select the Pressed event from the resulting window and confirm the selection so it is added to the Window1 object.</p>



<p>With the Pressed event selected in the Navigator, add the following fragment of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted"><code>Var d As New PDFDocument

Var tTable As New PDFTable
tTable.ColumnCount = 3
tTable.ColumnWidths = "150,150,150"
tTable.DataSource = Self
tTable.HasRepeatingHeader = RepeatHeaderCB.Value

Var x As Double = d.PageWidth/2 - 450/2

d.addTable(tTable,x,20)

d.Save(SpecialFolder.Desktop.Child("Table.pdf"))</code></pre>



<p>Drop any image you want to use from your computer drive into the Xojo&#8217;s IDE Navigator. Our example uses an image named &#8220;XojoLogo100&#8221;. When adding your own image, you can opt to rename it to the name name used in the code of this example, or changing all the references where &#8220;XojoLogo100&#8221; is referenced to the name of your image.</p>



<p>Now it is time to implement the code in every one of the five methods required by the PDFTableDataSource class interface.</p>



<p>In the AddNewRow method:</p>



<pre class="wp-block-preformatted"><code>Return rowCount &lt; 100 // Our example table has a total of 100 rows.</code></pre>



<p>In the HeaderHeight method:</p>



<pre class="wp-block-preformatted"><code>Return 30 // This is the height for the cells of the header in the table.</code></pre>



<p>In the PaintCell method:</p>



<pre class="wp-block-preformatted"><code>Var rd As New Random

Var option As Integer = rd.InRange(1,4)

Select Case option

Case 1

  Var s As String = "row: " + row.ToString + " column: " + Column.ToString
  Var h As Double = g.Height/2 + g.TextHeight/2
  g.DrawText s,5,h

Case 2

  g.DrawPicture(XojoLogo100,0,0)

Case 3

  g.DrawingColor = Color.RGB(rd.InRange(0,255), rd.InRange(0,255), rd.InRange(0,55))
  g.FillRectangle(2,2,g.Width-4,g.Height-4)

Case 4

 // Empty cell
End Select

g.DrawingColor = Color.Black
g.DrawRectangle 0,0, g.Width,g.Height</code></pre>



<p>And this is the code we will use in order to draw the content of the table Header cells in the PaintHeaderContent:</p>



<pre class="wp-block-preformatted"><code>Var tRandom As New Random

Var c As Color

Select Case Column
Case 0
  c = color.Red
Case 1
  c = Color.Green
Case 2
  c = Color.Blue
End Select

g.DrawingColor = c
g.FillRectangle 0,0,g.Width,g.Height

Var s As String = "Column: " + Column.ToString

Var x, y As Double
x = (g.Width/2) - (g.TextWidth(s)/2)
y = (g.Height/2) + (g.TextHeight/2)

g.DrawingColor = color.Black
g.DrawText s,x, y</code></pre>



<p>Now add the code responsible for setting the height of every new row added to the table in the RowHeight method. As you can see, this value is a random value between 25 and 100 points:</p>



<pre class="wp-block-preformatted"><code>Var rd As New Random

return rd.InRange(25,100)</code></pre>



<h3 class="wp-block-heading">Running the App</h3>



<p>Run the app, click on the button and you will see how a new PDF document named &#8220;Table.pdf&#8221; is created in your computer desktop. The number of pages of the PDF document may vary because the height of the table rows vary on every iteration.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1018" height="1024" src="https://blog.xojo.com/wp-content/uploads/2022/10/Table-PDF-1018x1024.png" alt="" class="wp-image-10759" srcset="https://blog.xojo.com/wp-content/uploads/2022/10/Table-PDF-1018x1024.png 1018w, https://blog.xojo.com/wp-content/uploads/2022/10/Table-PDF-298x300.png 298w, https://blog.xojo.com/wp-content/uploads/2022/10/Table-PDF-150x150.png 150w, https://blog.xojo.com/wp-content/uploads/2022/10/Table-PDF-768x772.png 768w, https://blog.xojo.com/wp-content/uploads/2022/10/Table-PDF-1527x1536.png 1527w, https://blog.xojo.com/wp-content/uploads/2022/10/Table-PDF-2036x2048.png 2036w" sizes="auto, (max-width: 1018px) 100vw, 1018px" /></figure>
</div>


<p>This time, unselect the checkbox and click the button to create the PDF document again. Now, the table header is only rendered in the first page of the document.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PDFDocument Improvements</title>
		<link>https://blog.xojo.com/2022/10/12/pdfdocument-improvements/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Wed, 12 Oct 2022 13:45:29 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10810</guid>

					<description><![CDATA[While PDFTable and PDFDataTableSource ease the way to render tables in your PDF documents on Desktop, Web, Raspberry Pi and Console projects (iOS to come!) are the big new features for PDFDocument in Xojo 2022r3, that's not all that improved. There are additional features added to PDFDocument you might find helpful such as more control while using PDFForm and improvements to dealing with TOC indexes. Learn more about how to get advantage of these additions!]]></description>
										<content:encoded><![CDATA[
<p>While <a href="https://blog.xojo.com/2022/10/12/adding-tables-to-pdfdocument/">PDFTable</a> and PDFDataTableSource ease the way to render tables in your PDF documents on Desktop, Web, Raspberry Pi and Console projects (iOS to come!) and are the big new features for PDFDocument in Xojo 2022r3, that&#8217;s not all that improved. There are additional features added to PDFDocument you might find helpful such as more control while using PDFForm and improvements to dealing with TOC indexes. Learn more about how to get advantage of these additions!</p>



<span id="more-10810"></span>



<h3 class="wp-block-heading">More Control with PDFForm Controls</h3>



<p>It is possible to add forms to your PDF documents using controls as text fields, text areas, list boxes, radio buttons, check boxes, popup menus, combo boxes, etc. Starting with Xojo 2022r3 you&#8217;ll be able to set if the items of PDFComboBox, PDFListBox and PDFPopupMenu are displayed sorted or as they were added using the Boolean IsSorted property.</p>



<p>As for the PDFTextArea and PDFTextField controls, there is a new property AllowSpellChecking so you can set if these will enable spell check or not. And in the case of the PDFTextField, it is also possible to set the HasPassword property so the text typed by the user is substituted in the PDF document with the usual replacement character, hiding what the user is typing from prying eyes!</p>



<h3 class="wp-block-heading">Dealing with TOC Indexes</h3>



<p>Lastly, there are also improvements to dealing with TOC creation in your PDF documents. For example, you can get now the Parent instance from a PDFToc entry through the Parent property; and there are new methods to get all the child items hanging from a PDFTOCEntry —Entries() As PDFTOCEntry()— and to remove existing entries from a PDFTOCEntry instance: PDFTOCEntry.RemoveEntry(ParamArray entries As PDFTOCEntry) and PDFTOCEntry.RemoveEntry(entries() as PDFTOCEntry).</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using Emojis in Xojo</title>
		<link>https://blog.xojo.com/2022/09/15/using-emojis-in-xojo/</link>
		
		<dc:creator><![CDATA[Geoff Perlman]]></dc:creator>
		<pubDate>Thu, 15 Sep 2022 19:00:18 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Fun]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Beginner Tips]]></category>
		<category><![CDATA[Emoji]]></category>
		<category><![CDATA[Multi-Platform Development]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[webdev]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10674</guid>

					<description><![CDATA[Xojo has very good graphics support. You can drop images into your project and use them with several controls. You can use the various Paint events to draw your own graphics. And there's another source of graphics you may not have considered: emojis. Emojis can be used anywhere that text can be used because they are simply Unicode characters. That means they can be used in textfields, buttons, labels, popup menus, listboxes and more.]]></description>
										<content:encoded><![CDATA[
<p>Xojo has very good graphics support. You can drop images into a Xojo project and use them with several controls. You can use the various Paint events to draw your own graphics. And there&#8217;s another source of graphics you may not have considered: emojis. Emojis can be used anywhere that text can be used because they are Unicode characters. That means they can be used in textfields, buttons, labels, popup menus, listboxes and more.</p>



<p>Emojis are available on every operating system and platform that Xojo supports from desktop to mobile to web. Because they are built-in to the OS as characters, they are client-side which means that web applications don&#8217;t have to load them from the server as they are already available to the browser from the user&#8217;s OS. As characters, they are scalable to any font size. There are also a lot of them. According to <a href="https://emojipedia.org/stats/">Emojipedia</a> (yes, that&#8217;s a thing), there are over 3,600 emojis as of September 2021. </p>



<p>Emojis can be especially useful for listboxes in web applications because unlike the <a href="https://documentation.xojo.com/api/user_interface/desktop/desktoplistbox.html#desktoplistbox">DesktopListBox</a>, the <a href="https://documentation.xojo.com/api/user_interface/web/weblistbox.html#weblistbox">WebListBox</a> does not have a PaintCellBackground event that can be used to draw graphics into a cell. Instead, if you can find an emoji that represents what you&#8217;d otherwise draw into a cell, you can simply assign it without having to deal with graphics at all. For example, there are a series of emojis that are just boxes in different colors that could be used to indicate the state of something: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e9.png" alt="🟩" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e5.png" alt="🟥" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e6.png" alt="🟦" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e7.png" alt="🟧" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e8.png" alt="🟨" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7eb.png" alt="🟫" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b1b.png" alt="⬛" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b1c.png" alt="⬜" class="wp-smiley" style="height: 1em; max-height: 1em;" />. The famous web game <a href="https://www.nytimes.com/games/wordle/index.html">Wordle</a> uses these when you share your results. With over 3600 available, you can likely find an emoji to represent nearly anything you might need from the common smiling face <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> to the less common umbrella on the ground<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26f1.png" alt="⛱" class="wp-smiley" style="height: 1em; max-height: 1em;" /> , banjo <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1fa95.png" alt="🪕" class="wp-smiley" style="height: 1em; max-height: 1em;" /> or moai <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5ff.png" alt="🗿" class="wp-smiley" style="height: 1em; max-height: 1em;" />from Easter Island.</p>



<p>Again, emojis can be used in any type of application, desktop, mobile or web. Here&#8217;s an example of them in a web popup menu:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="366" height="368" src="https://blog.xojo.com/wp-content/uploads/2022/09/drop.png" alt="" class="wp-image-10748" srcset="https://blog.xojo.com/wp-content/uploads/2022/09/drop.png 366w, https://blog.xojo.com/wp-content/uploads/2022/09/drop-298x300.png 298w, https://blog.xojo.com/wp-content/uploads/2022/09/drop-150x150.png 150w" sizes="auto, (max-width: 366px) 100vw, 366px" /></figure>



<p>And here they are in a web list box:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="390" height="378" src="https://blog.xojo.com/wp-content/uploads/2022/09/box.png" alt="" class="wp-image-10749" srcset="https://blog.xojo.com/wp-content/uploads/2022/09/box.png 390w, https://blog.xojo.com/wp-content/uploads/2022/09/box-300x291.png 300w" sizes="auto, (max-width: 390px) 100vw, 390px" /></figure>



<p>The key thing is to realize that they are individual characters which means they can appear alone or mixed in with other characters. If you need them to be larger, increase the font size. Here&#8217;s the lion for example at 60 point size:</p>



<p style="font-size:60px"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f981.png" alt="🦁" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Because they are characters, you can copy and paste emojis anywhere you&#8217;d use a character, including in your code:</p>



<pre class="wp-block-code"><code>ListBox1.AddRow("Fox &#x1f98a;")
ListBox1.AddRow("Lion &#x1f981;")
ListBox1.AddRow("Chipmunk &#x1f43f;")</code></pre>



<p>Just remember that emojis are characters, not graphics, so you should treat them as such. Notice in the code above, they are inside the quotes. You could even use them to name a variable:</p>



<pre class="wp-block-code"><code>Var &#x1f30b;&#x1f965;&#x1f4b0; As String = "Yes, you can do this!"</code></pre>



<p>I&#8217;m not sure that&#8217;s useful, but it clearly demonstrates how an emoji is a characters like any other.</p>



<p>On Emojipedia you can search for emojis by keyword and copy them to the clipboard. Again because they are simply characters, you can then paste them into a Xojo constant, make them the default value of a property or even paste them into the Code Editor to assign them in code.</p>



<p>Keep emojis in mind the next time you need an image in an application.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PDFDocument: How To Encrypt PDFs</title>
		<link>https://blog.xojo.com/2022/07/25/pdfdocument-how-to-encrypt-pdfs/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Mon, 25 Jul 2022 13:54:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[2022r2]]></category>
		<category><![CDATA[Encryption]]></category>
		<category><![CDATA[PDF]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10486</guid>

					<description><![CDATA[One of the PDFDocument features added in Xojo 2022r2 is the ability to encrypt PDF files created with Xojo. Continue reading and I will show&#8230;]]></description>
										<content:encoded><![CDATA[
<p>One of the PDFDocument features added in Xojo 2022r2 is the ability to encrypt PDF files created with Xojo. Continue reading and I will show you how.</p>



<span id="more-10486"></span>



<p>Encrypting PDF files with PDFDocument is based in the use of the PDFPermissions class. You&#8217;ll need to create a new instance of the class passing along the &#8220;Owner&#8221; and &#8220;User&#8221; passwords. For example:</p>



<pre class="wp-block-code"><code>Var d As New PDFDocument
Var g As Graphics = d.Graphics

Var p As New PDFPermissions("OwnerPassword","UserPassword")</code></pre>



<p>In addition, you can set other properties for the PDFPermissions instance; all of them are read/write and will be applied by the PDF viewer app for when the document is opened using the &#8220;user&#8221; password.</p>



<ul class="wp-block-list"><li><strong>AllowCopyingContents</strong> is set to <code>False</code> by default. When set to <code>True</code> it will allow copying contents from the PDF, as for example the selected text or image.</li><li><strong>AllowModifyingContents</strong> is set to <code>False</code> by default. When set to <code>True</code> it will all to modify the contents of the protected PDF document.</li><li><strong>AllowPrinting</strong> is se to <code>False</code> by default. When it is set to <code>True</code> it will be possible to print the PDF.</li></ul>



<p>Once the PDFPermissions instance has been created and the desired properties had been set, all you need to do is to assign such instance to the Permissions property for the PDFDocument instance you want to encrypt:</p>



<pre class="wp-block-code"><code>d.Permissions = p</code></pre>



<p>Then, when it&#8217;s saving the document to a file, PDFDocument will encrypt all the streams of data containing sensitive information, as it can be the text or Images rendered on every one of the PDF pages plus the metadata information itself. The used encryption algorithm is AES 128 bits.</p>



<p>That&#8217;s all! You can distinguish an encrypted PDF file from an unencrypted one because, usually, the first one will be displayed with the image of a Lock in the icon. When you open an encrypted PDF in the viewer app you&#8217;ll be asked to type a password. If you enter the passord set to the &#8220;Owner&#8221; user, you&#8217;ll be able to do all the kind of operations allowed by the viewer app, while if you enter the &#8220;User&#8221; password, then the kind of options available will be determined by those set using the PDFPermissions properties.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PDFDocument: New Annotations Types</title>
		<link>https://blog.xojo.com/2022/07/25/pdfdocument-new-annotations-types/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Mon, 25 Jul 2022 13:53:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[2022r2]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Rapid Application Development]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Xojo API 2.0]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10488</guid>

					<description><![CDATA[The annotations feature on PDFDocument has been significantly extended in the release of Xojo 2022r2. Continue reading to learn about the new annotation types. Currently,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>The annotations feature on PDFDocument has been significantly extended in the release of Xojo 2022r2. Continue reading to learn about the new annotation types.</p>



<p>Currently, Xojo&#8217;s PDFDocument supports the most common type of annotations for PDF &#8211; you know those little yellow boxes displayed like sticky notes on the pages of the PDF document that display their contents when clicked on.</p>



<p>Now with Xojo 2022r2 and later you have more options! And while most of these don&#8217;t look like &#8220;real&#8221; annotations or like something made using the Graphics methods, they will behave as the annotations you are familiar with using in PDF. What I mean by this is that they will display the associated text for the annotation while also allowing the user to Accept/Decline the annotation or interact with them by adding, for example, new replies.</p>



<p>Before reviewing the new options, let me just remind you: The PDF Standard dictates how some features must be implemented for a PDF. After that, it is up to the developers of the PDF viewer app to decide what features are implemented, keep in mind results can vary depending on the PDF viewer you or your user chose to use. As other many things related with PDF files, don&#8217;t expect every PDF viewer apps out there to support or adhere to the annotations outside of the most basic ones.</p>



<p>Adobe is the original creator of the PDF specification and its free PDF viewer, <a href="https://www.adobe.com/acrobat/pdf-reader.html">Acrobat Reader</a> app does support everything Xojo&#8217;s PDFDocument offers.</p>



<h3 class="wp-block-heading">Attaching Files to the PDF</h3>



<p>You&#8217;ll be able to embed any kind of file (except for those based in a Bundle structure) into the PDF document itself, for example an Excel file, a Hi-Res image, etc. Then the user viewing the PDF document will be able to open the attached file clicking in the annotation using whichever PDF Viewer app their computer/device has installed for the attached file format. Because this is an annotation, the user will be able to also edit the annotation or add new replies to it.</p>



<p>This is an example about how to create these kind of annotations:</p>



<pre class="wp-block-code"><code>Var d As New PDFDocument
Var g As Graphics = d.Graphics
Const kContentText As String = "Attached File Annotation: double click on me!"

g.FontSize = 20
g.DrawText(kContentText, 40, 60)

If exampleFile &lt;&gt; Nil Then
  
  // Creating a new EmbeddedSound.
  d.AddEmbeddedFile(exampleFile, 40, 60, g.TextWidth(kContentText), 20)
  
  // Saving the created PDF document to the selected path.
  Var f As FolderItem = FolderItem.ShowSaveFileDialog("","PDFAttachedFile.pdf")
  
  If f &lt;&gt; Nil Then
    
    d.Save(f)
    
  End If
End If
</code></pre>



<p>Here <code>exampleFile</code> is a FolderItem property that has been added to the project and that&#8217;s pointing to the file to embed.</p>



<p>And this is how it looks like in the resulting PDF file:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="439" src="https://blog.xojo.com/wp-content/uploads/2022/06/AttachedFilePDF-1024x439.png" alt="" class="wp-image-10490" srcset="https://blog.xojo.com/wp-content/uploads/2022/06/AttachedFilePDF-1024x439.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/06/AttachedFilePDF-300x129.png 300w, https://blog.xojo.com/wp-content/uploads/2022/06/AttachedFilePDF-768x329.png 768w, https://blog.xojo.com/wp-content/uploads/2022/06/AttachedFilePDF-1536x658.png 1536w, https://blog.xojo.com/wp-content/uploads/2022/06/AttachedFilePDF-2048x878.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<h3 class="wp-block-heading">Callout Annotations</h3>



<p>These kind of annotations are perfect for pointing our or calling attention to any other item in the PDF page. They are highly customizable- you can decide for example a solid or dashed line, the termination of the line, the color and, of course the text to be displayed in the annotation itself.</p>



<p>This is an example showing how to add a Callout annotation to a PDFDocument:</p>



<pre class="wp-block-code"><code>Var d As New PDFDocument
Var g As Graphics = d.Graphics
Const kContentText As String = "Callout Annotation in PDF"

g.FontSize = 40
g.Bold = True
g.DrawText(kContentText, 40, 60)

g.Bold = False
g.FontSize = 12
g.DrawText(kSampleText, 40, 100, 200)

Var originY As Double = (g.TextHeight(kSampleText, 200) / 2)

Var tCallout As New PDFCallout("Callout Example", New Point(250, originY), New Point(300, originY + 50), _
 New Point(350, originY + 50), 100, 50)

tCallout.BorderStyle = PDFAnnotation.BorderStyles.Dashed
tCallout.DashPattern = Array(2.0, 2.0)
tCallout.DrawingColor = Color.Orange
tCallout.LineEndingStyle = PDFAnnotation.LineEndingStyles.OpenArrow
tCallout.Text = "Some sample text in the annotation"
tCallout.FontSize = 9.5

d.AddCallout(tCallout)

// Saving the created PDF document to the selected path.
Var f As FolderItem = FolderItem.ShowSaveFileDialog("","PDFCalloutExample.pdf")

If f &lt;&gt; Nil Then
  
  d.Save(f)
  
End If
</code></pre>



<p>Where <code>kSampleText</code> is a Constant added to the project containing some sample text to be put in the page of the PDF.</p>



<p>And this is how the Callout will look like when opening the resulting PDF file in the viewer app:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="599" src="https://blog.xojo.com/wp-content/uploads/2022/06/Callout-1024x599.png" alt="" class="wp-image-10491" srcset="https://blog.xojo.com/wp-content/uploads/2022/06/Callout-1024x599.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/06/Callout-300x176.png 300w, https://blog.xojo.com/wp-content/uploads/2022/06/Callout-768x450.png 768w, https://blog.xojo.com/wp-content/uploads/2022/06/Callout-1536x899.png 1536w, https://blog.xojo.com/wp-content/uploads/2022/06/Callout-2048x1199.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<h3 class="wp-block-heading">Line Annotations</h3>



<p>A &#8220;Line&#8221; annotation displays a straight line on the page with a text caption inside the line or above it.</p>



<p>It is also possible to add leader lines to both sides of the main line (with possitive or negative values) plus set other properties as the type of the line (solid, dashed, etc), the kind of termination used on both ends of the main line (i.e: open/close arrow, butt, circle, diamond, square…), the drawing color, line width or font size.</p>



<p>This is how a Line annotation looks like when the resulting PDFDocument is viewed using Acrobat Reader (in this case, using Leader lines):</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="605" src="https://blog.xojo.com/wp-content/uploads/2022/06/Line-1024x605.png" alt="" class="wp-image-10493" srcset="https://blog.xojo.com/wp-content/uploads/2022/06/Line-1024x605.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/06/Line-300x177.png 300w, https://blog.xojo.com/wp-content/uploads/2022/06/Line-768x454.png 768w, https://blog.xojo.com/wp-content/uploads/2022/06/Line-1536x907.png 1536w, https://blog.xojo.com/wp-content/uploads/2022/06/Line-2048x1210.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<h3 class="wp-block-heading">Video Annotations</h3>



<p>Yes, you can embed .mp4 files in the PDF created with PDFDocument, and these will be played from the PDF itself. This will increase the size of the generated PDF file because it will add the size of the embedded mp4 file itself! So it is good practice to optimize the video files as much as possible and use the video resolution you want them to be played and not a higher one.</p>



<p>You can also add this kind of content as an Attached File annotation too. In both cases the data of the file will be embedded in the generated PDF file. The main difference in this case is that the mp4 file will be opened using an external app and that you&#8217;ll be able to include other video file format besides .mp4.</p>



<p>The way to add this kind of annotation to a PDFDocument instance is quite simple, for example:</p>



<pre class="wp-block-code"><code>Var d As New PDFDocument
Var g As Graphics = d.Graphics
Const kContentText As String = "Embedded Video Annotation"

g.FontSize = 30
g.DrawText(kContentText, 40, 60)

If exampleVideo &lt;&gt; Nil Then
  
  // Creating a new EmbeddedSound.
  d.AddEmbeddedMovie(exampleVideo, 40, 100, 478, 360)
  
  // Saving the created PDF document to the selected path.
  Var f As FolderItem = FolderItem.ShowSaveFileDialog("","PDFEmbeddedVideo.pdf")
  
  If f &lt;&gt; Nil Then
    
    d.Save(f)
    
  End If
End If
</code></pre>



<p>Where <code>exampleVideo</code> is a FolderItem Property that has been added to the project. This is how a video annotation will look like when opening the resulting PDF in Acrobat Reader:</p>



<figure class="wp-block-image is-style-default"><img loading="lazy" decoding="async" width="918" height="600" src="https://blog.xojo.com/wp-content/uploads/2022/06/VideoAnnotation.gif" alt="" class="wp-image-10494"/></figure>



<h3 class="wp-block-heading">Sound Annotations</h3>



<p>In addition to video annotations, it is also possible to add sound files that can be played from the PDF itself. In this case, the audio files need to be in AIFF format with 2 audio channels (stereo), 16 bits of resolution and a sampling frequency of 44,1 kHz.</p>



<p>As it is the case with video annotations, you can opt to add this kind of content as an Attached File annotation, so it will be opened with an external app and opens the range of sound file formats you can embed into the PDF.</p>



<p>The way to add Sound annotations using PDFDocument is shown here:</p>



<pre class="wp-block-code"><code>Var d As New PDFDocument
Var g As Graphics = d.Graphics
g.FontSize = 30

Const kContentText As String = "Embedded Sound Annotation"

Var x, y, width, height As Double

x = g.Width/2 - width/2
y = 60

width = g.TextWidth(kContentText)
height = g.TextHeight

g.DrawText(kContentText, x, y)

If exampleSound &lt;&gt; Nil Then
  
  // Creating a new EmbeddedSound.
  d.AddEmbeddedSound(exampleSound,x,y,width,height)
  
  // Saving the created PDF document to the selected path.
  Var f As FolderItem = FolderItem.ShowSaveFileDialog("","PDFEmbeddedSound.pdf")
  
  If f &lt;&gt; Nil Then
    
    d.Save(f)
    
  End If
End If
</code></pre>



<p>Here <code>exampleSound</code> is a FolderItem Property added to the project. And this is how a Sound annotation will look when the PDF document is opened using Acrobat Reader:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="361" src="https://blog.xojo.com/wp-content/uploads/2022/06/SoundAnnotation-1024x361.png" alt="" class="wp-image-10495" srcset="https://blog.xojo.com/wp-content/uploads/2022/06/SoundAnnotation-1024x361.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/06/SoundAnnotation-300x106.png 300w, https://blog.xojo.com/wp-content/uploads/2022/06/SoundAnnotation-768x271.png 768w, https://blog.xojo.com/wp-content/uploads/2022/06/SoundAnnotation-1536x542.png 1536w, https://blog.xojo.com/wp-content/uploads/2022/06/SoundAnnotation-2048x722.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<h3 class="wp-block-heading">Markup Annotations</h3>



<p>A Markup annotation is intended to mark an underlying text content using any of the different available types, for example &#8220;Highlight&#8221;, &#8220;Squiggly&#8221; (usually used to mark a text that needs some kind of correction), &#8220;Underline&#8221; or &#8220;Strikeout&#8221;. Besides the Markup type, it is also possible to set the color of the annotation.</p>



<p>For example, the following snippet of code adds two Markup annotations to the page of the PDFDocument using the Highlight and Squiggly types:</p>



<pre class="wp-block-code"><code>Var d As New PDFDocument
Var g As Graphics = d.Graphics
Const kContentText As String = "Markup Annotation in PDF"

g.FontSize = 40
g.Bold = True
g.DrawText(kContentText, 40, 60)

// Adding a Markup annotation with the Highlight type (Default)
d.AddMarkup(40,40,g.TextWidth(kContentText),g.FontSize,"Let's focus on this Section")

g.Bold = False
g.FontSize = 12
g.DrawText(kSampleText, 40, 100, g.Width-80)

// Adding a Markup annotation with the Squiggly type
d.AddMarkup(40,195,210,g.FontSize,"Some text to correct!",Color.Red,PDFAnnotation.MarkupTypes.Squiggly)

// Saving the created PDF document to the selected path.
Var f As FolderItem = FolderItem.ShowSaveFileDialog("","PDFMarkupExample.pdf")

If f &lt;&gt; Nil Then
  
  d.Save(f)
  
End If</code></pre>



<p>And this is how it looks when the resulting document is opened in Adobe Acrobat Reader:</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="632" src="https://blog.xojo.com/wp-content/uploads/2022/06/MarkupAnnotation-1024x632.png" alt="" class="wp-image-10499" srcset="https://blog.xojo.com/wp-content/uploads/2022/06/MarkupAnnotation-1024x632.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/06/MarkupAnnotation-300x185.png 300w, https://blog.xojo.com/wp-content/uploads/2022/06/MarkupAnnotation-768x474.png 768w, https://blog.xojo.com/wp-content/uploads/2022/06/MarkupAnnotation-1536x949.png 1536w, https://blog.xojo.com/wp-content/uploads/2022/06/MarkupAnnotation-2048x1265.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</div>


<h3 class="wp-block-heading">Bonus</h3>



<p>Let me end with one other minor improvement to PDFDocument annotations: starting with Xojo 2022r2 all of them include metadata information about the creation and modification date and time. this information will be displayed as part of the annotation metadata when the item is selected in the viewer app.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Creating Presentations with PDF and Xojo</title>
		<link>https://blog.xojo.com/2022/05/26/creating-presentations-with-pdf-and-xojo/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Thu, 26 May 2022 15:35:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[XDC]]></category>
		<category><![CDATA[Xojo Cloud]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10319</guid>

					<description><![CDATA[I know, I know, it's not the usual thing to do when making presentations, but did you know that it is possible to create presentations using Xojo's and PDF? If you're interested in this little experiment, read on and I'll show you how to do it using Xojo's PDFDocument. ]]></description>
										<content:encoded><![CDATA[
<p>I know, I know, it&#8217;s not the <em>usual</em> thing to do when making presentations, but did you know that it is possible to create presentations using Xojo&#8217;s and PDF? If you&#8217;re interested in this little experiment, read on and I&#8217;ll show you how to do it using Xojo&#8217;s <a href="https://documentation.xojo.com/api/pdf/pdfdocument.html#pdfdocument">PDFDocument</a>. </p>



<span id="more-10319"></span>



<p>Why, you ask? What are the advantages of using a PDF for your presentations? PDF is portable, meaning you can distribute these files easily, usually, with a small file footprint. The content is vectorial (except for images), what means that it will look great no matter the screen resolution of the display/projector used for playing the presentation.</p>



<p>There is one current issue to watch out for, currently <a href="https://acrobat.uservoice.com/forums/590923-acrobat-for-windows-and-mac/suggestions/44616444-fullscreen-page-transitions-in-pdfs-no-longer-work">there&#8217;s a bug on macOS</a> where Acrobat Reader is not able to apply the assigned transitions on Big Sur and later; they still work under Catalina and previous versions of macOS.</p>



<p>In this example project I will create a shortened version of the slides I used in the <a href="https://youtu.be/aSfff3hQaX4">Using Xojo&#8217;s PDFDocument</a> presentation. You&#8217;ll need to change the font names (and any other item) to those you are using and that are installed in your computer.</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="960" height="540" src="https://blog.xojo.com/wp-content/uploads/2022/04/Presentation.gif" alt="" class="wp-image-10320"/></figure>
</div>


<h3 class="wp-block-heading">Setting the Viewer Options</h3>



<p>Start by creating a new project for the target you&#8217;re most comfortable with &#8211; Xojo&#8217;s PDFDocument is cross-platform and supported on Desktop, Web, iOS and Console projects. Next, add the Opening Event Handler to the default DesktopWindow/WebWindow/MobileScreen, or choose the Run Event Handler if you chose to create a new Console Project.</p>



<p>Write the following lines of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">// Let's create a new PDFDocument instance with
// the page size set to HD resolution

Var Presentation As New PDFDocument(1920, 1080)

// Let's make sure that the fonts used are embedded in the
// document, so there are no unexpected problems when the
// PDF is opened in a device that doesn't have that
// same fonts installed

Presentation.EmbeddedFonts = True</pre>



<p>The next lines of code will set the default for the View mode of the document when it is opened in Acrobat Reader or any other PDF reader. Since it&#8217;s a presentation, you&#8217;ll likely want the document to be opened in Full Screen mode by default, so let&#8217;s make that happen:</p>



<pre class="wp-block-preformatted">// Let's create a new Instance from the PDFViewerOptions class

Var ViewerOptions As New PDFViewerOptions

// And set the FullScreen property to True
// so the document will set that view size
// when opened in the PDF Viewer app

ViewerOptions.FullScreen = True</pre>



<h3 class="wp-block-heading">Drawing the Background of the Page</h3>



<p>Use a linear gradient brush to fill in the background of all the slides. For convenience, create a new method for it using the following values in the Inspector Panel:</p>



<ul class="wp-block-list"><li><strong>Name:</strong> DrawPageBackground</li><li><strong>Parameters:</strong> g As Graphics</li></ul>



<p>Type the following code in the associated Code Editor for the method:</p>



<pre class="wp-block-preformatted">Static backgroundfilling As LinearGradientBrush

If backgroundfilling = Nil Then
  backgroundfilling = New LinearGradientBrush
  backgroundfilling.StartPoint = New Point(g.Width / 2, 0)
  backgroundfilling.EndPoint = New Point(g.Width / 2, g.Height)
  backgroundfilling.GradientStops.Add(New Pair(0.0, &amp;c000000))
  backgroundfilling.GradientStops.Add(New Pair(0.7, &amp;c000000))
  backgroundfilling.GradientStops.Add(New Pair(1.0, &amp;ccccccc))
End If

g.Brush = backgroundfilling
g.FillRectangle(0, 0, g.Width, g.Height)</pre>



<p>All this code does is create a LinearGradientBrush instance ranging from full black to some degree of gray at the bottom of the page. Because we are not going to change the values of the gradient every time we need to call the method, it makes sense to assign it to a Static instead of a Variable.</p>



<p>Then the LinearGradientBrush is assigned to the Brush property of the received Graphic context and, finally, used as the &#8220;filling color&#8221; when drawing the filled rectangle.</p>



<h3 class="wp-block-heading">Drawing the Header</h3>



<p>Now add the method that will be responsible for drawing the header shared by all the slides. Use the following values in the Inspector Panel:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> DrawTopHeader</li><li><strong>Parameters:</strong> g As Graphics</li></ul>



<p>And type the following code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">Const kPDFDocumentHeader As String = "PDFDocument"

DrawUpperBand(g, 0, 50)

g.FontName = "Helvetica"
g.FontSize = 180
g.Bold = True

Var x As Double = g.Width / 2 - g.TextWidth(kPDFDocumentHeader) / 2
Var y As Double = 220

g.DrawingColor = &amp;cC9F2C900
g.DrawText(kPDFDocumentHeader, x + 4, 224)

g.DrawingColor = Color.Black

Static linearGradient As LinearGradientBrush

If linearGradient = Nil Then
  Lineargradient = New LinearGradientBrush
  linearGradient.StartPoint = New point(x, y - 224)
  linearGradient.EndPoint = New point(x, y)
  linearGradient.GradientStops.Add(New Pair(0.0, &amp;c00BD4E00))
  linearGradient.GradientStops.Add(New Pair(0.7, &amp;c00FD1F00))
  linearGradient.GradientStops.Add(New Pair(1.0, &amp;c25712900))
End If

g.Brush = LinearGradient
g.DrawText(kPDFDocumentHeader, x, y)</pre>



<p>As you can see, this is quite similar to the previous code. We are using a new Linear Gradient as the filling for the &#8220;PDFDocument&#8221; text. But first, it calls the DrawUpperBand method. This is a method that will draw a picture with a 50% opacity behind the header text, as well as the XDC logo in the lower left corner of the slide. Add a new method using the following values:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> DrawUpperBand</li><li><strong>Parameters:</strong> g As Graphics, x As Integer, y As Integer</li></ul>



<p>Type the following snippet of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">g.Transparency = 50.0
g.DrawPicture(UpperBand, x, y)
g.Transparency = 0
g.DrawPicture(XDC2x, 20, g.Height - XDC2x.Height)</pre>



<p>Both &#8220;UpperBand&#8221; and &#8220;XDC2x&#8221; are PNG image files added to the project.</p>



<h3 class="wp-block-heading">NextPage</h3>



<p>Because the background and the header are common items in all the slides, we will render them every time we add a new slide (page) to the presentation (document). Create a new method using the following values:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> NextPage</li><li><strong>Parameters:</strong> g As Graphics</li></ul>



<p>And type the following lines of code:</p>



<pre class="wp-block-preformatted">g.NextPage(1920, 1080)
DrawPageBackground(g)
DrawTopHeader(g)</pre>



<h3 class="wp-block-heading">Creating the Content of the Slides</h3>



<p>Now let&#8217;s add the method that will be responsible for adding the text of the main topic on every slide:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> DrawTopic</li><li><strong>Parameters:</strong> g As Graphics, topic As String</li></ul>



<p>And type the following code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">Var x, y As Double
g.FontName = "Anoxic Light"
g.FontSize = 150

x = g.Width / 2 - g.TextWidth(topic) / 2
y = g.Height / 2 - g.TextHeight / 2

Static LinearGradient As LinearGradientBrush

If LinearGradient = Nil Then
  linearGradient = New LinearGradientBrush
  linearGradient.StartPoint = New Point(x, y)
  linearGradient.EndPoint = New Point(x, y - 80)
  linearGradient.GradientStops.Add(New pair(1.0, &amp;c9FE1FF00))
  linearGradient.GradientStops.Add(New pair(0.0, &amp;cF6F6F600))
End If

g.Brush = linearGradient
g.DrawText(topic, x, y)</pre>



<h3 class="wp-block-heading">Setting Transitions Between Slides</h3>



<p>PDFDocument does support the range of transitions available under the PDF standard. In this case we are going to use the simplest: Fade. So, create a new method with the following values:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> AddTransitions</li><li><strong>Parameters:</strong> d as PDFDocument</li></ul>



<p>And type the following lines of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">// New PDFTransition instance, using the Fade style
// with one second of length.

Var t As New PDFTransition(PDFTransition.Styles.Fade, 1)

// And apply it on every page of the PDFDocument, starting on Page 2.
If d.PageCount &gt; 2 Then
  For n As Integer = 2 To d.PageCount
    d.TransitionAt(n) = t
  Next
End If</pre>



<h3 class="wp-block-heading">Putting It All Together</h3>



<p>Now we have all the pieces needed to create this small presentation in PDF. Go back to the Opening Event Handler and type the following lines of code below the last one:</p>



<pre class="wp-block-preformatted">DrawPageBackground(g)
DrawTopHeader(g)
DrawTopic(g, "First Slide")
NextPage(g)
DrawTopic(g, "Second Slide")
NextPage(g)
DrawTopic(g, "Third Slide")
NextPage(g)
DrawTopic(g, "Fourth Slide")

AddTransitions(d)

d.Save(SpecialFolder.Desktop.Child("Presentation.pdf"))</pre>



<p>Done! Run the project and the PDF document will be saved to your computer Desktop. Once you open it in Adobe Reader, the app will detect that the document wants to use the full screen mode and, as soon as you confirm, it will begin the presentation. Depending on your preferences in Acrobat Reader, you may need to use the mouse button or arrow keys to change slides, or it may automatically advance to the next slide after the amount of time set.</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Graphics: Right and Center Aligned Text</title>
		<link>https://blog.xojo.com/2022/05/17/graphics-right-and-center-aligned-text/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Tue, 17 May 2022 13:00:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Xojo API 2.0]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=10351</guid>

					<description><![CDATA[The DrawText method from the Graphics class offers a simple way to left-align text at the given X and Y coordinates, even applying a wrap value to wrap every line of the text block at the provided width value. Wouldn't it be great to be able to do the same thing while aligning blocks of text to the right and center? Continue reading and I will show you a technique you can use as the starting point which you can fine-tune to your specific needs.]]></description>
										<content:encoded><![CDATA[
<p>The <a href="https://documentation.xojo.com/api/graphics/graphics.html#graphics-drawtext">DrawText</a> method from the <a href="https://documentation.xojo.com/api/graphics/graphics.html#graphics">Graphics</a> class offers a simple way to left-align text at the given X and Y coordinates, even applying a wrap value to wrap every line of the text block at the provided width value. Wouldn&#8217;t it be great to be able to do the same thing while aligning blocks of text to the right and center? Continue reading and I will show you a technique you can use as the starting point which you can fine-tune to your specific needs.</p>



<div class="wp-block-image wp-image-10353 size-large is-style-default"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="665" src="https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentLinux-1024x665.png" alt="" class="wp-image-10353" srcset="https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentLinux-1024x665.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentLinux-300x195.png 300w, https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentLinux-768x499.png 768w, https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentLinux.png 1472w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Right-aligned and centered text on Linux Desktop, both in a Picture and a document created with PDFDocument.</figcaption></figure></div>



<h3 class="wp-block-heading">Graphics Extensions</h3>



<p>To start, and because we want to provide this ability to any graphic context, it makes sense to include all our logic in a Module. Let&#8217;s name it GraphicExtensions (as usual you can opt to use any other name). Start a new Xojo project and add a new Module to it using the following values in the Inspector Panel:</p>



<ul class="wp-block-list"><li><strong>Name:</strong> GraphicsExtensions</li></ul>



<p>Next, and with the GraphicsExtensions item selected in the Navigator, add a new method to it using the following values in the associated Inspector Panel:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> DrawText</li><li><strong>Parameters:</strong> Extends g As Graphics, value As String, x As Double, y As Double, wrap As Double, alignment As TextAlignments = TextAlignments.Left</li><li><strong>Scope:</strong> Global</li></ul>



<p>As you can see, it nearly replicates the standard DrawText method from the Graphics class. Some things to note here, we are using the <a href="https://documentation.xojo.com/api/language/extends.html#extends">Extends</a> keyword in combination with the first parameter to instruct the compiler that this method will &#8220;extend&#8221; the existing functionality of the Graphics class, so you&#8217;ll be able to invoke it using the usual dot notation on any instance created from the Graphics class.</p>



<p>The second thing is the last of the parameters &#8220;alignment As TextAlignments&#8221; <a href="https://documentation.xojo.com/api/text/textalignments.html#textalignments">TextAlignments</a> is a global enumeration that we are going to use to learn the text alignment the user wants to apply to the block of text.</p>



<p>Next, type the following snippet of code in the associated Code Editor for the method:</p>



<pre class="wp-block-preformatted">#Pragma DisableBackgroundTasks
#Pragma DisableBoundsChecking
#Pragma NilObjectChecking False

value = value.ReplaceLineEndings(EndOfLine)

Select Case Alignment
  
Case TextAlignments.Default, TextAlignments.Left
  
  g.DrawText(value, x, y + g.TextHeight, wrap - x)
  
Case TextAlignments.Center, TextAlignments.Right
  
  Var tOutput() As String = PrepareOutput(g, value, wrap, x)
  
  Var tx, ty As Double
  ty = y + g.TextHeight
  
  Select Case Alignment
    
  Case TextAlignments.Center
    
    For Each s As String In tOutput
      
      tx = If(Wrap = 0, ((g.Width - Wrap) / 2 - g.TextWidth(s) / 2) + (x / 2), (wrap / 2 - g.TextWidth(s) / 2) + (x / 2))
      g.DrawText(s, tx, ty)
      ty = ty + g.TextHeight
      
      If ty &gt; g.Height Then Exit
      
    Next
    
  Case TextAlignments.Right
    
    For Each s As String In tOutput
      
      tx = If(Wrap = 0, (g.Width - g.TextWidth(s)) + x / 2, (wrap - x) - g.TextWidth(s) + x)
      g.DrawText(s, tx, ty)
      ty = ty + g.TextHeight
      
      If ty &gt; g.Height Then Exit
      
    Next
    
  End Select
  
End Select
</pre>



<p>As you can see, this code looks into the value of the Alignment parameter to see the approach to apply. If the Alignment equals to Default or Left we simply call the standard DrawText method on the received graphics context represented by the &#8220;g&#8221; variable. If not, we need to do some stuff to the received block of text to create the required chunks (or lines) that do fit the available width plus the received wrapping value. This is done in the PrepareOutput method.</p>



<p>Once we have all the lines with the apropiate widths in the Output array, then we only need to do some basic maths in order to calculate the final TX coordinate for every line, incrementing the TY coordinate on every new line to draw. As you can see, and in order to speed-up things a bit more, we check if the calculated TY coordinate is beyond the visible area of the graphic context and, if it is the case, we exit the function (it wouldn&#8217;t make much sense drawing text that is not going to be visible and whose processing consumes time).</p>



<p>Of course, the calculation of the TX and TY coordinates depends on the value of the Alignment parameter; that is, if the block of text is going to be drawn right aligned or centered.</p>



<h3 class="wp-block-heading">Calculating Every Line Width</h3>



<p>Now add the PreapareOutput method to the GraphicsExtensions module using the following values in the associated Inspector Panel:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> PrepareOutput</li><li><strong>Parameters:</strong> g As Graphics, value As string, wrap As Double, x As Double</li><li><strong>Return Type:</strong> String()</li><li><strong>Scope:</strong> Private</li></ul>



<p>And typing the following snippet of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">#Pragma DisableBoundsChecking
#Pragma DisableBackgroundTasks
#Pragma NilObjectChecking False

Var totalWidth As Double = If(wrap = 0, g.Width - x, wrap - x)

Var Input() As String = value.ToArray(EndOfLine)
Var output() As String

For Each s As String In Input
  
  If g.TextWidth(s) &lt;= totalWidth Then
    output.Add(s)
  Else
    AdjustWidth(g, s, totalWidth, output)
  End If
  
Next

Return output
</pre>



<p>Once again, the first lines are some pragmas put in place to speed-up things, then the totalWidth variable will store the required maximum width value for every line. The Input array is going to store every paragraph from the source text, while the Output array will store every processed line and, the one returned to the method calling PrepareOutput.</p>



<p>Then we need to iterate every entry in the Input array and check if its width meets the totalWidth requirement. If it does, then that entry will be added to the Output array; if not, we still need to split the &#8220;paragraph&#8221; into as many chunks of text as needed that meet the expected maximum width. That is something that&#8217;s going to be done by the AdjustWidth method.</p>



<h3 class="wp-block-heading">Splitting Chunks of Text …&nbsp;Recursively</h3>



<p>Add the third, and last required, method to the GraphicExtensions module using the following values in the associated Inspector Panel:</p>



<ul class="wp-block-list"><li><strong>Method Name:</strong> AdjustWidth</li><li><strong>Parameters:</strong> g As Graphics, s As String, width As Double, ByRef output() As String</li><li><strong>Scope:</strong> Private</li></ul>



<p>Next, type the following lines of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">#Pragma DisableBoundsChecking
#Pragma DisableBackgroundTasks
#Pragma NilObjectChecking False

Var n As Integer

If g.TextWidth(s) &lt;= width Then
  output.Add(s)
Else
  
  // This can be improved pre-calculating the initial value for "n"…&nbsp;so it's
  // left as an exercise for the reader :-)
  
  While round(g.TextWidth(s.Left(s.Length - n))) &gt; width
    n = n + 1
  Wend
  
  output.Add(s.Left(s.Length - n))
  
  AdjustWidth(g, s.Right(n), width, output)
  
End If
</pre>



<p>As you can see, if the width of the received string is less than or equal to the expected width, then it is added to the Output array. If not, we are using a really less than optimal technique to calculate the appropriate one, saving the resulting string to the Output array and passing the remaining of the string again to the method. You&#8217;ll probably want to change some things here to better pre-calculate the initial value of the <code>n</code> variable.</p>



<h3 class="wp-block-heading">Testing It</h3>



<p>We have everything in place, so let&#8217;s test it! Start by adding a new constant to the Window1 window and name it <code>kSampleText</code>. Use the associated Inspector Panel to assign the block of text you want to use for testing (personally I like to use the <a href="https://www.lipsum.com">https://www.lipsum.com</a> website for getting sample text).</p>



<p>If you prefer, you can <a href="https://www.dropbox.com/s/amt86u9fnbq31xh/GraphicsExtensions.xojo_binary_project.zip?dl=1">download the complete Example project from this link</a>.</p>



<p>Add next the Opening Event Handler to the Window1 window and type following snippet of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">Var p As New Picture(612, 792)
Var d As New PDFDocument

Var g As Graphics = p.Graphics
g.FontName = "Helvetica"
Var gPDF As Graphics = d.Graphics

g.DrawText(kSampleText, 40, 10, 570, TextAlignments.Right)
gPDF.DrawText(kSampleText, 40, 10, 570, TextAlignments.Right)

Var tH As Double = g.TextHeight(kSampleText, 570) + g.TextHeight * 4
Var tPH As Double = gPDF.TextHeight(kSampleText, 570) + gPDF.TextHeight * 4

g.DrawText(kSampleText, 40, th, 570, TextAlignments.Center)
gPDF.DrawText(kSampleText, 40, tPh, 570, TextAlignments.Center)

CustomDesktopCanvas1.Image = p
d.Save(SpecialFolder.Desktop.Child("PDFTextAlignment.pdf"))
</pre>



<p>As you can see, I chose the &#8220;Helvetica&#8221; font, but you can change it to any font that is available in your OS. The point here is that we are feeding our &#8220;extended&#8221; DrawText method with the sample text and the alignment we want to use in for drawing it both in the graphic context of a Picture and the one from a PDFDocument instance. Also, CustomDesktopCanvas1 is a simple Canvas subclass with an &#8220;image&#8221; property added to it as a computed property, so when a new value is assigned it will refresh itself and the Paint event will draw the picture centered in the canvas.</p>



<div class="wp-block-image is-style-default"><figure class="aligncenter"><img loading="lazy" decoding="async" width="1024" height="838" src="https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentMacOS-1024x838.png" alt="" class="wp-image-10352" srcset="https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentMacOS-1024x838.png 1024w, https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentMacOS-300x245.png 300w, https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentMacOS-768x628.png 768w, https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentMacOS-1536x1257.png 1536w, https://blog.xojo.com/wp-content/uploads/2022/05/AlignmentMacOS-2048x1675.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure></div>



<p>Run the example project and you should see something similar to the result displayed in the screenshots. Of course, there are still some details to take care of, for example when a line is split so the last letter goes over to the next line and things like that but, as mentioned in the beginning of this post, this is just a starting point. Adapt and improve upon it as needed.</p>



<p>Have fun!</p>



<p><em>Javier Menendez is an engineer at Xojo and has been using Xojo since 1998. He lives in Castellón</em>, <em>Spain and hosts regular Xojo hangouts en español. Ask Javier questions on Twitter at <a href="https://twitter.com/xojoes" target="_blank" rel="noreferrer noopener">@XojoES</a> or on the <a href="https://forum.xojo.com/u/javier_menendez/summary" target="_blank" rel="noreferrer noopener">Xojo Forum</a>.</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
