From 8d03def677ff09f43322f4d38b6d56e6ebf0e1f9 Mon Sep 17 00:00:00 2001 From: Sami Nurmenniemi Date: Wed, 25 Oct 2017 13:17:22 +0300 Subject: Add loader animation to the application startup Task-number: QTBUG-60089 Change-Id: Iadfd5898098a2aa5d6b717263275f929a08d24f0 Reviewed-by: Kari Oikarinen --- qml.qrc | 2 + qml/BootScreen.qml | 3 +- qml/DemoInfoPopup.qml | 24 +--- qml/DetailView.qml | 21 +--- qml/FlickSlider.qml | 56 +++++++++ qml/LaunchScreen.qml | 24 +--- qml/Main.qml | 313 ++++------------------------------------------- qml/MainWindow.qml | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 432 insertions(+), 341 deletions(-) create mode 100644 qml/FlickSlider.qml create mode 100644 qml/MainWindow.qml diff --git a/qml.qrc b/qml.qrc index a2b22b8..a18b658 100644 --- a/qml.qrc +++ b/qml.qrc @@ -21,5 +21,7 @@ qml/settings.xml qml/ImageTextDelegate.qml qml/BusyIndicator.qml + qml/MainWindow.qml + qml/FlickSlider.qml diff --git a/qml/BootScreen.qml b/qml/BootScreen.qml index cab6838..1318495 100644 --- a/qml/BootScreen.qml +++ b/qml/BootScreen.qml @@ -32,6 +32,7 @@ Rectangle { id: bootScreen color: viewSettings.backgroundColor opacity: 0.95 + property string applicationName: "" Column { anchors.centerIn: parent @@ -54,7 +55,7 @@ Rectangle { font.pixelSize: bootScreen.height * 0.05 font.styleName: "Bold" color: "white" - text: engine.applicationName + text: applicationName } } } diff --git a/qml/DemoInfoPopup.qml b/qml/DemoInfoPopup.qml index 5cc64aa..11bc826 100644 --- a/qml/DemoInfoPopup.qml +++ b/qml/DemoInfoPopup.qml @@ -121,26 +121,12 @@ Rectangle { width: frame.width * 0.9 wrapMode: Text.WordWrap } + } - ScrollBar.vertical: ScrollBar { - parent: flickable.parent - anchors.top: flickable.top - anchors.bottom: flickable.bottom - anchors.right: parent.right - anchors.rightMargin: viewSettings.pageMargin * 0.2 - anchors.topMargin: viewSettings.pageMargin * 0.5 - width: viewSettings.pageMargin * 0.6 - size: 0.3 - position: 0.2 - active: true - orientation: Qt.Vertical - - contentItem: Rectangle { - implicitWidth: root.margin * 0.3 - implicitHeight: root.height * 0.1 - color: viewSettings.scrollBarColor - } - } + FlickSlider { + flickItem: flickable + anchors.right: frame.right + width: viewSettings.pageMargin * 0.5 } } } diff --git a/qml/DetailView.qml b/qml/DetailView.qml index e390267..231be57 100644 --- a/qml/DetailView.qml +++ b/qml/DetailView.qml @@ -59,7 +59,6 @@ Item { anchors.rightMargin: viewSettings.pageMargin contentHeight: descriptionHolder.height contentWidth: width - clip: true Column { id: descriptionHolder @@ -147,20 +146,12 @@ Item { thumbList.currentIndex = index } } - ScrollBar.horizontal: ScrollBar{ - parent: thumbList.parent - anchors.bottom: parent.bottom - anchors.bottomMargin: viewSettings.pageMargin * 0.3 - anchors.left: parent.left - anchors.leftMargin: viewSettings.pageMargin - anchors.right: parent.right - anchors.rightMargin: viewSettings.pageMargin - size: 0.3 - contentItem: Rectangle{ - color: viewSettings.scrollBarColor - implicitHeight: thumbList.height * 0.03 - } - } + } + + FlickSlider { + flickItem: thumbList + anchors.bottom: listHolder.bottom + isVertical: false } } } diff --git a/qml/FlickSlider.qml b/qml/FlickSlider.qml new file mode 100644 index 0000000..5db0096 --- /dev/null +++ b/qml/FlickSlider.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Device Creation. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +// QTBUG-50992, using customized ScrollBar inside Loader does not work, implement with rectangle +Rectangle { + property var flickItem + property bool isVertical: true + + color: viewSettings.scrollBarColor + anchors.margins: viewSettings.pageMargin * 0.375 + height: isVertical ? flickItem.height * 0.3 : viewSettings.pageMargin * 0.25 + width: isVertical ? viewSettings.pageMargin * 0.25 : flickItem.width * 0.2 + + // How much (0...1) the view area covers from the content height/width + property real visibilityRatio: isVertical ? flickItem.visibleArea.heightRatio : flickItem.visibleArea.widthRatio + + // How many pixels the slider can move in the view area + property real moveSize: isVertical ? flickItem.height - height : flickItem.width - width + + // Coefficient between x/y position (0...1) and slider move pixels + property real slideCoefficient: (visibilityRatio < 1) ? (moveSize)/(1 - visibilityRatio) : 1 + + // Slider is only visible if view area is smaller than the content + visible: (visibilityRatio < 1) + + // Actual calculation of the slider position + x: isVertical ? 0 : slideCoefficient * flickItem.visibleArea.xPosition + flickItem.x + y: isVertical ? slideCoefficient * flickItem.visibleArea.yPosition + flickItem.y : 0 +} diff --git a/qml/LaunchScreen.qml b/qml/LaunchScreen.qml index a83d524..7d369b7 100644 --- a/qml/LaunchScreen.qml +++ b/qml/LaunchScreen.qml @@ -27,7 +27,7 @@ ** ****************************************************************************/ import QtQuick 2.0 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.3 Item { id: gridroot @@ -51,24 +51,10 @@ Item { width: grid.cellWidth onClicked: root.launchApplication(sLocation, sMainFile, sName, sDescription) } - ScrollBar.vertical: ScrollBar { - parent: gridroot - anchors.top: grid.top - anchors.bottom: grid.bottom - anchors.right: parent.right - anchors.rightMargin: viewSettings.pageMargin * 0.25 - anchors.topMargin: viewSettings.pageMargin * 0.5 - width: viewSettings.pageMargin * 0.5 - size: 0.3 - position: 0.2 - active: true - orientation: Qt.Vertical + } - contentItem: Rectangle { - implicitWidth: viewSettings.pageMargin * 0.25 - implicitHeight: root.height * 0.1 - color: viewSettings.scrollBarColor - } - } + FlickSlider { + flickItem: grid + anchors.right: parent.right } } diff --git a/qml/Main.qml b/qml/Main.qml index 23d365c..99c6e4d 100644 --- a/qml/Main.qml +++ b/qml/Main.qml @@ -26,20 +26,16 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -import QtQuick 2.4 +import QtQml 2.2 +import QtQuick 2.6 import QtQuick.Window 2.2 -import QtQuick.VirtualKeyboard 2.1 -import QtDeviceUtilities.SettingsUI 1.0 -import com.qtcompany.B2QtLauncher 1.0 Window { id: window visible: true - //width: qpa_platform === "wayland" ? 800 : Screen.desktopAvailableWidth - //height: qpa_platform === "wayland" ? 600 : Screen.desktopAvailableHeight - width: 800 - height: 600 + width: Screen.desktopAvailableWidth + height: Screen.desktopAvailableHeight color: viewSettings.backgroundColor property alias appFont: viewSettings.appFont @@ -48,295 +44,38 @@ Window { id: viewSettings } - Component { - id: emptyComponent - Item { - objectName: "empty" + Loader { + id: mainWindowLoader + anchors.fill: parent + active: false + source: "qrc:///qml/MainWindow.qml" + asynchronous: true + onLoaded: { + item.visible = true; + splashScreenLoader.item.visible = false; + splashScreenLoader.source = ""; } } - Component { - id: gridComponent - LaunchScreen { - id: launchScreen - } - } - - Component { - id: detailComponent - DetailView { - id: detailView - } - } - - Component { - id: settingsUIComponent - SettingsUI { - id: settingsUI - objectName: "settingsView" - anchors.fill: parent - model: "settings.xml" - margin: viewSettings.pageMargin - onClosed: root.closeApplication() - } - } - - LauncherApplicationsModel { - id: applicationsModel - onReady: engine.markApplicationsModelReady(); - Component.onCompleted: { - //Set the directory to parse for apps - initialize(applicationSettings.appsRoot); - } - } - - LauncherEngine { - id: engine - fpsEnabled: applicationSettings.isShowFPSEnabled - } - - Item { - id: root - anchors.centerIn: window.contentItem - property bool portraitMode: Screen.desktopAvailableHeight > Screen.desktopAvailableWidth ? true : false - rotation: portraitMode ? 90 : 0 - width: portraitMode ? window.height : window.width - height: portraitMode ? window.width : window.height - - function closeApplication() - { - engine.closeApplication() - applicationLoader.setSource("") - applicationLoader.sourceComponent = emptyComponent - } - - function launchApplication(loc, mainFile, name, desc) - { - engine.launchApplication(loc, mainFile, name, desc) - applicationLoader.source = engine.applicationMain - } - - function launchSettings() - { - engine.state = "app-launching" - applicationLoader.sourceComponent = settingsUIComponent - } - - Background {} - - Header { - id: header - enabled: engine.state == "running" - onMenuClicked: root.launchSettings() - } - - Loader { - id: contentLoader + Loader { + id: splashScreenLoader + anchors.fill: parent + sourceComponent: Item { anchors.fill: parent - anchors.topMargin: header.height + viewSettings.pageMargin - sourceComponent: globalSettings.gridSelected ? gridComponent : detailComponent - enabled: engine.state != "settings" - } - - states: [ - State { - name: "running" - PropertyChanges { target: applicationLoader; opacity: 0; } - PropertyChanges { target: contentLoader; opacity: 1 } - PropertyChanges { target: header; opacity: 1 } - PropertyChanges { target: bootScreenLoader; opacity: 0 } - }, - State { - name: "app-launching" - PropertyChanges { target: header; opacity: 0 } - PropertyChanges { target: bootScreenLoader; opacity: 1 } - }, - State { - name: "app-running" - PropertyChanges { target: applicationLoader; opacity: 1 } - PropertyChanges { target: contentLoader; opacity: 0 } - PropertyChanges { target: header; opacity: 0 } - PropertyChanges { target: bootScreenLoader; opacity: 0 } - }, - State { - name: "settings" - PropertyChanges { target: applicationLoader; opacity: 1 } - PropertyChanges { target: contentLoader; opacity: 1 } - PropertyChanges { target: header; opacity: 0 } - PropertyChanges { target: bootScreenLoader; opacity: 0 } - }, - State { - name: "app-closing" - PropertyChanges { target: applicationLoader; opacity: 0; source: "" } - PropertyChanges { target: contentLoader; opacity: 0 } - PropertyChanges { target: header; opacity: 0 } - PropertyChanges { target: bootScreenLoader; opacity: 0 } - } - ] - - state: engine.state - - Timer { - id: failedAppLaunchTrigger; - interval: viewSettings.stateDelay; - running: false - repeat: false - onTriggered: root.closeApplication() - } - - Loader { - id: applicationLoader - opacity: 0; - visible: opacity > 0.1 - anchors.fill: parent - asynchronous: true; - onStatusChanged: { - if (status == Loader.Error) - failedAppLaunchTrigger.start(); - } - onLoaded: { - if (applicationLoader.item.objectName == "settingsView") - engine.state = "settings" - else if (applicationLoader.item.objectName !== "empty") - engine.state = "app-running"; - } - - DemoHeader { - id: demoHeader - onInfoClicked: demoInfoPopup.open() - onCloseClicked: demoClosePopup.open() - } - } - - Loader { - id: bootScreenLoader - visible: opacity > 0 - anchors.fill: parent - sourceComponent: BootScreen {} - } - - DemoClosePopup { - id: demoClosePopup - visible: false - } - - DemoInfoPopup { - id: demoInfoPopup - visible: false - } - - /* Handwriting input panel for full screen handwriting input. - - This component is an optional add-on for the InputPanel component, that - is, its use does not affect the operation of the InputPanel component, - but it also can not be used as a standalone component. - - The handwriting input panel is positioned to cover the entire area of - application. The panel itself is transparent, but once it is active the - user can draw handwriting on it. - */ - HandwritingInputPanel { - z: 79 - id: handwritingInputPanel - anchors.fill: parent - inputPanel: inputPanel - Rectangle { - z: -1 + Image { anchors.fill: parent - color: "black" - opacity: 0.10 - } - } - - /* Container area for the handwriting mode button. - - Handwriting mode button can be moved freely within the container area. - In this example, a single click changes the handwriting mode and a - double-click changes the availability of the full screen handwriting input. - */ - Item { - z: 89 - visible: handwritingInputPanel.enabled && Qt.inputMethod.visible - anchors { left: parent.left; top: parent.top; right: parent.right; bottom: inputPanel.top; } - HandwritingModeButton { - id: handwritingModeButton - anchors.top: parent.top - anchors.right: parent.right - anchors.margins: 10 - floating: true - flipable: true - width: 76 - height: width - state: handwritingInputPanel.state - onClicked: handwritingInputPanel.active = !handwritingInputPanel.active - onDoubleClicked: handwritingInputPanel.available = !handwritingInputPanel.available + source: "images/backImg.jpg" + fillMode: Image.PreserveAspectCrop } - onVisibleChanged: console.log("hw visible: "+visible) - Component.onCompleted: console.log("input visible " +visible) - } - /* Keyboard input panel. - The keyboard is anchored to the bottom of the application. - */ - InputPanel { - id: inputPanel - z: 99 - y: root.height - anchors.left: root.left - anchors.right: root.right - visible: y < root.height - - states: State { - name: "visible" - /* The visibility of the InputPanel can be bound to the Qt.inputMethod.visible property, - but then the handwriting input panel and the keyboard input panel can be visible - at the same time. Here the visibility is bound to InputPanel.active property instead, - which allows the handwriting panel to control the visibility when necessary. - */ - when: inputPanel.active - PropertyChanges { - target: inputPanel - y: root.height - inputPanel.height - } - } - transitions: Transition { - from: "" - to: "visible" - reversible: true - ParallelAnimation { - NumberAnimation { - properties: "y" - duration: 250 - easing.type: Easing.InOutQuad - } - } + BootScreen { + anchors.fill: parent + applicationName: qsTr("Qt Launcher") } - AutoScroller {} } - - Item { - id: fps - opacity: engine.fpsEnabled ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 500 } } - - anchors.bottom: parent.bottom; - anchors.left: parent.left; - width: fpsLabel.width - height: fpsLabel.height - - Rectangle { - color: "black" - opacity: 0.5 - anchors.fill: fpsLabel - } - - Text { - id: fpsLabel; - color: "white" - text: "FPS: " + engine.fps - font.pixelSize: engine.sensibleButtonSize() * 0.2 - } + onLoaded: { + mainWindowLoader.active = true; } } } diff --git a/qml/MainWindow.qml b/qml/MainWindow.qml new file mode 100644 index 0000000..aa1e19c --- /dev/null +++ b/qml/MainWindow.qml @@ -0,0 +1,330 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Device Creation. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.4 +import QtQuick.Window 2.2 +import QtQuick.VirtualKeyboard 2.1 +import QtDeviceUtilities.SettingsUI 1.0 +import com.qtcompany.B2QtLauncher 1.0 + +Item { + anchors.fill: parent + Component { + id: emptyComponent + Item { + objectName: "empty" + } + } + + Component { + id: gridComponent + LaunchScreen { + id: launchScreen + } + } + + Component { + id: detailComponent + DetailView { + id: detailView + } + } + + Component { + id: settingsUIComponent + SettingsUI { + id: settingsUI + objectName: "settingsView" + anchors.fill: parent + model: "settings.xml" + margin: viewSettings.pageMargin + onClosed: root.closeApplication() + } + } + + LauncherApplicationsModel { + id: applicationsModel + onReady: engine.markApplicationsModelReady(); + Component.onCompleted: { + //Set the directory to parse for apps + initialize(applicationSettings.appsRoot); + } + } + + LauncherEngine { + id: engine + fpsEnabled: applicationSettings.isShowFPSEnabled + } + + Item { + id: root + anchors.centerIn: parent + property bool portraitMode: Screen.desktopAvailableHeight > Screen.desktopAvailableWidth ? true : false + rotation: portraitMode ? 90 : 0 + width: portraitMode ? window.height : window.width + height: portraitMode ? window.width : window.height + + function closeApplication() + { + engine.closeApplication() + applicationLoader.setSource("") + applicationLoader.sourceComponent = emptyComponent + } + + function launchApplication(loc, mainFile, name, desc) + { + engine.launchApplication(loc, mainFile, name, desc) + applicationLoader.source = engine.applicationMain + } + + function launchSettings() + { + engine.state = "app-launching" + applicationLoader.sourceComponent = settingsUIComponent + } + + Background {} + + Header { + id: header + enabled: engine.state == "running" + onMenuClicked: root.launchSettings() + } + + Loader { + id: contentLoader + anchors.fill: parent + anchors.topMargin: header.height + viewSettings.pageMargin + sourceComponent: globalSettings.gridSelected ? gridComponent : detailComponent + enabled: engine.state != "settings" + } + + states: [ + State { + name: "running" + PropertyChanges { target: applicationLoader; opacity: 0; } + PropertyChanges { target: contentLoader; opacity: 1 } + PropertyChanges { target: header; opacity: 1 } + PropertyChanges { target: bootScreenLoader; opacity: 0 } + }, + State { + name: "app-launching" + PropertyChanges { target: header; opacity: 0 } + PropertyChanges { target: bootScreenLoader; opacity: 1 } + }, + State { + name: "app-running" + PropertyChanges { target: applicationLoader; opacity: 1 } + PropertyChanges { target: contentLoader; opacity: 0 } + PropertyChanges { target: header; opacity: 0 } + PropertyChanges { target: bootScreenLoader; opacity: 0 } + }, + State { + name: "settings" + PropertyChanges { target: applicationLoader; opacity: 1 } + PropertyChanges { target: contentLoader; opacity: 1 } + PropertyChanges { target: header; opacity: 0 } + PropertyChanges { target: bootScreenLoader; opacity: 0 } + }, + State { + name: "app-closing" + PropertyChanges { target: applicationLoader; opacity: 0; source: "" } + PropertyChanges { target: contentLoader; opacity: 0 } + PropertyChanges { target: header; opacity: 0 } + PropertyChanges { target: bootScreenLoader; opacity: 0 } + } + ] + + state: engine.state + + Timer { + id: failedAppLaunchTrigger; + interval: viewSettings.stateDelay; + running: false + repeat: false + onTriggered: root.closeApplication() + } + + Loader { + id: applicationLoader + opacity: 0; + visible: opacity > 0.1 + + anchors.fill: parent + asynchronous: true; + onStatusChanged: { + if (status == Loader.Error) + failedAppLaunchTrigger.start(); + } + onLoaded: { + if (applicationLoader.item.objectName == "settingsView") + engine.state = "settings" + else if (applicationLoader.item.objectName !== "empty") + engine.state = "app-running"; + } + + DemoHeader { + id: demoHeader + onInfoClicked: demoInfoPopup.open() + onCloseClicked: demoClosePopup.open() + } + } + + Loader { + id: bootScreenLoader + visible: opacity > 0 + anchors.fill: parent + sourceComponent: BootScreen { + applicationName: engine.applicationName + } + } + + DemoClosePopup { + id: demoClosePopup + visible: false + } + + DemoInfoPopup { + id: demoInfoPopup + visible: false + } + + /* Handwriting input panel for full screen handwriting input. + + This component is an optional add-on for the InputPanel component, that + is, its use does not affect the operation of the InputPanel component, + but it also can not be used as a standalone component. + + The handwriting input panel is positioned to cover the entire area of + application. The panel itself is transparent, but once it is active the + user can draw handwriting on it. + */ + HandwritingInputPanel { + z: 79 + id: handwritingInputPanel + anchors.fill: parent + inputPanel: inputPanel + Rectangle { + z: -1 + anchors.fill: parent + color: "black" + opacity: 0.10 + } + } + + /* Container area for the handwriting mode button. + + Handwriting mode button can be moved freely within the container area. + In this example, a single click changes the handwriting mode and a + double-click changes the availability of the full screen handwriting input. + */ + Item { + z: 89 + visible: handwritingInputPanel.enabled && Qt.inputMethod.visible + anchors { left: parent.left; top: parent.top; right: parent.right; bottom: inputPanel.top; } + HandwritingModeButton { + id: handwritingModeButton + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 10 + floating: true + flipable: true + width: 76 + height: width + state: handwritingInputPanel.state + onClicked: handwritingInputPanel.active = !handwritingInputPanel.active + onDoubleClicked: handwritingInputPanel.available = !handwritingInputPanel.available + } + onVisibleChanged: console.log("hw visible: "+visible) + Component.onCompleted: console.log("input visible " +visible) + } + + /* Keyboard input panel. + The keyboard is anchored to the bottom of the application. + */ + InputPanel { + id: inputPanel + z: 99 + y: root.height + anchors.left: root.left + anchors.right: root.right + visible: y < root.height + + states: State { + name: "visible" + /* The visibility of the InputPanel can be bound to the Qt.inputMethod.visible property, + but then the handwriting input panel and the keyboard input panel can be visible + at the same time. Here the visibility is bound to InputPanel.active property instead, + which allows the handwriting panel to control the visibility when necessary. + */ + when: inputPanel.active + PropertyChanges { + target: inputPanel + y: root.height - inputPanel.height + } + } + transitions: Transition { + from: "" + to: "visible" + reversible: true + ParallelAnimation { + NumberAnimation { + properties: "y" + duration: 250 + easing.type: Easing.InOutQuad + } + } + } + AutoScroller {} + } + + Item { + id: fps + opacity: engine.fpsEnabled ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 500 } } + + anchors.bottom: parent.bottom; + anchors.left: parent.left; + width: fpsLabel.width + height: fpsLabel.height + + Rectangle { + color: "black" + opacity: 0.5 + anchors.fill: fpsLabel + } + + Text { + id: fpsLabel; + color: "white" + text: "FPS: " + engine.fps + font.pixelSize: engine.sensibleButtonSize() * 0.2 + } + } + } +} -- cgit v1.2.3