Compare commits

...

5 Commits

17 changed files with 1336 additions and 7 deletions

View File

@ -777,5 +777,29 @@ RibbonTabBar {
} }
} }
} }
RibbonTabGroup{
width: messagebar_layout.width + 30
text: qsTr("MessageBar")
ColumnLayout{
id: messagebar_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
RibbonButton{
text: qsTr("Generate One Message")
iconSource: RibbonIcons.Add
onClicked: {
messageBar.showMessage(Math.floor(Math.random()*6), "test"+Math.floor(Math.random()*6))
}
}
RibbonButton{
text: qsTr("Clear All Messages")
iconSource: RibbonIcons.DismissCircle
onClicked: {
messageBar.clearMessages()
}
}
}
}
} }
} }

View File

@ -12,6 +12,57 @@ RibbonWindow {
title: qsTr("RibbonUI APP") title: qsTr("RibbonUI APP")
comfirmedQuit: true comfirmedQuit: true
property bool modernStyle: RibbonTheme.modernStyle property bool modernStyle: RibbonTheme.modernStyle
RibbonMessageBarGroup{
id: msg_bar
implicitWidth: windowItems.width
x: windowItems.x
y: titleBar.height + tab_bar.y + tab_bar.height - tab_bar.modernMargin + (RibbonTheme.modernStyle ? 5 : 0)
target: windowItems
Component.onCompleted: {
messageModel.append([{
type: RibbonMessageBar.Info,
text: "Info (default) MessageBar."
},{
type: RibbonMessageBar.Warning,
text: "Warning defaults to multiline. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris.Visit our website.",
actionALabel: "Yes",
actionBLabel: "No",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website."
},{
type: RibbonMessageBar.Warning,
text: "Warning MessageBar content.",
actionALabel: "Action",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website.",
disableMultiline: true
},{
type: RibbonMessageBar.SevereWarning,
text: "SevereWarning MessageBar with action buttons which defaults to multiline.",
actionALabel: "Yes",
actionBLabel: "No",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website.",
},{
type: RibbonMessageBar.Blocked,
text: "Blocked MessageBar - single line, with dismiss button and truncated text. Truncation is not available if you use action buttons or multiline and should be used sparingly. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris.",
},{
type: RibbonMessageBar.Success,
text: "Success MessageBar with single line and action buttons.",
actionALabel: "Yes",
actionBLabel: "No",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website."
},{
type: RibbonMessageBar.Error,
text: "Error MessageBar with single line, with dismiss button.",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website."
}])
messageBar.showMessage(RibbonMessageBar.Info, "test")
}
}
RibbonTour{ RibbonTour{
id: tour id: tour
RibbonTourItem{ RibbonTourItem{
@ -106,6 +157,7 @@ RibbonWindow {
pageWidth: (page_slider.value / 100.0) * width pageWidth: (page_slider.value / 100.0) * width
spacing: 0 spacing: 0
isMainView: true isMainView: true
topBorderFix: msg_bar.folded ? msg_bar.barHeight : 0
ColumnLayout{ ColumnLayout{
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.topMargin: 30 Layout.topMargin: 30

View File

@ -777,5 +777,29 @@ RibbonTabBar {
} }
} }
} }
RibbonTabGroup{
width: messagebar_layout.width + 30
text: qsTr("MessageBar")
ColumnLayout{
id: messagebar_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
RibbonButton{
text: qsTr("Generate One Message")
iconSource: RibbonIcons.Add
onClicked: {
messageBar.showMessage(Math.floor(Math.random()*6), "test"+Math.floor(Math.random()*6))
}
}
RibbonButton{
text: qsTr("Clear All Messages")
iconSource: RibbonIcons.DismissCircle
onClicked: {
messageBar.clearMessages()
}
}
}
}
} }
} }

View File

@ -12,6 +12,57 @@ RibbonWindow {
title: qsTr("RibbonUI APP") title: qsTr("RibbonUI APP")
comfirmedQuit: true comfirmedQuit: true
property bool modernStyle: RibbonTheme.modernStyle property bool modernStyle: RibbonTheme.modernStyle
RibbonMessageBarGroup{
id: msg_bar
implicitWidth: windowItems.width
x: windowItems.x
y: titleBar.height + tab_bar.y + tab_bar.height - tab_bar.modernMargin + (RibbonTheme.modernStyle ? 5 : 0)
target: windowItems
Component.onCompleted: {
messageModel.append([{
type: RibbonMessageBar.Info,
text: "Info (default) MessageBar."
},{
type: RibbonMessageBar.Warning,
text: "Warning defaults to multiline. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris.Visit our website.",
actionALabel: "Yes",
actionBLabel: "No",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website."
},{
type: RibbonMessageBar.Warning,
text: "Warning MessageBar content.",
actionALabel: "Action",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website.",
disableMultiline: true
},{
type: RibbonMessageBar.SevereWarning,
text: "SevereWarning MessageBar with action buttons which defaults to multiline.",
actionALabel: "Yes",
actionBLabel: "No",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website.",
},{
type: RibbonMessageBar.Blocked,
text: "Blocked MessageBar - single line, with dismiss button and truncated text. Truncation is not available if you use action buttons or multiline and should be used sparingly. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi luctus, purus a lobortis tristique, odio augue pharetra metus, ac placerat nunc mi nec dui. Vestibulum aliquam et nunc semper scelerisque. Curabitur vitae orci nec quam condimentum porttitor et sed lacus. Vivamus ac efficitur leo. Cras faucibus mauris libero, ac placerat erat euismod et. Donec pulvinar commodo odio sit amet faucibus. In hac habitasse platea dictumst. Duis eu ante commodo, condimentum nibh pellentesque, laoreet enim. Fusce massa lorem, ultrices eu mi a, fermentum suscipit magna. Integer porta purus pulvinar, hendrerit felis eget, condimentum mauris.",
},{
type: RibbonMessageBar.Success,
text: "Success MessageBar with single line and action buttons.",
actionALabel: "Yes",
actionBLabel: "No",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website."
},{
type: RibbonMessageBar.Error,
text: "Error MessageBar with single line, with dismiss button.",
externalURL: "https://github.com/mentalfl0w/RibbonUI",
externalURLLabel: "Visit our website."
}])
messageBar.showMessage(RibbonMessageBar.Info, "test")
}
}
RibbonTour{ RibbonTour{
id: tour id: tour
RibbonTourItem{ RibbonTourItem{
@ -106,6 +157,7 @@ RibbonWindow {
pageWidth: (page_slider.value / 100.0) * width pageWidth: (page_slider.value / 100.0) * width
spacing: 0 spacing: 0
isMainView: true isMainView: true
topBorderFix: msg_bar.folded ? msg_bar.barHeight : 0
ColumnLayout{ ColumnLayout{
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.topMargin: 30 Layout.topMargin: 30

View File

@ -47,7 +47,8 @@ set(qml_files
RibbonBackStageView.qml RibbonBackStagePage.qml RibbonBackStageGroup.qml RibbonBackStageView.qml RibbonBackStagePage.qml RibbonBackStageGroup.qml
RibbonRadioButton.qml RibbonBackStageMenuItem.qml RibbonTourItem.qml RibbonRadioButton.qml RibbonBackStageMenuItem.qml RibbonTourItem.qml
RibbonObject.qml RibbonProgressBar.qml RibbonProgressRing.qml RibbonObject.qml RibbonProgressBar.qml RibbonProgressRing.qml
RibbonBusyBar.qml RibbonBusyRing.qml RibbonPageIndicator.qml) RibbonBusyBar.qml RibbonBusyRing.qml RibbonPageIndicator.qml
RibbonMessageBar.qml RibbonMessageBarGroup.qml)
# Set the QML prefix path # Set the QML prefix path
set(qml_prefix "qml/Qt${QT_VERSION_MAJOR}/") set(qml_prefix "qml/Qt${QT_VERSION_MAJOR}/")

View File

@ -4,6 +4,7 @@ import RibbonUI 1.0
RibbonRectangle { RibbonRectangle {
id: control id: control
property bool enableEffect: true
property int blurRadius: 32 property int blurRadius: 32
property alias target: effect.sourceItem property alias target: effect.sourceItem
property rect targetRect : Qt.rect(control.x, control.y, control.width, control.height) property rect targetRect : Qt.rect(control.x, control.y, control.width, control.height)
@ -18,7 +19,7 @@ RibbonRectangle {
id: effect id: effect
anchors.fill: parent anchors.fill: parent
sourceRect: control.targetRect sourceRect: control.targetRect
visible: false visible: !enableEffect
} }
GaussianBlur{ GaussianBlur{
@ -28,6 +29,7 @@ RibbonRectangle {
deviation: 8 deviation: 8
samples: (control.blurRadius / 4) * 3 samples: (control.blurRadius / 4) * 3
source: effect source: effect
visible: enableEffect
} }
RibbonRectangle{ RibbonRectangle{

View File

@ -0,0 +1,333 @@
import QtQuick 2.15
import QtQuick.Layouts 1.11
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import RibbonUI 1.0
Rectangle {
id: control
implicitWidth: parent ? parent.width : 100
implicitHeight: contentHeight
enum Type {
Info,
Success,
SevereWarning,
Blocked,
Error,
Warning
}
property real contentHeight: layout.height + layout.anchors.topMargin * 2
property alias text: barText.text
property int type: RibbonMessageBar.Info
property bool rounded: false
property string warningColor: RibbonTheme.isDarkMode ? "#41361D" : "#FDF4D2"
property string successColor: RibbonTheme.isDarkMode ? "#3A3D1F" : "#E3F5DF"
property string severeWarningColor: RibbonTheme.isDarkMode ? "#4A2C15" : "#F8DACE"
property string blockedColor: RibbonTheme.isDarkMode ? "#402827" : "#F9E8E9"
property string errorColor: RibbonTheme.isDarkMode ? "#402827" : "#F9E8E9"
property string infoColor: RibbonTheme.isDarkMode ? "#323130" : "#F3F2F1"
property string externalURL: ""
property string externalURLLabel: qsTr("Link")
property string dismissLabel: qsTr("Close")
property string overflowLabel: qsTr("See More")
property string actionALabel: qsTr("ActionA")
property string actionBLabel: qsTr("ActionB")
property bool isMultiline: type === RibbonMessageBar.SevereWarning || type === RibbonMessageBar.Warning
property bool showActionA: false
property bool showActionB: false
property bool truncated: !(isMultiline || showActionA || showActionB) && type === RibbonMessageBar.Blocked
property var actionA: ()=>console.log(qsTr("ActionA Clicked"))
property var actionB: ()=>console.log(qsTr("ActionB Clicked"))
property var dismissAction
radius: rounded ? 3 : 0
clip: true
signal dismissClicked()
signal actionAClicked()
signal actionBClicked()
Behavior on implicitHeight {
NumberAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
Behavior on color {
ColorAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
Behavior on opacity{
NumberAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
onOpacityChanged: {
if(opacity > 0)
visible = true
else
visible = false
}
onVisibleChanged: {
if(visible && opacity === 0)
opacity = 1
}
Rectangle{
anchors{
top: parent.top
horizontalCenter: parent.horizontalCenter
}
width: parent.width
height: 1
color: Qt.rgba(0,0,0,0.1)
visible: RibbonTheme.modernStyle
}
Rectangle{
anchors{
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
width: parent.width
height: 1
color: Qt.rgba(0,0,0,0.1)
}
gradient: Gradient {
GradientStop { position: 0.0; color: calculateClassicColor(control.color)[0]}
GradientStop { position: 1.0; color: calculateClassicColor(control.color)[1]}
}
Column{
id: layout
anchors{
top: parent.top
left: parent.left
leftMargin: 12
right: parent.right
rightMargin: anchors.leftMargin
topMargin: 8
bottomMargin: anchors.topMargin
}
spacing: 5
RowLayout{
id: row_1
RibbonIcon{
id: icon
iconSize: height
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
}
Text{
id: barText
elide: truncated.checked ? Text.ElideNone : Text.ElideRight
renderType: RibbonTheme.nativeText ? Text.NativeRendering : Text.QtRendering
wrapMode: truncated.checked ? Text.Wrap : Text.NoWrap
color: RibbonTheme.isDarkMode ? "white" : "black"
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: {
var w = 0
if(actiona.visible && !control.isMultiline)
w += actiona.width + row_1.spacing
if(actionb.visible && !control.isMultiline)
w += actionb.width + row_1.spacing
if(link.visible)
w += link.width + row_1.spacing
if(truncated.visible)
w += truncated.width + row_1.spacing
if(dismiss.visible)
w += dismiss.width + row_1.spacing
return control.implicitWidth - (w + icon.width + layout.anchors.leftMargin * 2)
}
}
Text{
id: link
text: control.externalURLLabel
font.underline: true
elide: Text.ElideRight
renderType: RibbonTheme.nativeText ? Text.NativeRendering : Text.QtRendering
color: tm.containsMouse ? RibbonTheme.isDarkMode ? "#91C5FA" : "#1A4474" :
RibbonTheme.isDarkMode ? "#7EB6F1" : "#255999"
Layout.alignment: Qt.AlignVCenter
Layout.maximumWidth: 150
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
visible: control.externalURL
MouseArea{
id: tm
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: Qt.openUrlExternally(control.externalURL)
}
}
Item{
parent: control.isMultiline ? row_2 : row_1
visible: control.isMultiline && (showActionA || showActionB)
implicitHeight: 1
implicitWidth: {
var w = 0
if(actiona.visible && control.isMultiline)
w += actiona.width + row_2.spacing
if(actionb.visible && control.isMultiline)
w += actionb.width + row_2.spacing
return layout.width - w
}
}
RibbonButton{
id: actiona
parent: control.isMultiline ? row_2 : row_1
text: control.actionALabel
Layout.alignment: control.isMultiline ? Qt.AlignVCenter : Qt.AlignTop
Layout.topMargin: control.isMultiline ? 0 : - layout.anchors.topMargin * 3 / 4
Layout.bottomMargin: Layout.topMargin
onClicked: {
actionA()
actionAClicked()
}
visible: control.showActionA
}
RibbonButton{
id: actionb
parent: control.isMultiline ? row_2 : row_1
text: control.actionBLabel
Layout.alignment: control.isMultiline ? Qt.AlignVCenter : Qt.AlignTop
Layout.topMargin: control.isMultiline ? 0 : - layout.anchors.topMargin * 3 / 4
Layout.bottomMargin: Layout.topMargin
onClicked: {
actionB()
actionBClicked()
}
visible: control.showActionB
}
RibbonButton{
id: truncated
textColor: RibbonTheme.isDarkMode ? "#F1F0EF" : "#3E3D3C"
showBg: false
checkable: true
showHoveredBg: false
iconSource: RibbonIcons.ChevronDoubleDown
rotation: checked ? 180 : 0
tipText: control.overflowLabel
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
visible: control.truncated && ((barText.height > link.height) || barText.truncated)
checked: control.isMultiline
Behavior on rotation {
NumberAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
}
RibbonButton{
id: dismiss
textColor: RibbonTheme.isDarkMode ? "#F1F0EF" : "#3E3D3C"
showBg: false
showHoveredBg: false
iconSource: RibbonIcons.Dismiss
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
tipText: control.dismissLabel
onClicked: {
dismissAction()
dismissClicked()
control.opacity = 0
}
}
}
RowLayout{
id: row_2
visible: control.isMultiline
layoutDirection: Qt.RightToLeft
}
}
onInfoColorChanged: refresh()
onTypeChanged: refresh()
Component.onCompleted: refresh()
function refresh(){
switch(type){
case RibbonMessageBar.Info:{
icon.color = !RibbonTheme.isDarkMode ? "#605E5C" : "#C8C6C4"
icon.iconSource = RibbonIcons.Info
control.color = infoColor
break
}
case RibbonMessageBar.Warning:{
icon.color = !RibbonTheme.isDarkMode ? "#605E5C" : "#C8C6C4"
icon.iconSource = RibbonIcons.Info
control.color = warningColor
break
}
case RibbonMessageBar.Success:{
icon.color = !RibbonTheme.isDarkMode ? "#387A26" : "#9CC262"
icon.iconSource = RibbonIcons.CheckmarkCircle
control.color = successColor
break
}
case RibbonMessageBar.SevereWarning:{
icon.color = !RibbonTheme.isDarkMode ? "#C74821" : "#F8E24B"
icon.iconSource = RibbonIcons.Warning
control.color = severeWarningColor
break
}
case RibbonMessageBar.Blocked:{
icon.color = !RibbonTheme.isDarkMode ? "#9A1E13" : "#E1777E"
icon.iconSource = RibbonIcons.SubtractCircle
control.color = blockedColor
break
}
case RibbonMessageBar.Error:{
icon.color = !RibbonTheme.isDarkMode ? "#9A1E13" : "#E1777E"
icon.iconSource = RibbonIcons.DismissCircle
control.color = errorColor
break
}
}
}
function calculateClassicColor(modernColor){
function limit(num){
if(num < 0)
return 0
else if(num > 255)
return 255
else
return num
}
if(!RibbonTheme.modernStyle){
modernColor = String(modernColor)
const num = parseInt(modernColor.slice(1), 16)
if(modernColor.length === 7)
return [Qt.rgba(limit((num >> 16) & 255) / 255,
limit(((num >> 8) & 255) + 0x03) / 255,
limit((num & 255) + 0x12) / 255,
1),
Qt.rgba(limit(((num >> 16) & 255) - 0x05) / 255,
limit(((num >> 8) & 255) - 0x01) / 255,
limit((num & 255) + 0x0C) / 255,
1)]
else
return [Qt.rgba(limit((num >> 24) & 255) / 255,
limit(((num >> 16) & 255) + 0x03) / 255,
limit(((num >> 8) & 255) + 0x12) / 255,
limit(num & 255) / 255),
Qt.rgba(limit(((num >> 24) & 255) - 0x05) / 255,
limit(((num >> 16) & 255) - 0x01) / 255,
limit(((num >> 8) & 255) + 0x0C) / 255,
limit(num & 255) / 255)]
}
else
return [modernColor,modernColor]
}
}

View File

@ -0,0 +1,249 @@
import QtQuick 2.15
import QtQuick.Layouts 1.11
import QtQuick.Controls 2.15
import RibbonUI 1.0
RibbonBlur {
id: control
parent: Overlay.overlay
implicitWidth: parent.width
useSolidBg: true
implicitHeight: 30
maskColor: folded ? "transparent" : RibbonTheme.isDarkMode ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
bottomLeftRadius: folded ? 0 : 5
bottomRightRadius: bottomLeftRadius
enableEffect: handler.visible || !folded
readonly property alias folded: folded_btn.checked
property int animationTime: 400
property real barHeight: implicitHeight - handler.height
property alias messageModel: messageModel
Behavior on implicitHeight {
NumberAnimation {
duration: control.animationTime / 2
easing.type: Easing.OutSine
}
}
ListModel{
id: messageModel
onCountChanged: {
message_list.currentIndex = count ? count - 1 : 0
if(count === 0)
folded_btn.checked = true
}
}
Timer{
id: auto_scroll_btn_timer
interval: control.animationTime
repeat: false
onTriggered: {
message_list.positionViewAtIndex(message_list.currentIndex, ListView.Center)
}
}
ListView{
id: message_list
cacheBuffer: Math.abs(message_list.height * 2)
interactive: !folded
clip: true
anchors{
top: parent.top
left: parent.left
right: parent.right
}
height: parent.height - (folded ? handler.height : 0)
model: messageModel
delegate: Item{
id: item
implicitHeight: bar.contentHeight + (control.folded ? 0 : 10)
implicitWidth: control.width
RibbonMessageBar{
id: bar
anchors.centerIn: parent
property bool isCurrentItem: item === message_list.currentItem
onIsCurrentItemChanged: {
if(folded){
message_list.height = item.implicitHeight
control.implicitHeight = item.implicitHeight + handler.height
}
}
text: model.text
externalURL: model.externalURL
dismissAction: ()=>messageModel.remove(index)
Component.onCompleted: {
if(model.disableMultiline)
isMultiline = !model.disableMultiline
if(model.type)
type = model.type
if(model.rounded)
rounded = model.rounded
if(model.externalURLLabel)
externalURLLabel = model.externalURLLabel
if(model.dismissLabel)
dismissLabel = model.dismissLabel
if(model.overflowLabel)
overflowLabel = model.overflowLabel
if(model.actionALabel){
actionALabel = model.actionALabel
showActionA = true
if(model.actionA){
actionA = model.actionA
}else{
actionA = ()=>console.log(index+qsTr(`'s ${model.actionALabel} Clicked`))
}
}
if(model.actionBLabel){
actionBLabel = model.actionBLabel
showActionB = true
if(model.actionB){
actionB = model.actionB
}else{
actionB = ()=>console.log(index+qsTr(`'s ${model.actionBLabel} Clicked`))
}
}
}
}
onImplicitHeightChanged:{
if(message_list.currentIndex === index && folded){
message_list.height = item.implicitHeight
control.implicitHeight = item.implicitHeight + handler.height
}
}
}
verticalLayoutDirection: ListView.BottomToTop
add: Transition {
NumberAnimation {
properties: "y"
from: message_list.height
duration: control.animationTime / 2
}
}
remove: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: control.animationTime / 2
}
}
ScrollBar.vertical: RibbonScrollBar {
anchors.right: message_list.right
anchors.rightMargin: 2
interactive: !folded
}
onCurrentItemChanged: {
auto_scroll_btn_timer.restart()
}
Behavior on height {
NumberAnimation {
duration: control.animationTime / 2
easing.type: Easing.OutSine
}
}
}
HoverHandler{
id: hover
}
RibbonRectangle{
id: handler
x: message_list.x + (message_list.width - width) / 2
y: message_list.y + message_list.height * (folded ? 1 : 0)
implicitHeight: 20
implicitWidth: parent.width
topLeftRadius: folded ? 0 : 10
topRightRadius: topLeftRadius
bottomLeftRadius: folded ? 10 : 0
bottomRightRadius: bottomLeftRadius
visible: hover.hovered && messageModel.count
color: RibbonTheme.isDarkMode ? Qt.rgba(0,0,0,0.5) : Qt.rgba(1,1,1,0.5)
Behavior on color {
ColorAnimation {
duration: control.animationTime / 2
easing.type: Easing.OutSine
}
}
RowLayout{
anchors.centerIn: parent
RibbonButton{
checkable: true
showBg: false
showHoveredBg: false
iconSource: RibbonIcons.DismissCircle
tipText: qsTr("Clear All")
onClicked: clearMessages()
visible: !folded
}
RibbonButton{
id: folded_btn
checkable: true
showBg: false
showHoveredBg: false
iconSource: RibbonIcons.TriangleDown
rotation: checked ? 0 : 180
checked: true
tipText: checked ? qsTr("Show all messages") : qsTr("Hide all messages")
textColor: RibbonTheme.isDarkMode ? "white" : "black"
onClicked:{
if(!folded){
control.implicitHeight = Window.window.viewItems.height - control.x
message_list.height = control.implicitHeight
}
else{
message_list.height = message_list.currentItem.implicitHeight
control.implicitHeight = message_list.currentItem.implicitHeight + handler.height
auto_scroll_btn_timer.restart()
}
}
Behavior on rotation {
NumberAnimation {
duration: control.animationTime
easing.type: Easing.OutSine
}
}
}
}
}
Component.onCompleted: {
message_list.currentIndex = messageModel.count > 0 ? messageModel.count - 1 : 0
Window.window.messageBar = control
}
function showMessage(type, text, actionALabel, actionBLabel, externalURL,
externalURLLabel, disableMultiline, rounded){
let item = {}
if(type)
item['type'] = type
if(text)
item['text'] = text
if(actionALabel)
item['actionALabel'] = actionALabel
if(actionBLabel)
item['actionBLabel'] = actionBLabel
if(externalURL)
item['externalURL'] = externalURL
if(externalURLLabel)
item['externalURLLabel'] = externalURLLabel
if(disableMultiline)
item['disableMultiline'] = disableMultiline
if(rounded)
item['rounded'] = rounded
messageModel.append(item)
}
function clearMessages(){
messageModel.clear()
implicitHeight = 0
barHeight = 0
message_list.height = 0
}
}

View File

@ -66,6 +66,7 @@ Item {
implicitHeight: container.height implicitHeight: container.height
implicitWidth: container.width implicitWidth: container.width
maskSource: shape maskSource: shape
invert: control.color === "transparent" || control.color === "#00000000"
} }
} }
} }

View File

@ -14,6 +14,8 @@ Item {
property bool isMainView: false property bool isMainView: false
property alias bgColor: bg.color property alias bgColor: bg.color
property alias bgVisible: bg.visible property alias bgVisible: bg.visible
property real topBorderFix: 0
property real bottomBorderFix: 0
z:-2 z:-2
clip: true clip: true
width: parent.width width: parent.width
@ -32,7 +34,7 @@ Item {
right: parent.right right: parent.right
top: parent.top top: parent.top
} }
height: isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.tabBar.height - Window.window.tabBar.modernMargin) : 0 : 0 : 0 height: (isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.tabBar.height - Window.window.tabBar.modernMargin) : 0 : 0 : 0) + topBorderFix
} }
Item{ Item{
@ -60,7 +62,7 @@ Item {
right: parent.right right: parent.right
bottom: parent.bottom bottom: parent.bottom
} }
height: isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.bottomBar.height) : 0 : 0 : 0 height: (isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.bottomBar.height) : 0 : 0 : 0) + bottomBorderFix
} }
Component.onCompleted: { Component.onCompleted: {

View File

@ -22,6 +22,7 @@ Window {
property var viewItems property var viewItems
property var tabBar property var tabBar
property var bottomBar property var bottomBar
property var messageBar
readonly property int borderWidth: border_rect.border.width readonly property int borderWidth: border_rect.border.width
readonly property int borderRadius: border_rect.radius readonly property int borderRadius: border_rect.radius
visible: false visible: false

View File

@ -4,6 +4,7 @@ import RibbonUI
RibbonRectangle { RibbonRectangle {
id: control id: control
property bool enableEffect: true
property int blurRadius: 32 property int blurRadius: 32
property alias target: effect.sourceItem property alias target: effect.sourceItem
property rect targetRect : Qt.rect(control.x, control.y, control.width, control.height) property rect targetRect : Qt.rect(control.x, control.y, control.width, control.height)
@ -18,7 +19,7 @@ RibbonRectangle {
id: effect id: effect
anchors.fill: parent anchors.fill: parent
sourceRect: control.targetRect sourceRect: control.targetRect
visible: false visible: !enableEffect
} }
GaussianBlur{ GaussianBlur{
@ -28,6 +29,7 @@ RibbonRectangle {
deviation: 8 deviation: 8
samples: (control.blurRadius / 4) * 3 samples: (control.blurRadius / 4) * 3
source: effect source: effect
visible: enableEffect
} }
RibbonRectangle{ RibbonRectangle{

View File

@ -0,0 +1,333 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import RibbonUI
Rectangle {
id: control
implicitWidth: parent ? parent.width : 100
implicitHeight: contentHeight
enum Type {
Info,
Success,
SevereWarning,
Blocked,
Error,
Warning
}
property real contentHeight: layout.height + layout.anchors.topMargin * 2
property alias text: barText.text
property int type: RibbonMessageBar.Info
property bool rounded: false
property string warningColor: RibbonTheme.isDarkMode ? "#41361D" : "#FDF4D2"
property string successColor: RibbonTheme.isDarkMode ? "#3A3D1F" : "#E3F5DF"
property string severeWarningColor: RibbonTheme.isDarkMode ? "#4A2C15" : "#F8DACE"
property string blockedColor: RibbonTheme.isDarkMode ? "#402827" : "#F9E8E9"
property string errorColor: RibbonTheme.isDarkMode ? "#402827" : "#F9E8E9"
property string infoColor: RibbonTheme.isDarkMode ? "#323130" : "#F3F2F1"
property string externalURL: ""
property string externalURLLabel: qsTr("Link")
property string dismissLabel: qsTr("Close")
property string overflowLabel: qsTr("See More")
property string actionALabel: qsTr("ActionA")
property string actionBLabel: qsTr("ActionB")
property bool isMultiline: type === RibbonMessageBar.SevereWarning || type === RibbonMessageBar.Warning
property bool showActionA: false
property bool showActionB: false
property bool truncated: !(isMultiline || showActionA || showActionB) && type === RibbonMessageBar.Blocked
property var actionA: ()=>console.log(qsTr("ActionA Clicked"))
property var actionB: ()=>console.log(qsTr("ActionB Clicked"))
property var dismissAction
radius: rounded ? 3 : 0
clip: true
signal dismissClicked()
signal actionAClicked()
signal actionBClicked()
Behavior on implicitHeight {
NumberAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
Behavior on color {
ColorAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
Behavior on opacity{
NumberAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
onOpacityChanged: {
if(opacity > 0)
visible = true
else
visible = false
}
onVisibleChanged: {
if(visible && opacity === 0)
opacity = 1
}
Rectangle{
anchors{
top: parent.top
horizontalCenter: parent.horizontalCenter
}
width: parent.width
height: 1
color: Qt.rgba(0,0,0,0.1)
visible: RibbonTheme.modernStyle
}
Rectangle{
anchors{
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
width: parent.width
height: 1
color: Qt.rgba(0,0,0,0.1)
}
gradient: Gradient {
GradientStop { position: 0.0; color: calculateClassicColor(control.color)[0]}
GradientStop { position: 1.0; color: calculateClassicColor(control.color)[1]}
}
Column{
id: layout
anchors{
top: parent.top
left: parent.left
leftMargin: 12
right: parent.right
rightMargin: anchors.leftMargin
topMargin: 8
bottomMargin: anchors.topMargin
}
spacing: 5
RowLayout{
id: row_1
RibbonIcon{
id: icon
iconSize: height
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
}
Text{
id: barText
elide: truncated.checked ? Text.ElideNone : Text.ElideRight
renderType: RibbonTheme.nativeText ? Text.NativeRendering : Text.QtRendering
wrapMode: truncated.checked ? Text.Wrap : Text.NoWrap
color: RibbonTheme.isDarkMode ? "white" : "black"
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: {
var w = 0
if(actiona.visible && !control.isMultiline)
w += actiona.width + row_1.spacing
if(actionb.visible && !control.isMultiline)
w += actionb.width + row_1.spacing
if(link.visible)
w += link.width + row_1.spacing
if(truncated.visible)
w += truncated.width + row_1.spacing
if(dismiss.visible)
w += dismiss.width + row_1.spacing
return control.implicitWidth - (w + icon.width + layout.anchors.leftMargin * 2)
}
}
Text{
id: link
text: control.externalURLLabel
font.underline: true
elide: Text.ElideRight
renderType: RibbonTheme.nativeText ? Text.NativeRendering : Text.QtRendering
color: tm.containsMouse ? RibbonTheme.isDarkMode ? "#91C5FA" : "#1A4474" :
RibbonTheme.isDarkMode ? "#7EB6F1" : "#255999"
Layout.alignment: Qt.AlignVCenter
Layout.maximumWidth: 150
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
visible: control.externalURL
MouseArea{
id: tm
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: Qt.openUrlExternally(control.externalURL)
}
}
Item{
parent: control.isMultiline ? row_2 : row_1
visible: control.isMultiline && (showActionA || showActionB)
implicitHeight: 1
implicitWidth: {
var w = 0
if(actiona.visible && control.isMultiline)
w += actiona.width + row_2.spacing
if(actionb.visible && control.isMultiline)
w += actionb.width + row_2.spacing
return layout.width - w
}
}
RibbonButton{
id: actiona
parent: control.isMultiline ? row_2 : row_1
text: control.actionALabel
Layout.alignment: control.isMultiline ? Qt.AlignVCenter : Qt.AlignTop
Layout.topMargin: control.isMultiline ? 0 : - layout.anchors.topMargin * 3 / 4
Layout.bottomMargin: Layout.topMargin
onClicked: {
actionA()
actionAClicked()
}
visible: control.showActionA
}
RibbonButton{
id: actionb
parent: control.isMultiline ? row_2 : row_1
text: control.actionBLabel
Layout.alignment: control.isMultiline ? Qt.AlignVCenter : Qt.AlignTop
Layout.topMargin: control.isMultiline ? 0 : - layout.anchors.topMargin * 3 / 4
Layout.bottomMargin: Layout.topMargin
onClicked: {
actionB()
actionBClicked()
}
visible: control.showActionB
}
RibbonButton{
id: truncated
textColor: RibbonTheme.isDarkMode ? "#F1F0EF" : "#3E3D3C"
showBg: false
checkable: true
showHoveredBg: false
iconSource: RibbonIcons.ChevronDoubleDown
rotation: checked ? 180 : 0
tipText: control.overflowLabel
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
visible: control.truncated && ((barText.height > link.height) || barText.truncated)
checked: control.isMultiline
Behavior on rotation {
NumberAnimation {
duration: 200
easing.type: Easing.OutSine
}
}
}
RibbonButton{
id: dismiss
textColor: RibbonTheme.isDarkMode ? "#F1F0EF" : "#3E3D3C"
showBg: false
showHoveredBg: false
iconSource: RibbonIcons.Dismiss
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: Math.min(barText.contentHeight, 16)
tipText: control.dismissLabel
onClicked: {
dismissAction()
dismissClicked()
control.opacity = 0
}
}
}
RowLayout{
id: row_2
visible: control.isMultiline
layoutDirection: Qt.RightToLeft
}
}
onInfoColorChanged: refresh()
onTypeChanged: refresh()
Component.onCompleted: refresh()
function refresh(){
switch(type){
case RibbonMessageBar.Info:{
icon.color = !RibbonTheme.isDarkMode ? "#605E5C" : "#C8C6C4"
icon.iconSource = RibbonIcons.Info
control.color = infoColor
break
}
case RibbonMessageBar.Warning:{
icon.color = !RibbonTheme.isDarkMode ? "#605E5C" : "#C8C6C4"
icon.iconSource = RibbonIcons.Info
control.color = warningColor
break
}
case RibbonMessageBar.Success:{
icon.color = !RibbonTheme.isDarkMode ? "#387A26" : "#9CC262"
icon.iconSource = RibbonIcons.CheckmarkCircle
control.color = successColor
break
}
case RibbonMessageBar.SevereWarning:{
icon.color = !RibbonTheme.isDarkMode ? "#C74821" : "#F8E24B"
icon.iconSource = RibbonIcons.Warning
control.color = severeWarningColor
break
}
case RibbonMessageBar.Blocked:{
icon.color = !RibbonTheme.isDarkMode ? "#9A1E13" : "#E1777E"
icon.iconSource = RibbonIcons.SubtractCircle
control.color = blockedColor
break
}
case RibbonMessageBar.Error:{
icon.color = !RibbonTheme.isDarkMode ? "#9A1E13" : "#E1777E"
icon.iconSource = RibbonIcons.DismissCircle
control.color = errorColor
break
}
}
}
function calculateClassicColor(modernColor){
function limit(num){
if(num < 0)
return 0
else if(num > 255)
return 255
else
return num
}
if(!RibbonTheme.modernStyle){
modernColor = String(modernColor)
const num = parseInt(modernColor.slice(1), 16)
if(modernColor.length === 7)
return [Qt.rgba(limit((num >> 16) & 255) / 255,
limit(((num >> 8) & 255) + 0x03) / 255,
limit((num & 255) + 0x12) / 255,
1),
Qt.rgba(limit(((num >> 16) & 255) - 0x05) / 255,
limit(((num >> 8) & 255) - 0x01) / 255,
limit((num & 255) + 0x0C) / 255,
1)]
else
return [Qt.rgba(limit((num >> 24) & 255) / 255,
limit(((num >> 16) & 255) + 0x03) / 255,
limit(((num >> 8) & 255) + 0x12) / 255,
limit(num & 255) / 255),
Qt.rgba(limit(((num >> 24) & 255) - 0x05) / 255,
limit(((num >> 16) & 255) - 0x01) / 255,
limit(((num >> 8) & 255) + 0x0C) / 255,
limit(num & 255) / 255)]
}
else
return [modernColor,modernColor]
}
}

View File

@ -0,0 +1,249 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import RibbonUI
RibbonBlur {
id: control
parent: Overlay.overlay
implicitWidth: parent.width
useSolidBg: true
implicitHeight: 30
maskColor: folded ? "transparent" : RibbonTheme.isDarkMode ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
bottomLeftRadius: folded ? 0 : 5
bottomRightRadius: bottomLeftRadius
enableEffect: handler.visible || !folded
readonly property alias folded: folded_btn.checked
property int animationTime: 400
property real barHeight: implicitHeight - handler.height
property alias messageModel: messageModel
Behavior on implicitHeight {
NumberAnimation {
duration: control.animationTime / 2
easing.type: Easing.OutSine
}
}
ListModel{
id: messageModel
onCountChanged: {
message_list.currentIndex = count ? count - 1 : 0
if(count === 0)
folded_btn.checked = true
}
}
Timer{
id: auto_scroll_btn_timer
interval: control.animationTime
repeat: false
onTriggered: {
message_list.positionViewAtIndex(message_list.currentIndex, ListView.Center)
}
}
ListView{
id: message_list
cacheBuffer: Math.abs(message_list.height * 2)
interactive: !folded
clip: true
anchors{
top: parent.top
left: parent.left
right: parent.right
}
height: parent.height - (folded ? handler.height : 0)
model: messageModel
delegate: Item{
id: item
implicitHeight: bar.contentHeight + (control.folded ? 0 : 10)
implicitWidth: control.width
RibbonMessageBar{
id: bar
anchors.centerIn: parent
property bool isCurrentItem: item === message_list.currentItem
onIsCurrentItemChanged: {
if(folded){
message_list.height = item.implicitHeight
control.implicitHeight = item.implicitHeight + handler.height
}
}
text: model.text
externalURL: model.externalURL
dismissAction: ()=>messageModel.remove(index)
Component.onCompleted: {
if(model.disableMultiline)
isMultiline = !model.disableMultiline
if(model.type)
type = model.type
if(model.rounded)
rounded = model.rounded
if(model.externalURLLabel)
externalURLLabel = model.externalURLLabel
if(model.dismissLabel)
dismissLabel = model.dismissLabel
if(model.overflowLabel)
overflowLabel = model.overflowLabel
if(model.actionALabel){
actionALabel = model.actionALabel
showActionA = true
if(model.actionA){
actionA = model.actionA
}else{
actionA = ()=>console.log(index+qsTr(`'s ${model.actionALabel} Clicked`))
}
}
if(model.actionBLabel){
actionBLabel = model.actionBLabel
showActionB = true
if(model.actionB){
actionB = model.actionB
}else{
actionB = ()=>console.log(index+qsTr(`'s ${model.actionBLabel} Clicked`))
}
}
}
}
onImplicitHeightChanged:{
if(message_list.currentIndex === index && folded){
message_list.height = item.implicitHeight
control.implicitHeight = item.implicitHeight + handler.height
}
}
}
verticalLayoutDirection: ListView.BottomToTop
add: Transition {
NumberAnimation {
properties: "y"
from: message_list.height
duration: control.animationTime / 2
}
}
remove: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: control.animationTime / 2
}
}
ScrollBar.vertical: RibbonScrollBar {
anchors.right: message_list.right
anchors.rightMargin: 2
interactive: !folded
}
onCurrentItemChanged: {
auto_scroll_btn_timer.restart()
}
Behavior on height {
NumberAnimation {
duration: control.animationTime / 2
easing.type: Easing.OutSine
}
}
}
HoverHandler{
id: hover
}
RibbonRectangle{
id: handler
x: message_list.x + (message_list.width - width) / 2
y: message_list.y + message_list.height * (folded ? 1 : 0)
implicitHeight: 20
implicitWidth: parent.width
topLeftRadius: folded ? 0 : 10
topRightRadius: topLeftRadius
bottomLeftRadius: folded ? 10 : 0
bottomRightRadius: bottomLeftRadius
visible: hover.hovered && messageModel.count
color: RibbonTheme.isDarkMode ? Qt.rgba(0,0,0,0.5) : Qt.rgba(1,1,1,0.5)
Behavior on color {
ColorAnimation {
duration: control.animationTime / 2
easing.type: Easing.OutSine
}
}
RowLayout{
anchors.centerIn: parent
RibbonButton{
checkable: true
showBg: false
showHoveredBg: false
iconSource: RibbonIcons.DismissCircle
tipText: qsTr("Clear All")
onClicked: clearMessages()
visible: !folded
}
RibbonButton{
id: folded_btn
checkable: true
showBg: false
showHoveredBg: false
iconSource: RibbonIcons.TriangleDown
rotation: checked ? 0 : 180
checked: true
tipText: checked ? qsTr("Show all messages") : qsTr("Hide all messages")
textColor: RibbonTheme.isDarkMode ? "white" : "black"
onClicked:{
if(!folded){
control.implicitHeight = Window.window.viewItems.height - control.x
message_list.height = control.implicitHeight
}
else{
message_list.height = message_list.currentItem.implicitHeight
control.implicitHeight = message_list.currentItem.implicitHeight + handler.height
auto_scroll_btn_timer.restart()
}
}
Behavior on rotation {
NumberAnimation {
duration: control.animationTime
easing.type: Easing.OutSine
}
}
}
}
}
Component.onCompleted: {
message_list.currentIndex = messageModel.count > 0 ? messageModel.count - 1 : 0
Window.window.messageBar = control
}
function showMessage(type, text, actionALabel, actionBLabel, externalURL,
externalURLLabel, disableMultiline, rounded){
let item = {}
if(type)
item['type'] = type
if(text)
item['text'] = text
if(actionALabel)
item['actionALabel'] = actionALabel
if(actionBLabel)
item['actionBLabel'] = actionBLabel
if(externalURL)
item['externalURL'] = externalURL
if(externalURLLabel)
item['externalURLLabel'] = externalURLLabel
if(disableMultiline)
item['disableMultiline'] = disableMultiline
if(rounded)
item['rounded'] = rounded
messageModel.append(item)
}
function clearMessages(){
messageModel.clear()
implicitHeight = 0
barHeight = 0
message_list.height = 0
}
}

View File

@ -68,6 +68,7 @@ Item {
implicitHeight: container.height implicitHeight: container.height
implicitWidth: container.width implicitWidth: container.width
maskSource: shape maskSource: shape
invert: control.color === "transparent" || control.color === "#00000000"
} }
} }
} }

View File

@ -14,6 +14,8 @@ Item {
property bool isMainView: false property bool isMainView: false
property alias bgColor: bg.color property alias bgColor: bg.color
property alias bgVisible: bg.visible property alias bgVisible: bg.visible
property real topBorderFix: 0
property real bottomBorderFix: 0
z:-2 z:-2
clip: true clip: true
width: parent.width width: parent.width
@ -32,7 +34,7 @@ Item {
right: parent.right right: parent.right
top: parent.top top: parent.top
} }
height: isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.tabBar.height - Window.window.tabBar.modernMargin) : 0 : 0 : 0 height: (isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.tabBar.height - Window.window.tabBar.modernMargin) : 0 : 0 : 0) + topBorderFix
} }
Item{ Item{
@ -60,7 +62,7 @@ Item {
right: parent.right right: parent.right
bottom: parent.bottom bottom: parent.bottom
} }
height: isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.bottomBar.height) : 0 : 0 : 0 height: (isMainView ? Window.window ? Window.window.tabBar ? Math.abs(Window.window.bottomBar.height) : 0 : 0 : 0) + bottomBorderFix
} }
Component.onCompleted: { Component.onCompleted: {

View File

@ -22,6 +22,7 @@ Window {
property var viewItems property var viewItems
property var tabBar property var tabBar
property var bottomBar property var bottomBar
property var messageBar
readonly property int borderWidth: border_rect.border.width readonly property int borderWidth: border_rect.border.width
readonly property int borderRadius: border_rect.radius readonly property int borderRadius: border_rect.radius
visible: false visible: false