Merge branch 'ui_rework_4_0' into CURA-5942_printer_selector

This commit is contained in:
Diego Prado Gesto 2018-11-26 14:33:41 +01:00
commit 52b916d45c
16 changed files with 186 additions and 107 deletions

View File

@ -20,8 +20,9 @@ Dependencies
------------ ------------
* [Uranium](https://github.com/Ultimaker/Uranium) Cura is built on top of the Uranium framework. * [Uranium](https://github.com/Ultimaker/Uranium) Cura is built on top of the Uranium framework.
* [CuraEngine](https://github.com/Ultimaker/CuraEngine) This will be needed at runtime to perform the actual slicing. * [CuraEngine](https://github.com/Ultimaker/CuraEngine) This will be needed at runtime to perform the actual slicing.
* [fdm_materials](https://github.com/Ultimaker/fdm_materials) Required to load a printer that has swappable material profiles.
* [PySerial](https://github.com/pyserial/pyserial) Only required for USB printing support. * [PySerial](https://github.com/pyserial/pyserial) Only required for USB printing support.
* [python-zeroconf](https://github.com/jstasiak/python-zeroconf) Only required to detect mDNS-enabled printers * [python-zeroconf](https://github.com/jstasiak/python-zeroconf) Only required to detect mDNS-enabled printers.
Build scripts Build scripts
------------- -------------

View File

@ -13,6 +13,6 @@ TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura
Icon=cura-icon Icon=cura-icon
Terminal=false Terminal=false
Type=Application Type=Application
MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;model/x3d+xml; MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;model/x3d+xml;text/x-gcode;
Categories=Graphics; Categories=Graphics;
Keywords=3D;Printing;Slicer; Keywords=3D;Printing;Slicer;

View File

@ -19,4 +19,12 @@
<glob-deleteall/> <glob-deleteall/>
<glob pattern="*.obj"/> <glob pattern="*.obj"/>
</mime-type> </mime-type>
<mime-type type="text/x-gcode">
<sub-class-of type="text/plain"/>
<comment>Gcode file</comment>
<icon name="unknown"/>
<glob-deleteall/>
<glob pattern="*.gcode"/>
<glob pattern="*.g"/>
</mime-type>
</mime-info> </mime-info>

View File

@ -46,12 +46,13 @@ class Backup:
# We copy the preferences file to the user data directory in Linux as it's in a different location there. # We copy the preferences file to the user data directory in Linux as it's in a different location there.
# When restoring a backup on Linux, we move it back. # When restoring a backup on Linux, we move it back.
if Platform.isLinux(): if Platform.isLinux(): #TODO: This should check for the config directory not being the same as the data directory, rather than hard-coding that to Linux systems.
preferences_file_name = self._application.getApplicationName() preferences_file_name = self._application.getApplicationName()
preferences_file = Resources.getPath(Resources.Preferences, "{}.cfg".format(preferences_file_name)) preferences_file = Resources.getPath(Resources.Preferences, "{}.cfg".format(preferences_file_name))
backup_preferences_file = os.path.join(version_data_dir, "{}.cfg".format(preferences_file_name)) backup_preferences_file = os.path.join(version_data_dir, "{}.cfg".format(preferences_file_name))
Logger.log("d", "Copying preferences file from %s to %s", preferences_file, backup_preferences_file) if os.path.exists(preferences_file) and (not os.path.exists(backup_preferences_file) or not os.path.samefile(preferences_file, backup_preferences_file)):
shutil.copyfile(preferences_file, backup_preferences_file) Logger.log("d", "Copying preferences file from %s to %s", preferences_file, backup_preferences_file)
shutil.copyfile(preferences_file, backup_preferences_file)
# Create an empty buffer and write the archive to it. # Create an empty buffer and write the archive to it.
buffer = io.BytesIO() buffer = io.BytesIO()

View File

@ -37,7 +37,7 @@ Item
leftMargin: UM.Theme.getSize("wide_margin").width leftMargin: UM.Theme.getSize("wide_margin").width
topMargin: UM.Theme.getSize("wide_margin").height topMargin: UM.Theme.getSize("wide_margin").height
} }
color: white //Always a white background for image (regardless of theme). color: "white" //Always a white background for image (regardless of theme).
Image Image
{ {
anchors.fill: parent anchors.fill: parent

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
@ -325,13 +325,12 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
## Handler for zeroConf detection. ## Handler for zeroConf detection.
# Return True or False indicating if the process succeeded. # Return True or False indicating if the process succeeded.
# Note that this function can take over 3 seconds to complete. Be carefull calling it from the main thread. # Note that this function can take over 3 seconds to complete. Be careful
# calling it from the main thread.
def _onServiceChanged(self, zero_conf, service_type, name, state_change): def _onServiceChanged(self, zero_conf, service_type, name, state_change):
if state_change == ServiceStateChange.Added: if state_change == ServiceStateChange.Added:
Logger.log("d", "Bonjour service added: %s" % name)
# First try getting info from zero-conf cache # First try getting info from zero-conf cache
info = ServiceInfo(service_type, name, properties={}) info = ServiceInfo(service_type, name, properties = {})
for record in zero_conf.cache.entries_with_name(name.lower()): for record in zero_conf.cache.entries_with_name(name.lower()):
info.update_record(zero_conf, time(), record) info.update_record(zero_conf, time(), record)

View File

@ -11,20 +11,16 @@ Row
{ {
spacing: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width
Cura.ActionButton Cura.SecondaryButton
{ {
width: UM.Theme.getSize("account_button").width width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Create account") text: catalog.i18nc("@button", "Create account")
color: UM.Theme.getColor("secondary")
hoverColor: UM.Theme.getColor("secondary")
textColor: UM.Theme.getColor("main_window_header_button_text_active")
textHoverColor: UM.Theme.getColor("main_window_header_button_text_active")
onClicked: Qt.openUrlExternally("https://account.ultimaker.com/app/create") onClicked: Qt.openUrlExternally("https://account.ultimaker.com/app/create")
fixedWidthMode: true fixedWidthMode: true
} }
Cura.ActionButton Cura.PrimaryButton
{ {
width: UM.Theme.getSize("account_button").width width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height height: UM.Theme.getSize("account_button").height

View File

@ -11,20 +11,16 @@ Row
{ {
spacing: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width
Cura.ActionButton Cura.SecondaryButton
{ {
width: UM.Theme.getSize("account_button").width width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height height: UM.Theme.getSize("account_button").height
text: catalog.i18nc("@button", "Manage account") text: catalog.i18nc("@button", "Manage account")
color: UM.Theme.getColor("secondary")
hoverColor: UM.Theme.getColor("secondary")
textColor: UM.Theme.getColor("main_window_header_button_text_active")
textHoverColor: UM.Theme.getColor("main_window_header_button_text_active")
onClicked: Qt.openUrlExternally("https://account.ultimaker.com") onClicked: Qt.openUrlExternally("https://account.ultimaker.com")
fixedWidthMode: true fixedWidthMode: true
} }
Cura.ActionButton Cura.PrimaryButton
{ {
width: UM.Theme.getSize("account_button").width width: UM.Theme.getSize("account_button").width
height: UM.Theme.getSize("account_button").height height: UM.Theme.getSize("account_button").height

View File

@ -4,25 +4,33 @@
import QtQuick 2.7 import QtQuick 2.7
import QtQuick.Controls 2.1 import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0 // For the dropshadow
import UM 1.1 as UM import UM 1.1 as UM
Button Button
{ {
id: button id: button
property alias cursorShape: mouseArea.cursorShape
property alias iconSource: buttonIcon.source property alias iconSource: buttonIcon.source
property alias textFont: buttonText.font property alias textFont: buttonText.font
property alias cornerRadius: backgroundRect.radius property alias cornerRadius: backgroundRect.radius
property alias tooltip: tooltip.text property alias tooltip: tooltip.text
property var color: UM.Theme.getColor("primary")
property var hoverColor: UM.Theme.getColor("primary_hover") property color color: UM.Theme.getColor("primary")
property var disabledColor: color property color hoverColor: UM.Theme.getColor("primary_hover")
property var textColor: UM.Theme.getColor("button_text") property color disabledColor: color
property var textHoverColor: UM.Theme.getColor("button_text_hover") property color textColor: UM.Theme.getColor("button_text")
property var textDisabledColor: textColor property color textHoverColor: textColor
property var outlineColor: color property color textDisabledColor: textColor
property var outlineHoverColor: hoverColor property color outlineColor: color
property var outlineDisabledColor: outlineColor property color outlineHoverColor: hoverColor
property color outlineDisabledColor: outlineColor
hoverEnabled: true
property alias shadowColor: shadow.color
property alias shadowEnabled: shadow.visible
// This property is used to indicate whether the button has a fixed width or the width would depend on the contents // This property is used to indicate whether the button has a fixed width or the width would depend on the contents
// Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case, // Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case,
// we elide the text to the right so the text will be cut off with the three dots at the end. // we elide the text to the right so the text will be cut off with the three dots at the end.
@ -67,6 +75,19 @@ Button
border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor
} }
DropShadow
{
id: shadow
// Don't blur the shadow
radius: 0
anchors.fill: backgroundRect
source: backgroundRect
verticalOffset: 2
visible: false
// Should always be drawn behind the background.
z: backgroundRect.z - 1
}
ToolTip ToolTip
{ {
id: tooltip id: tooltip
@ -74,12 +95,4 @@ Button
delay: 500 delay: 500
visible: text != "" && button.hovered visible: text != "" && button.hovered
} }
MouseArea
{
id: mouseArea
anchors.fill: parent
onPressed: mouse.accepted = false
hoverEnabled: true
}
} }

View File

@ -12,7 +12,7 @@ Item
{ {
id: widget id: widget
Cura.ActionButton Cura.PrimaryButton
{ {
id: saveToButton id: saveToButton
height: parent.height height: parent.height
@ -42,6 +42,9 @@ Item
id: deviceSelectionMenu id: deviceSelectionMenu
height: parent.height height: parent.height
shadowEnabled: true
shadowColor: UM.Theme.getColor("primary_shadow")
anchors anchors
{ {
top: parent.top top: parent.top
@ -65,7 +68,7 @@ Item
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
contentItem: Column contentItem: ColumnLayout
{ {
Repeater Repeater
{ {
@ -77,7 +80,7 @@ Item
color: "transparent" color: "transparent"
cornerRadius: 0 cornerRadius: 0
hoverColor: UM.Theme.getColor("primary") hoverColor: UM.Theme.getColor("primary")
Layout.fillWidth: true
onClicked: onClicked:
{ {
UM.OutputDeviceManager.setActiveDevice(model.id) UM.OutputDeviceManager.setActiveDevice(model.id)
@ -91,10 +94,7 @@ Item
{ {
opacity: visible ? 1 : 0 opacity: visible ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } } Behavior on opacity { NumberAnimation { duration: 100 } }
radius: UM.Theme.getSize("default_radius").width
color: UM.Theme.getColor("action_panel_secondary") color: UM.Theme.getColor("action_panel_secondary")
border.color: UM.Theme.getColor("lining")
border.width: UM.Theme.getSize("default_lining").width
} }
} }
} }

View File

@ -101,20 +101,18 @@ Column
spacing: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width
width: parent.width width: parent.width
Cura.ActionButton Cura.SecondaryButton
{ {
id: previewStageShortcut id: previewStageShortcut
leftPadding: UM.Theme.getSize("default_margin").width
rightPadding: UM.Theme.getSize("default_margin").width
height: UM.Theme.getSize("action_panel_button").height height: UM.Theme.getSize("action_panel_button").height
text: catalog.i18nc("@button", "Preview") text: catalog.i18nc("@button", "Preview")
color: UM.Theme.getColor("secondary")
hoverColor: UM.Theme.getColor("secondary")
textColor: UM.Theme.getColor("primary")
textHoverColor: UM.Theme.getColor("text")
onClicked: UM.Controller.setActiveStage("PreviewStage") onClicked: UM.Controller.setActiveStage("PreviewStage")
visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage" visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage"
shadowEnabled: true
shadowColor: UM.Theme.getColor("action_button_disabled_shadow")
} }
Cura.OutputDevicesActionButton Cura.OutputDevicesActionButton

View File

@ -40,9 +40,21 @@ Column
} }
} }
Label
{
id: autoSlicingLabel
width: parent.width
visible: prepareButtons.autoSlice && widget.backendState == UM.Backend.Processing
text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...")
color: UM.Theme.getColor("text")
font: UM.Theme.getFont("very_small")
renderType: Text.NativeRendering
}
Cura.IconLabel Cura.IconLabel
{ {
id: message id: unableToSliceMessage
width: parent.width width: parent.width
visible: widget.backendState == UM.Backend.Error visible: widget.backendState == UM.Backend.Error
@ -81,38 +93,41 @@ Column
} }
} }
Cura.ActionButton
{
id: prepareButton
width: parent.width
height: UM.Theme.getSize("action_panel_button").height
fixedWidthMode: true
text:
{
if ([UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) != -1)
{
return catalog.i18nc("@button", "Slice")
}
if (autoSlice)
{
return catalog.i18nc("@button", "Auto slicing...")
}
return catalog.i18nc("@button", "Cancel")
}
enabled: !autoSlice && !disabledSlice
Item
{
id: prepareButtons
// Get the current value from the preferences // Get the current value from the preferences
property bool autoSlice: UM.Preferences.getValue("general/auto_slice") property bool autoSlice: UM.Preferences.getValue("general/auto_slice")
// Disable the slice process when // Disable the slice process when
property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error].indexOf(widget.backendState) != -1
disabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled") : "transparent" width: parent.width
textDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_text") : UM.Theme.getColor("primary") height: UM.Theme.getSize("action_panel_button").height
outlineDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_border") : "transparent" visible: !autoSlice
Cura.PrimaryButton
{
id: sliceButton
fixedWidthMode: true
anchors.fill: parent
text: catalog.i18nc("@button", "Slice")
enabled: widget.backendState != UM.Backend.Error
visible: widget.backendState == UM.Backend.NotStarted || widget.backendState == UM.Backend.Error
onClicked: sliceOrStopSlicing()
}
onClicked: sliceOrStopSlicing() Cura.SecondaryButton
{
id: cancelButton
fixedWidthMode: true
anchors.fill: parent
text: catalog.i18nc("@button", "Cancel")
enabled: sliceButton.enabled
visible: !sliceButton.visible
onClicked: sliceOrStopSlicing()
}
} }
// React when the user changes the preference of having the auto slice enabled // React when the user changes the preference of having the auto slice enabled
Connections Connections
{ {
@ -120,7 +135,7 @@ Column
onPreferenceChanged: onPreferenceChanged:
{ {
var autoSlice = UM.Preferences.getValue("general/auto_slice") var autoSlice = UM.Preferences.getValue("general/auto_slice")
prepareButton.autoSlice = autoSlice prepareButtons.autoSlice = autoSlice
} }
} }

View File

@ -2,6 +2,7 @@
// Cura is released under the terms of the LGPLv3 or higher. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7 import QtQuick 2.7
import QtQuick.Controls 2.0 as Controls2
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
@ -73,25 +74,37 @@ Rectangle
} }
// Shortcut button to quick access the Toolbox // Shortcut button to quick access the Toolbox
Cura.ActionButton Controls2.Button
{ {
id: marketplaceButton
text: catalog.i18nc("@action:button", "Marketplace")
height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height)
onClicked: Cura.Actions.browsePackages.trigger()
hoverEnabled: true
background: Rectangle
{
radius: UM.Theme.getSize("action_button_radius").width
color: marketplaceButton.hovered ? UM.Theme.getColor("primary_text") : UM.Theme.getColor("main_window_header_background")
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("primary_text")
}
contentItem: Label
{
id: label
text: marketplaceButton.text
color: marketplaceButton.hovered ? UM.Theme.getColor("main_window_header_background") : UM.Theme.getColor("primary_text")
width: contentWidth
}
anchors anchors
{ {
right: accountWidget.left right: accountWidget.left
rightMargin: UM.Theme.getSize("default_margin").width rightMargin: UM.Theme.getSize("default_margin").width
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
leftPadding: UM.Theme.getSize("default_margin").width
rightPadding: UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@action:button", "Marketplace")
height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height)
color: UM.Theme.getColor("main_window_header_secondary_button_background_active")
hoverColor: UM.Theme.getColor("main_window_header_secondary_button_background_hovered")
outlineColor: UM.Theme.getColor("main_window_header_secondary_button_outline_active")
outlineHoverColor: UM.Theme.getColor("main_window_header_secondary_button_outline_hovered")
textColor: UM.Theme.getColor("main_window_header_secondary_button_text_active")
textHoverColor: UM.Theme.getColor("main_window_header_secondary_button_text_hovered")
onClicked: Cura.Actions.browsePackages.trigger()
} }
AccountWidget AccountWidget

View File

@ -0,0 +1,20 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import UM 1.4 as UM
import Cura 1.1 as Cura
Cura.ActionButton
{
shadowEnabled: true
shadowColor: enabled ? UM.Theme.getColor("primary_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow")
color: UM.Theme.getColor("primary_button")
textColor: UM.Theme.getColor("primary_button_text")
outlineColor: "transparent"
disabledColor: UM.Theme.getColor("action_button_disabled")
textDisabledColor: UM.Theme.getColor("action_button_disabled_text")
hoverColor: UM.Theme.getColor("primary_button_hover")
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import UM 1.4 as UM
import Cura 1.1 as Cura
Cura.ActionButton
{
shadowEnabled: true
shadowColor: enabled ? UM.Theme.getColor("secondary_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow")
color: UM.Theme.getColor("secondary_button")
textColor: UM.Theme.getColor("secondary_button_text")
outlineColor: "transparent"
disabledColor: UM.Theme.getColor("action_button_disabled")
textDisabledColor: UM.Theme.getColor("action_button_disabled_text")
hoverColor: UM.Theme.getColor("secondary_button_hover")
}

View File

@ -82,10 +82,22 @@
"viewport_overlay": [0, 0, 0, 192], "viewport_overlay": [0, 0, 0, 192],
"primary": [50, 130, 255, 255], "primary": [50, 130, 255, 255],
"primary_shadow": [64, 47, 205, 255],
"primary_hover": [48, 182, 231, 255], "primary_hover": [48, 182, 231, 255],
"primary_text": [255, 255, 255, 255], "primary_text": [255, 255, 255, 255],
"border": [127, 127, 127, 255], "border": [127, 127, 127, 255],
"secondary": [245, 245, 245, 255], "secondary": [245, 245, 245, 255],
"secondary_shadow": [228, 228, 228, 255],
"primary_button": [38, 113, 231, 255],
"primary_button_shadow": [27, 95, 202, 255],
"primary_button_hover": [81, 145, 247, 255],
"primary_button_text": [255, 255, 255, 255],
"secondary_button": [240, 240, 240, 255],
"secondary_button_shadow": [228, 228, 228, 255],
"secondary_button_hover": [228, 228, 228, 255],
"secondary_button_text": [30, 102, 215, 255],
"main_window_header_background": [10, 8, 80, 255], "main_window_header_background": [10, 8, 80, 255],
"main_window_header_button_text_active": [10, 8, 80, 255], "main_window_header_button_text_active": [10, 8, 80, 255],
@ -95,13 +107,6 @@
"main_window_header_button_background_inactive": [255, 255, 255, 0], "main_window_header_button_background_inactive": [255, 255, 255, 0],
"main_window_header_button_background_hovered": [255, 255, 255, 102], "main_window_header_button_background_hovered": [255, 255, 255, 102],
"main_window_header_secondary_button_text_active": [255, 255, 255, 255],
"main_window_header_secondary_button_text_hovered": [10, 8, 80, 255],
"main_window_header_secondary_button_background_active": [255, 255, 255, 0],
"main_window_header_secondary_button_background_hovered": [255, 255, 255, 255],
"main_window_header_secondary_button_outline_active": [255, 255, 255, 255],
"main_window_header_secondary_button_outline_hovered": [255, 255, 255, 255],
"account_widget_outline_active": [70, 66, 126, 255], "account_widget_outline_active": [70, 66, 126, 255],
"machine_selector_bar": [31, 36, 39, 255], "machine_selector_bar": [31, 36, 39, 255],
@ -174,14 +179,8 @@
"action_button_disabled": [245, 245, 245, 255], "action_button_disabled": [245, 245, 245, 255],
"action_button_disabled_text": [127, 127, 127, 255], "action_button_disabled_text": [127, 127, 127, 255],
"action_button_disabled_border": [245, 245, 245, 255], "action_button_disabled_border": [245, 245, 245, 255],
"action_button_shadow": [64, 47, 205, 255],
"print_button_ready": [50, 130, 255, 255], "action_button_disabled_shadow": [228, 228, 228, 255],
"print_button_ready_border": [50, 130, 255, 255],
"print_button_ready_text": [255, 255, 255, 255],
"print_button_ready_hovered": [30, 186, 245, 243],
"print_button_ready_hovered_border": [30, 186, 245, 243],
"print_button_ready_pressed": [30, 186, 245, 243],
"print_button_ready_pressed_border": [30, 186, 245, 243],
"scrollbar_background": [255, 255, 255, 255], "scrollbar_background": [255, 255, 255, 255],
"scrollbar_handle": [31, 36, 39, 255], "scrollbar_handle": [31, 36, 39, 255],