Qt Design Studio - Sketch Bridge Tutorial Part 1

Welcome to this Qt Design Studio Sketch Bridge Tutorial, to follow along with this you will need the commercial Qt Design Studio 1.5 Package and Sketch Bridge, macOS and Sketch installed (I'm using 66.1).

With this tutorial I want to show you how to build up a sketch project that creates a clean export and import into Qt Design Studio (which i will refer to as qds for the rest of the tutorial), uses symbols and instances for proper componentization and goes back and forth from Sketch to qds in iterative loops building up a more complex scene from simple building blocks. I'll also cover some of the most common issues i come across from other users and the tips and tricks I've developed while working with the Bridge Plugin.

I think it's important before we start to clarify that although Sketch allows designers to achieve their design concepts in a flexible and open ended manner, in order to have a pixel perfect design built around developer friendly components in qds, it is very important to structure and prepare your project in a certain manner, and although that is not overly complex to learn it does take some time and knowledge to do it well. My hope is this tutorial will provide you with the necessary experience to bring your designs much closer to this point. With this caveat out the way let's dive right in and start designing.

Part 1.1 - A humble button.

For the first step we will start by designing a single button which we will use as the base for our project, we are going to start this project in Sketch so open up the Sketch app and create a new blank file.

First, let's create the default button background by dragging out a rectangle, we want to make sure this is the correct size for our button, then make it into a symbol, after we’ve created this symbol let’s delete the instance here and work directly on the master symbol.

 

addRectangle

 

We can then add some nice rounded corners, create a gradient and add a shadow and highlight to the button, before we start with the visual design though i want to share the first tips. To make seeing our design that bit easier we can drag the artboard out a bit to give us space to see our shadows and add a working background for the symbol, let’s pick a color we don’t plan on using in our design that gives us plenty of contrast to our design colors, like this pale pink here.

Also, while we’re here let’s make sure we deselect these two options, we don’t want this background to be exported or to appear when we’re using the symbols later. With our design space sorted out lets round out these corners, add a simple gradient and add some shadows.

 

designButton

 

Now we have the default background state for the button let’s create the other two states we want to use for this tutorial, a hover and pressed state.

We can do this by duplicating our original rectangle, renaming the layers and then putting them side by side for now so we can see the design changes in parallel, to make this a bit easier we can drag the symbol width out so we can fit our buttons side by side, we will be resizing this after we are done with the design.

 

resizeArtboard

 

Now we have a good setup let’s change the hover and pressed versions of our button background by subtly adjusting the gradient colors until we have three versions of the button, although there are no real rules in UX for this that I think are relevant, as long as these states are consistent and work with your overall UI design, I’m going to go with the logic that the hover state is a little brighter than the normal and the pressed state is a little darker.

During this step i also name my layers clearly, I'll be using these names for the qml ids as well but we'll cover that in more detail shortly.

 

addVarients

 

With our three backgrounds created lets put them back on top of each other, as we want them to be like this for the export, and for now we’ll add one more item that we want to be the top layer, a hotspot, which on import into design studio will be automatically transformed into a mouse area.

It’s important when we add the hotspot to set the target to none, and we want to make sure this is the top layer of our button to properly capture our mouse events.

Also we want to make sure here that the artboard with our button on it is large enough to fit all the shadow effects we have added without clipping but brought in close enough to the edges to make sure our item’s bounding box is not too big, nice and tight to the edge of the effects works best.

With that done we have our simple button ready to prepare the settings in the Qt Bridge Plugin.

 

addMouseArea

 

The most common errors I see from people using the sketch bridge come from having duplicate id’s in your project, although the importer in qds is capable of detecting and remaining qml ids, it is still advisable to manually check all your qml ids to make sure they are unique and meaningful.

One of the issues with this is that once you rename the qml id for a symbol, each instance of that symbol will have the duplicate id. My advice with this is to use a naming scheme that differentiates between the symbol and the instances of that symbol.

For example here we will make sure our button symbol has the id myButton_Symbol, and the different layers have this as a prefix with the states appended - so myButton_Symbol_default , etc. And because for sure we will have more mouse areas later in this project lets rename the hotspot to myButton_Symbol_hotspot, when we use the button as instances in our screen we will make sure to give these instances a unique id that relates to that screen, also import to note that qml ids can only use a subset of possible characters, for this tutorial we are going to use underscores to separate our names as we know this format will work.

As we rename these ids we want to make sure all our layers are set to Child, we want separate assets for each layer so we don’t want to merge anything at this point, later on when we design some backgrounds we’ll use merging to help minimize the amount of individual assets in our design but for now let’s set all these qml id's correctly and make sure the export type is set to child for each of our button designs.

In my experience it really makes sense to also name the layers in exactly the same way as your qml ids, this makes it a lot easier to find artwork later, especially as these files can grow very large and complicated as they approach the level of a complete UI project.

 

makeIds

 

In the Qt Bridge plugin we also want to set up a few basic things first in the settings page, these are the global settings we’ll use for the rest of the project so we should only have to do this once. First we need to choose an export path, let’s create a project folder where later we can create the Design Studio project and in that we’ll create an export folder and select it as the location we want our assets and metadata to go in. To create the folders you need to use the shortcut keys shift+cmd+n.

Now let’s leave all the rest of the settings as default, we’ll use pngs for this tutorial, at this point we’re ready to go back to the home panel of our Bridge Plugin and do our first export.

 

settingsGlobal

 

With all our qml ids and export types set correctly we are ready to export our component and bring it into Design Studio, where we’ll add a few states and test our button before we carry on with our design in Sketch.

So let’s just hit export and then head over to Design Studio.

 

firstExport

 

Part 1.2 - My First Import

To start with in Design Studio we can create an empty project to import our Sketch design into, so lets go to the home page and click on the new project button, this will launch the wizard and we want to choose a default empty project.

We can select our project folder as the location for this project and then the rest of the settings can be left as default. After the wizard finishes we will automatically be switched to the default ui.qml file that comes with the project, to import our sketch design we can now go to the assets panel, add new assets and then navigate to the folder we exported our designs into from Sketch.

 

createProject

 

The file we need is the .metadata file, this will only be an option if we have the 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 we can select this file to launch the importer panel.

 

chooseMetaData

 

For this first import we are going to use the default settings, we want to export the metadata and assets while leaving merge disabled, but as we want to start working on things in Design Studio right away all subsequent imports we will leave the merge feature enabled so we don’t accidentally overwrite any of our work in qds.

 

importMetadata

 

After the import is complete we can close the importer for now and take a look at our imported files, these will be located in a folder named the same as our export folder. if we navigate into this folder we will see our items, at the moment we only have the single button component so let’s go into this file and check our export is correct.

 

checkImport

 

We should have in this file our three different button layers that we want to use for the different states, plus the mouse area. By default sketch attaches an empty layer in the export, here it’s called asset and the first thing we need to do now is delete this layer as we don’t need it in our component.

After that we need to set the mouse area to hover enabled, as we do want to use the hover state. Lets do that straight away by clicking on the mouse area in the navigator and going into the property panel and selecting hover enabled for our mouse area.

As we are currently in the Base State, the only state we have for now, this will be applied to every state we add from now on. While we’re here we can add the states we need. We should always try and use explicit user created states for our app.

The base state is useful for making changes to our design we want to be applied to every other state, if we are currently in a user created state any changes we make here will be only applied to locally to this state.

 

enableHover

 

So let’s create the three states we need for this button. We’ll add a state called default, one called hovered and one called pressed.

Now we have these states we can change the system default state to our user created 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 we see the button in.

 

createStates

 

We need to add some logic to these states to connect them to our mouse area, we will do this in the next step by using the when conditions option also found here in the action icon. For now though let’s prepare our artwork in each of our respective states, and as this is the first import let’s start by simply just showing the correct corresponding asset layer in each state.

The easiest way to do this is just to make the corresponding layer visible and all other layers invisible in each state. So first let’s go to our default state by clicking on the correct thumbnail in the state panel, with this state selected we can go to our images in the navigator and select each of our assets one by one and toggle the visibility for each layer correctly, so for the default state we want the default layer visible and the hover and pressed set to invisible.

Once these are set up we can move to the hover state and repeat this process, making the hover artwork the only visible layer. Finally we report this one more time for the pressed layer by selecting the pressed state and repeating the process of making only the pressed layer visible.

 

setArtworkNew

 

Part 1.3 - When Conditions

Now we should be ready to add the logic to these states to connect them to our mouse area, but first we can quickly check that everything is set up correctly by clicking between the state thumbnails, if everything is correct you should only see the appropriate layer in the form editor. Assuming this works fine let’s go and add the logic to the mouse area / states, we will do this by using “When Conditions” - essentially this means when the defined conditions are met, this state will be active. When conditions can get complicated pretty fast and often you need to add more than one condition to properly model any meaningful behavior, you’ll see here that in two of our states we need to assess two separate conditions to be sure that we have the correct behavior. Also we will need to be careful to check if things are both true and not true.

In case you are not familiar with When Conditions and the syntax they use i wanted to introduce the concept a bit. When conditions work with something called logical operators, which you can read a bit more about here if you want to - https://doc-snapshots.qt.io/qtdesignstudio-4.13/quick-states.html#summary-of-logical-operators

In the context of qml and qds it means we can evaluate the truthfulness of several items properties and move the UI to the state in which these conditions apply. We can look at whether something is true, not true, bigger than, equal to. etc. We can also compare multiple items for their truthfulness with operators such as AND or OR.

When 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 these conditions to apply to your state. So it's important to make sure that each condition can only be true in one state.

If this all sound a bit complicated at the moment just try and follow along step by step, hopefully by the end you will have a better idea about the usefulness and power of when conditions.

Lets start by adding the default condition, we want to make sure that our default button is shown when there is no mouse hovering over the button and the mouse button is not pressed. Both of these need to be a “not” condition and both need to be true, so the mouse is not hovered and the button is not pressed is a true statement.

We can define a not condition by adding an exclamation mark before our conditions and to assess both conditions we will use a double ampersand symbol between them. So let’s go ahead and do this right away. Start by selecting the default state in the state thumbnail preview panel, then click on the action icon and select the “add when condition” option, this will launch the binding editor and we can add our condition here.

It’s very important that the condition is properly written so we will use the autocomplete function to make sure there are no spelling mistakes.

Default State Condition

So lets start by typing a single ! - followed by the first three letters to of the item we want to check, in this case it’s the mouse area which is called myButton_Symbol_mouseArea after the first three characters are typed we should have the list of all possible completions so let’s select the mouse area from the list. In this first condition we need to check a property of the mouse area, called containsMouse

This will tell us if a user is hovered over the button. So after we have the mouse area here in the binding editor we can add a full stop and then go through the list and look for the contains mouse property and select that.

Now we have our first condition, which checks that the user is not currently hovered over the button, and this reads as:

      !myButton_Symbol_mouseArea.containsMouse

We also need to evaluate a second condition to make sure the user is also not pressing the button as well, and we can set the condition to evaluate both things by adding the two ampersands between them, now we can repeat the first step, as we want to check this mouse area again.

So lets type another ! Followed by the first letters of the mouse area again, which brings up the autocomplete list. Select the mouse area again and add the full stop to get the property list, this time the property we are interested in is the pressed property, which tells us if this button currently contains a pressed mouse.

So our final condition for this first button should look like this:

    !myButton_Symbol_mouseArea.containsMouse && !myButton_mouseArea.pressed

 

defaultWhen2

 

By itself this when condition doesn’t actually do anything, it simply ensures that after a hover or press event has passed the button will reset to the default state, as our button always starts in this default state for now we won’t see any change, so lets add the next conditions and then we can test our button.

 

Hover State Condition

Our second condition should be for our hover button, so lets make sure we go into the correct state first, by clicking on the hover state thumbnail in the thumbnail panel and then once we have the correct state in the form editor we can add the next condition, in this case we want to be in the hover state when the mouse is over the mouse area but also not pressed (as that would be our pressed state.)

So, let’s launch the binding editor again for this state, by clicking on the action icon and selecting add when condition. With the binding editor open we can use the autocomplete function to make sure our condition is spelt correctly, the first condition we want is the one that checks if a mouse is over the mouse area, so again the id we’re looking for is myButton_Symbol_mouseArea

When we type the first three letters and we will get the list of items available in the file, we can scroll down and select our mouse area. once that’s been selected we add the full stop to the end and this launches the list for the available properties to evaluate.

The property we want is called containsMouse, so we can either start typing to narrow the list or scroll down and select it, note that this time we didn’t add the exclamation point this time, because we want to check if the condition is true and the exclamation checks if it is false. So we have our first condition for this state:

    myButton_Symbol_mouseArea.containsMouse

We also need to make sure that the hover state is not shown when we press the button too, as of course to press the button the mouse must be in the mouse area, in order to differentiate between these two events we will add another condition to the state.

With the binding editor open we can add the && and then start adding another condition, in this case it’s the same second condition as our previous default state, starting with an exclamation point we can again type the !myButton_mouseArea.pressed condition again, checking that the button is not pressed.

So now we should have our full condition for this state and it should look like this:

    myButton_mouseArea.containsMouse && !myButton_mouseArea.pressed

This one checks if the mouse is hovered over the button but not yet pressed, as you can see the only difference is the first exclamation point, which is why we have to carefully check our when conditions to make sure everything is properly formatted.

 

hoverWhen2

 

Lets add the final condition and then we can test our button and I’ll show you a tip that I use when I’m debugging more complex states and conditions.

 

Pressed State Condition

So our final condition is for the pressed state, this one is simpler as the only thing we want to evaluate is whether the button is pressed or not. Let’s go to the pressed state, again by clicking on the thumbnail in the state panel, launch the binding editor via the action icon and this time we want the simple condition: 

    myButton_mouseArea.pressed

 

pressedWhen2

 

Now all our states have conditions we could test this button by running the live preview, in our case it’s pretty simple to see if the correct states are activated at the right time, however the more complex your design and more subtle the changes, the more difficult it becomes just by looking at the preview. So lets use a little trick now to make sure we are in the correct state.

First, we need to make sure we are in the base state for this, we want to add a label that shows us the current state we are in so the label and binding we will use must be in every state and the correct way to handle this is to go to the base state by clicking on it in the state panel, then go to the Qt-Quick Basics section in the QML Types library and drag a text item into the form editor on top of our button.

We’re going to delete this again once we are sure the states are correct so it doesn’t matter too much how it looks, just as long as it’s readable. So if you need to adjust the size and color so the label is readable go ahead and do so now.

Once the label is ready we’re going to add a binding, so let’s make sure the text item is selected and then we can go to the property panel and find the text property for this item.

Now we hover over it to reveal the action icon and launch the binding editor by selecting set binding, what were going to do here is use this text label to show the current state of our component, which we can do by binding the component’s state property to our text, we can do this using the combo boxes in the binding editor, an alternative simpler way to add a binding.

So lets use the left combo box to select the button myButton_Symbol and then the right combo box to select the state property which will automatically create the binding:

     myButton_Symbol.state

 

addLabel2

 

We should see now that for all of our defined states the label will show the state name, we can click on the thumbnails to make sure the label is correct in each state, then we can test our button to make sure that it works correctly.

We do that by launching the live preview with the live preview button in the top toolbar, this will launch our button in a window where we can interact with it. So click the button, which should now start in the default state, moving the mouse over and off should switch the state from default to hovered and back again, and press the mouse button to move between hover and pressed. You should see the correct artwork and label in each state. If that works, congratulations! You have now created your first proper qds Component : )

 

livePreview2

 

If it’s not working for you there is a couple of things to check.

1. Make sure the Mouse Area hover property is set to true in the base state and all other user created states.

2. Check your when conditions carefully for spelling errors.

If all of these are correct you should have a working button, I’ll post the code for the example here if you want to double check that yours is the same.

In the next part we are going to go back into Sketch and use this button to build up a menu symbol consisting of several instances of our button and then bring that back into Qt Design Studio to further develop our project.

All the project files can be found here:

https://www.dropbox.com/sh/onycru7p2s3h1fp/AADAl8thdX8nB8SWsK9Czmnea?dl=0

And for those interested in how the qml for this looks, here's the code:


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
            }
        }
    ]
}

 


Blog Topics:

Comments