Jump to content

Plasma/QMLStyle: Difference between revisions

From KDE Community Wiki
Notmart (talk | contribs)
Sitter (talk | contribs)
https://mail.kde.org/pipermail/plasma-devel/2022-August/122683.html
 
(19 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Directory Structure ==
== Directory Structure ==
* QML files have the .qml suffix go into ui/
* QML files have the .qml suffix go into ui/ they use CamelCase in their filename.
* Javascript files have the .js suffix and go into code/
* Javascript files have the .js suffix and go into code/
* If and only if we are in a qml-only plasmoid, the javascript files should be imported with the following keyword:


== Whitespace ==
<syntaxhighlight lang="javascript">
For all Javascript code, follow the kdelibs code style. This means spaces around operators, if/else on same line as braces, four space indentation (''not'' tabs) etc.
  import "plasmapackage:/code/Foo.js" as Foo
</syntaxhighlight>
 
This way becomes easier to have device-specific qml files, while keeping a single copy of the javascript code. (And we really, really need to find a way to make this magic happen for generic packages.)
 
== JS Code Blocks ==
For all Javascript code blocks we loosely follow the framworks code style. This means spaces around operators, if/else on same line as braces, four space indentation (''not'' tabs), etc, see [[Policies/Frameworks_Coding_Style]]. The two major exceptions are that functions should have their opening brace on the same line as the declaration, and that we do not terminate lines with semicolons unless necessary. Existing code must not needlessly be hammered into compliance!


== Items ==
== Items ==
Line 25: Line 32:
     height:
     height:


     // Data models, including Plasma.DataSources
     // Signals
     model: { }
     signal somethingJustHappened


     // Properties of this item
     // Properties of this item
     property bool foo
     property bool foo
    // properties and other items that fit on a single line may do so
    property svg: PlasmaCore.Svg { imagePath: "path" }


     // Property aliases
     // Property aliases
     property alias bar : actual.property
     property alias bar: actual.property


     // Properties defined by Item class, except geometry properties
     // Properties defined by Item class, except geometry properties
     someProperty: 123
     someProperty: 123
    // Data models, including Plasma.DataSources
    model: { }


     // all on* change functions
     // all on* change functions
Line 44: Line 56:


     // all functions
     // all functions
     function fooFunction()
     function fooFunction() {
    { // open brace on its OWN line
         // code goes here; kdelibs style
         // code goes here; kdelibs style
     }
     }


     function barFunction()
     function barFunction() {
    {
         // note the newline between this function and the one above it
         // note the newline between this function and the one above it
     }
     }
Line 80: Line 90:
         id: secondComponent
         id: secondComponent
     }
     }
    // States
    states: [
        State {
            name: firstState
            // changes
        },
        State {
            name: secondState
        }
    ] // states
    // Transitions
    transitions: [
            Transition {
                from: "foo"
                to: "bar"
                // transition definition
            },
            Transition {
                from: "foo"
                to: "bar"
                // transition definition
            },
    ] // transitions


     // the "onCompleted" function goes last, as befits the name
     // the "onCompleted" function goes last, as befits the name
Line 88: Line 124:
} // topLevelItem: for items with considerable contents, comment the last } with what it matches
} // topLevelItem: for items with considerable contents, comment the last } with what it matches
</syntaxhighlight>
</syntaxhighlight>
== Best Practices ==
These are just notes for now, gathered from the discussion on the mailing list, to be turned into something more concrete once the dust settles. Things in this section are not requirements, but recommendations and preferred methods.
* Wrap each block of a QML Item definition with "//BEGIN: &lt;section&gt;" and "//END: &lt;section&gt;" to allow for code folding
* Geometry management, such as anchors, should usually be done where the item is used, not in the item definition (e.g. if there is a MyButton component that is used inside of MyFrame, MyButton anchors should be set in its actual use within MyFrame)
* The top level item in a QML file should have "id: root" for consistency
* Always try your plasmoid on touch platform as well, just export the variable on a terminal before running plasmoidviewer:
<syntaxhighlight lang="javascript">
PLASMA_PLATFORM=tablet:touch
</syntaxhighlight>
===Import Aliases===
Here is a list of common import aliases that should be used consistently for more easily readable coede:
* import org.kde.plasma.core 0.1 as PlasmaCore
* import org.kde.plasma.components 0.1 as PlasmaComponents
* import org.kde.plasma.extras 0.1 as PlasmaExtras
* import org.kde.metadatamodels 0.1 as MetadataModels
* import org.kde.runnermodel 0.1 as RunnerModels
=== Scrollbars ===
Don't use the ScrollBar component directly. When you need a Flickable, ListView, or GridView with scrollbars, embed it in PlasmaComponents.ScrollView instead.
<syntaxhighlight lang="javascript">
ScrollView {
    contentItem: ListView {
        model: ....
        /*All ListView code here*/
    }
}
</syntaxhighlight>
This gives several advantages:
* more memory efficient: it actually creates and instantiates the scrollbars only when needed (i.e. the contents are bigger than the container)
* Automatically sets the proper anchors of the scrollbars to the listview
* On the desktop it will create actual interactive scrollbars, positioned besides the ListView contents, reserving space for it
* On a touch interface it will create non interactive scroll indicators visible only when scrolling. They will be  an overlay on top of the ListView. no space will be reserved for them
=== Icon-only buttons ===
In general, '''strongly prefer showing text on all buttons'''. If an icon-only button is required due to limited space, or permitted because its icon is universally recognizable, implement it in the following way:
* give it both an icon and text
* set `display: [PlasmaComponents import].AbstractButton.IconOnly`,
* give it a `ToolTip` child item item that gets its text from its parent item
<syntaxhighlight lang="javascript">
PlasmaComponents3.Button {
    text: "Make it explode"
    icon.name: "edit-bomb
    display: PlasmaComponents3.AbstractButton.IconOnly
    PlasmaComponents3.ToolTip {
        text: parent.text
    }
</syntaxhighlight>
This gives several advantages:
* Qt automatically sets Accessible properties for screen readers
* Pointing device users see a tooltip that tells them what a button does, in case its icon is not 100% obvious.

Latest revision as of 12:28, 30 August 2022

Directory Structure

  • QML files have the .qml suffix go into ui/ they use CamelCase in their filename.
  • Javascript files have the .js suffix and go into code/
  • If and only if we are in a qml-only plasmoid, the javascript files should be imported with the following keyword:
  import "plasmapackage:/code/Foo.js" as Foo

This way becomes easier to have device-specific qml files, while keeping a single copy of the javascript code. (And we really, really need to find a way to make this magic happen for generic packages.)

JS Code Blocks

For all Javascript code blocks we loosely follow the framworks code style. This means spaces around operators, if/else on same line as braces, four space indentation (not tabs), etc, see Policies/Frameworks_Coding_Style. The two major exceptions are that functions should have their opening brace on the same line as the declaration, and that we do not terminate lines with semicolons unless necessary. Existing code must not needlessly be hammered into compliance!

Items

QML Item declarations follow this pattern:


Item { // brace on same line
    // line indentation
    id: topLevelItem // the id, if any, is the first line

    // Geometry properties
    // grouped propertes as anchors should be in this notation,
    // unless there is only one property
    anchors {
        fill: parent
        rightMargin: 6
    }
    width: 
    height:

    // Signals
    signal somethingJustHappened

    // Properties of this item
    property bool foo
    // properties and other items that fit on a single line may do so
    property svg: PlasmaCore.Svg { imagePath: "path" }

    // Property aliases
    property alias bar: actual.property

    // Properties defined by Item class, except geometry properties
    someProperty: 123

    // Data models, including Plasma.DataSources
    model: { }

    // all on* change functions
    onFooChanged: fooFunction()
    onSomePropertyChanged: { // brace on same line as these use the "name:" style
        fooFunction(); // in blocks, kdelibs style, including semicolons at EOL
    }

    // all functions
    function fooFunction() {
        // code goes here; kdelibs style
    }

    function barFunction() {
        // note the newline between this function and the one above it
    }

    // all non-UI sub-items, such as timers
    Timer {
        id: timer
        interval: 100
        onTriggered: fooFunction()
    }

    // all UI sub-items
    Item {
        anchors.fill: parent
    }

    Item {
        // note the newline between items
        id: secondItem
    }

    // all components
    Component {
        id: firstComponent
    }

    Component {
        // note the newline between components
        id: secondComponent
    }

    // States
    states: [
        State {
            name: firstState
            // changes
        },

        State {
            name: secondState
        }
    ] // states

    // Transitions
    transitions: [
            Transition {
                from: "foo"
                to: "bar"
                // transition definition
            },
            Transition {
                from: "foo"
                to: "bar"
                // transition definition
            },
    ] // transitions

    // the "onCompleted" function goes last, as befits the name
    Component.onCompleted: {
        // if multi-line block, brace goes on same line due to "name:" style of declaration
        fooFunction();
    }
} // topLevelItem: for items with considerable contents, comment the last } with what it matches

Best Practices

These are just notes for now, gathered from the discussion on the mailing list, to be turned into something more concrete once the dust settles. Things in this section are not requirements, but recommendations and preferred methods.

  • Wrap each block of a QML Item definition with "//BEGIN: <section>" and "//END: <section>" to allow for code folding
  • Geometry management, such as anchors, should usually be done where the item is used, not in the item definition (e.g. if there is a MyButton component that is used inside of MyFrame, MyButton anchors should be set in its actual use within MyFrame)
  • The top level item in a QML file should have "id: root" for consistency
  • Always try your plasmoid on touch platform as well, just export the variable on a terminal before running plasmoidviewer:
PLASMA_PLATFORM=tablet:touch

Import Aliases

Here is a list of common import aliases that should be used consistently for more easily readable coede:

  • import org.kde.plasma.core 0.1 as PlasmaCore
  • import org.kde.plasma.components 0.1 as PlasmaComponents
  • import org.kde.plasma.extras 0.1 as PlasmaExtras
  • import org.kde.metadatamodels 0.1 as MetadataModels
  • import org.kde.runnermodel 0.1 as RunnerModels

Scrollbars

Don't use the ScrollBar component directly. When you need a Flickable, ListView, or GridView with scrollbars, embed it in PlasmaComponents.ScrollView instead.

ScrollView {
    contentItem: ListView {
        model: ....
        /*All ListView code here*/
    }
}

This gives several advantages:

  • more memory efficient: it actually creates and instantiates the scrollbars only when needed (i.e. the contents are bigger than the container)
  • Automatically sets the proper anchors of the scrollbars to the listview
  • On the desktop it will create actual interactive scrollbars, positioned besides the ListView contents, reserving space for it
  • On a touch interface it will create non interactive scroll indicators visible only when scrolling. They will be an overlay on top of the ListView. no space will be reserved for them


Icon-only buttons

In general, strongly prefer showing text on all buttons. If an icon-only button is required due to limited space, or permitted because its icon is universally recognizable, implement it in the following way:

  • give it both an icon and text
  • set `display: [PlasmaComponents import].AbstractButton.IconOnly`,
  • give it a `ToolTip` child item item that gets its text from its parent item


PlasmaComponents3.Button {
    text: "Make it explode"
    icon.name: "edit-bomb
    display: PlasmaComponents3.AbstractButton.IconOnly

    PlasmaComponents3.ToolTip {
        text: parent.text
    }

This gives several advantages:

  • Qt automatically sets Accessible properties for screen readers
  • Pointing device users see a tooltip that tells them what a button does, in case its icon is not 100% obvious.