Welcome to the Qt Design Studio Sketch Bridge tutorial. To follow it, you will need the commercial package Qt Design Studio 1.5 and Sketch Bridge, macOS and Sketch (developer uses 66.1).
This tutorial will show you how to make a sketch project that creates a clean export and import in Qt Design Studio (we'll call qds for short for the rest of the tutorial), uses symbols and instances for proper componentization, and moves from Sketch to qds in iterative loops. creating a more complex scene from simple building blocks. We will also talk about some of the most common problems that users face, give tips and tricks that the developer created while working with the Bridge plugin.
Before we get into further clarification, while Sketch allows designers the flexibility and openness to implement their design concepts, in order to create a pixel-perfect design built on developer-friendly components in qds, it is very important to structure and prepare your project in a certain way, and although it mastering is not too difficult, it takes time and knowledge to successfully complete it. The developers hope that this guide will provide you with the knowledge you need to bring your projects closer to your desired outcome. Let's dive in and start designing.
Part 1.1 - A simple button
The first step will start by designing a single button that will be used as the basis for the project and run it in Sketch, so open the Sketch app and create a new empty file.
First, let's create a default button background by dragging a rectangle. It's worth making sure it's the right size for the button, then turning it into a symbol. Once the symbol has been created, let's delete the instance and work directly on the main symbol.
You can then add some well-rounded corners, create a gradient, and add a shadow and highlight to the button. To make the design lighter, you can stretch the artboard a bit to make room for shadows and add a working background for the symbol. Let's choose a color that is not going to be used in the design, but that gives a strong contrast to the colors of the design, like this pale pink in the video below.
Also, although these two options have been deselected, this background does not need to be exported or appear when characters are later used. With the design space in place, let's round these corners, add a simple gradient and some shadows.
We now have a default background state for the button, so let's create two other states that we'd like to use for this tutorial - the hover and pressed state .
You can do this by duplicating the original rectangle, renaming the layers, and then placing them side by side so you can see the parallel design changes. To make this a little easier, let's stretch the character's width to fit the button next to it, resizing it after we're done with the design.
We now have a nice setup, so let's change the hover and click versions of the button's background by slightly adjusting the gradient colors until we have three versions of the button. While there are no real rules in UX that can be considered relevant, as long as these states are consistent and work with your overall UI design, developers stick to the logic that the hover state is a little brighter than normal and the pressed state is a little darker.
At this point, the developers will also name their layers and will use those names for the qml IDs as well, but this will be covered in more detail shortly.
With the three backgrounds created earlier, let's place them back on top of each other as we want them to be the same for export, and add one more element to make the top layer - hotspot , which when imported into design studio will be automatically converted to mouse area.
It's important that when adding an access point to set the target to none, you need to make sure it's the top layer of the button in order to properly capture mouse events.
It's also worth making sure the artboard with the button on it is big enough to accommodate all the shadow effects that were added without clipping, but got close enough to the borders to make sure the element's bounding box isn't too big. Good and border-tight effects work best.
After all the previous steps, there is a simple button to prepare settings in the Qt Bridge plugin.
The most common errors developers see with users using the sketch bridge are due to duplicate IDs in their project, although the qds importer is able to detect and store qml IDs, it is still a good idea to manually check all qml IDs to make sure they are are unique and significant.
One problem is that after renaming the qml id for a symbol, each instance of that symbol will have a duplicate id. The developer's advice is to use a naming scheme that distinguishes between a symbol and instances of that symbol.
For example, let's make sure your button symbol has an id of myButton_Symbol and different layers have that prefix with attached states like myButton_Symbol_default etc. And since there will probably be more mouse areas later in this project, you can rename the hotspot to myButton_Symbol_hotspot, when we use the button as screen instances, make sure to give those instances a unique ID associated with that screen, and also import to note that qml identifiers can only use a subset of the possible characters. To do this, the guide will use underscores to separate names, as it is well known that this format will work.
When we renamed these IDs, it's worth making sure that all layers are set to Child, since it is desirable that there are separate assets for each layer, so we will not merge anything at this stage. Later, when we create some backgrounds, we will use union to minimize the number of individual assets in the design, but for now, let's set all these qml ids correctly and make sure that each of the button designs has its export type set to child.
From the developer's subjective experience, it makes sense to name the layers exactly like your qml ids, this will make it much easier to find illustrations later, especially as these files can become very large and complex as they approach the completion level of a UI project.
In a Qt Bridge plugin, it's worth first setting up a few basic properties on the settings page. These are the global settings that will be used for the rest of the project, so you only need to do this once. First you need to select the export path. To do this, let's create a project folder, where we can later create a Design Studio project, in which we will create an export folder and select it as the location where we want to put assets and metadata. Create the necessary folders using the keyboard shortcut shift+cmd+n.
Let's leave all other settings as default and use png for this tutorial. At this point, you're all set to return to the Bridge Plugin's home panel and perform the first export.
With all the qml ids and export types set correctly, you're ready to export the component and bring it into Design Studio, where we'll add some states and test the button before we continue developing in Sketch.
So let's just click on export and then go to Design Studio.
Part 1.2 - First import
To get started in Design Studio, you can create an empty project to import Sketch design, so let's go to the home page and click on the new project button, this will launch the wizard, after which you need to select an empty default project.
You can select a project folder as the location for this project, and then leave the rest of the settings as default. After completing the wizard, it will automatically switch to the default ui.qml file that comes with the project, in order to import the sketch design, now you can go to the assets panel, add new assets and then go to the folder where the projects were exported from Sketch.
Next, you need a .metadata file. This option will only be available if you have a commercial version of Design Studio with the importer plugin enabled.
To check this, you can look at the available imports in the file type filter combo box. If the metadata type is there, then you have the correct setup, and you can select this file to launch the import panel.
For the first import, you should use the default settings, then export the metadata and assets, leaving the merge disabled. But, since we want to start working on some things right away in Design Studio, we will leave the merge function enabled for all subsequent imports, so do not accidentally overwrite your work in qds.
Once the import is complete, you can close the importer and take a look at the imported files. They will be located in a folder named after your export folder. If you go to this folder, you will see the elements. At the moment you only have one button component, so let's go to this file and check that your export is correct.
This file should have three different button layers to be used for different states plus the mouse area. By default, sketch attaches an empty layer to the export, here it's called an asset, and the first thing to do now is to delete this layer, since it's not needed in your component.
After that, you need to set the hover mouse area to be enabled as the hover state will be used. Let's do it right now by clicking on the mouse area in the navigator, going to the properties panel and selecting mouse hover for your mouse area.
Since we are currently in the Base State, this is the only state we have at the moment, so it will apply to every state that is added from now on. While you're here, you can add the states you want. You should always try to use explicit user states for an application.
The base state is useful for making changes to your design that we want to apply to any other state, if we are currently in a user-created state then any changes we make in the current state will only apply to that state.
So let's create the three states you need for this button. Let's add a state with a default name, one named hover and one named clicked.
Now that you have these states, you can change the default system state to a custom default state. We do this by clicking the action icon on the state thumbnail and selecting “make default”, now every time this component is loaded this will be the state in which the button is visible.
Now you need to add some logic to these states to connect them to your mouse area. We will do this in the next step using the option when the selection conditions will also be here on the action icon. For now, let's prepare an illustration in each of the respective states, and since this is the first import, let's start by simply showing the correct corresponding asset layer in each state.
The easiest way to do this is to simply make the respective layer visible and all other layers invisible in every state. So first let's go back to the default state by clicking on the desired thumbnail in the state bar. With this state selected it is possible to go to your images in the navigator and select each of the assets one by one and toggle the visibility properly for each layer so the default state needs the default layer to be visible and on hover and click set to invisible .
Once they're set up, you can go into the hover state and repeat this process, making the hover image the only visible layer. Finally, we do this again for the pressed layer, selecting the pressed state and repeating the process to make only the pressed layer visible.
Part 1.3 - Execution Conditions
You should now be ready to add logic to these states to connect them to your mouse area, but first it's worth a quick check that everything is set up correctly by clicking between the state thumbnails. If everything is correct, you should only see the corresponding layer in the form editor. Assuming everything is working fine, let's go ahead and add some logic to the states/mouse area, we'll do it with "Run Conditions" - basically, this means that when certain conditions are met, that state will be active. Execution conditions can get complicated quite quickly and often, you need to add more than one condition to properly model any meaningful behavior. You'll see that in two states, two separate conditions need to be evaluated to make sure the behavior is correct. You also have to be careful to check if all the conditions being tested are true or not true.
In the context of qml and qds, this means that it is possible to validate the properties of multiple elements and move the user interface to a state where those conditions apply. You can see if something is true, not true, greater, equal, etc. You can also compare multiple elements for their validity with operators such as AND or OR.
Execution conditions are evaluated from left to right and in order of appearance in the code, so if you have two conditions for different states that are both true, it will choose the first of those conditions to apply to your state. Therefore, it is important to make sure that each condition can only be true in one state.
If this all sounds a bit complicated at the moment, just try following it step by step, perhaps by the end you'll have a better idea of the usefulness and power of runtime conditions.
Let's start by adding a default condition. You need to make sure that your default button is displayed when the mouse is not hovered over the button and the mouse button is not pressed. Both of these conditions must be a "not" condition, and both must be true, so the mouse is not hovered and the button is not pressed - this is a valid statement.
We can define a fail condition by adding an exclamation point in front of our conditions, and to evaluate both conditions we will use a double ampersand character between them. So let's go ahead and do it right now. Start by selecting the default state in the state thumbnail preview panel, then click the action icon and select the “add when condition” option, this will open the binding editor and then you can add your condition here.
It is very important that the condition is written correctly, so we will use the autocomplete function to make sure there are no spelling errors.
Default State Condition
After the first three letters of the element to be checked, in this case it is the mouse area called myButton_Symbol_mouseArea. After the first three characters are entered, there should be a list of all possible completions, so let's select the mouse area from the list. In this first condition, you need to check the mouse area property, which is called containsMouse.
This will tell you if the user hovered over the button. So after you have the mouse area in the Binding Editor, you can add a full stop and then iterate through the list and find the property containing the mouse and select it.
Now there is a first condition that checks that the user has not currently hovered over the button, and it looks like this:
!myButton_Symbol_mouseArea.containsMouse
You also need to evaluate the second condition to make sure the user doesn't click the button as well, you can set a condition to evaluate both things by adding two ampersands between them. Now you can repeat the first step, as it is worth checking this area of the mouse again.
This is followed by the first letters of the mouse area, which brings up the autocomplete list. Select the mouse area again and add a full stop to get a list of properties, this time the property we're interested in is a click that tells us if this button contains a mouse pressed.
So the final condition for that first button should look like this:
!myButton_Symbol_mouseArea.containsMouse && !myButton_mouseArea.pressed
By itself, when this condition is not true, it does nothing, it just ensures that after the hover or click event has passed, the button will return to its default state. Since at the moment the button is always fired in the default state, no changes will be seen, so let's add the following conditions, and then we can test the button.
Hover State Condition
The second condition needs to be for your hover button, so let's first make sure we're in the correct state by clicking the hover state thumbnail in the thumbnail bar, and then after getting the correct state in the form editor, we can add the following. In this case, you need to have a hover state when the mouse is over the mouse area, but also not pressed down (since that would be the pressed state).
So let's run the binding editor for this state again by clicking on the action icon and selecting "add when condition". With the binding editor open, you can use the autocomplete feature to make sure the condition is written correctly. The first condition we need is the one that checks if the mouse is over the mouse area, so again looking for
myButton_Symbol_mouseArea
When we enter the first three letters and get a list of elements available in the file, you can scroll down and select the mouse area. Once chosen, we add a breakpoint to the end and run the list of available properties for evaluation.
The property we want is called containsMouse so we can either start typing to narrow down the list or scroll down and select it, notice that this time the exclamation mark is not added because we want to check if the condition is true and the exclamation mark checks if the it is false. So, there is a first condition for this state:
myButton_Symbol_mouseArea.containsMouse
We also need to make sure that the hover state is not displayed when the button is clicked, because the mouse must be in the mouse area in order to click the button. To distinguish between these two events, let's add another condition to the state.
With the binding editor open, you can add && and then start adding another condition. In this case, it is the same second condition as in the previous default state. Starting with an exclamation mark, you can re-enter the !myButton_mouseArea.pressed condition, checking that the button is not pressed.
So now you should have a complete condition for that state and it should look like this:
myButton_mouseArea.containsMouse && !myButton_mouseArea.pressed
The first checks that the mouse is over the button but not yet pressed. As you can see, the only difference is the exclamation point at the beginning, so you need to carefully check all conditions to make sure everything is formatted correctly.
Let's add a final condition, and then we can test the button, and the developer will give advice that will help when debugging more complex states and conditions.
Pressed State Condition
So, the last condition for the pressed state is simpler, simpler, since the only thing that needs to be evaluated is whether the button is pressed or not. Let's switch to the pressed state by clicking the thumbnail in the status bar again, launch the binding editor with the action icon, and this time we need a simple condition:
myButton_mouseArea.pressed
All states now have conditions that could be tested with this button by performing a live preview. In this case, it's pretty easy to see if the right states are activated at the right time, however, the more complex your design and the more subtle the changes, the more difficult it becomes. You will see it just by watching the preview. So let's use a little trick to make sure we're in the right state.
First, for this you need to make sure that you are in the base state. You need to add a label that will show the current state you are in, so the label and anchor that will be used must be in each state and have the correct way to handle it. To get to the basic state, click it in the states bar, then go to the Qt-Quick Basics section in the QML Types library and drag a text element into the form editor at the top of your button.
You'll need to remove it once you're sure the states are correct, so it doesn't really matter what it looks like as long as it's readable. So if you need to adjust the size and color to make the label readable, do so now.
Once the label is ready, the anchor will need to be added, so let's make sure the text element is selected and then we can go to the property panel and find the text property for that element.
Now hover your mouse over it to display the action icon and launch the Binding Editor by selecting "set binding". Here we will use this text label to show the current state of the component. which can be done by binding the component's state property to your text. This can be done using combo boxes in the Binding Editor, which is an alternative easier way to add a binding.
So let's use the left combo box to select the myButton_Symbol button, and then the right combo box to select the state property, which will automatically create the binding:
myButton_Symbol.state
You should now see that for all of your defined states, the label will display the name of the state. You can click on the thumbnails to make sure the label is correct in each state, and then test your button to make sure it works correctly.
It does this by launching a live preview using the preview button in the top toolbar, with the button launching in a window where you can interact with it. So click on the button, which should now start in the default state, moving the mouse on and off should switch the state from standard to hover and back, and press the mouse button to move between hover and click. You should see the correct image and label in each state. If it works, congratulations! You have created your first proper qds component.
If that doesn't work, there are a few things to check:
Make sure the hover property on the mouse area is set to true in the base state and in all other custom states.
Carefully check your terms for spelling errors.
If all of this is correct, you should have a working button. Below is an example code if you want to double check if yours matches.
In the next part, we will return to Sketch and use this button to create a menu symbol consisting of several button instances, and then transfer everything back to Qt Design Studio for further development of the project.
For those wondering what the qml code looks like:
import QtQuick 2.8 Item { id: myButton_Symbol width: 211 height: 211 state: "default" Image { id: myButton_Symbol_default x: 0 y: 0 source: "assets/myButton_Symbol_default.png" } Image { id: myButton_Symbol_hover x: 0 y: 0 source: "assets/myButton_Symbol_hover.png" } Image { id: myButton_Symbol_pressed x: 0 y: 0 source: "assets/myButton_Symbol_pressed.png" } MouseArea { id: myButton_Symbol_MouseArea x: 0 y: 0 width: 211 height: 211 hoverEnabled: true } states: [ State { name: "default" when: !myButton_Symbol_MouseArea.containsMouse && !myButton_Symbol_MouseArea.pressed PropertyChanges { target: myButton_Symbol_hover visible: false } PropertyChanges { target: myButton_Symbol_pressed visible: false } }, State { name: "hover" when: myButton_Symbol_MouseArea.containsMouse && !myButton_Symbol_MouseArea.pressed PropertyChanges { target: myButton_Symbol_pressed visible: false } PropertyChanges { target: myButton_Symbol_default visible: false } }, State { name: "pressed" when: myButton_Symbol_MouseArea.pressed PropertyChanges { target: myButton_Symbol_default visible: false } PropertyChanges { target: myButton_Symbol_hover visible: false } } ] }