Add focused window controls to top bar
Introduces a new `WindowControls` component in the desktop top bar with minimize, maximize/restore, and close actions for the focused window. `WindowService` now exposes focused-window presence/maximized state plus focused-window convenience actions to support this UI. Also extends Nebula UI with a reusable `NebulaToolTip`, keyboard/pressed-state support in `NebulaIconButton`, new window-control glyphs in `NebulaIcon`, and theme tokens for pressed/danger hover states, with CMake and README updates to register/document the new components.
This commit is contained in:
@@ -82,6 +82,18 @@ Item {
|
||||
case "application":
|
||||
paintApplication(ctx)
|
||||
break
|
||||
case "minimize":
|
||||
paintMinimize(ctx)
|
||||
break
|
||||
case "maximize":
|
||||
paintMaximize(ctx)
|
||||
break
|
||||
case "restore":
|
||||
paintRestore(ctx)
|
||||
break
|
||||
case "close":
|
||||
paintClose(ctx)
|
||||
break
|
||||
default:
|
||||
paintApplication(ctx)
|
||||
break
|
||||
@@ -192,6 +204,41 @@ Item {
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
function paintMinimize(ctx) {
|
||||
ctx.lineWidth = 1.7
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(6.4, 16.2)
|
||||
ctx.lineTo(17.6, 16.2)
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
function paintMaximize(ctx) {
|
||||
ctx.lineWidth = 1.5
|
||||
ctx.beginPath()
|
||||
roundedRect(ctx, 6.2, 6.2, 11.6, 11.6, 2)
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
function paintRestore(ctx) {
|
||||
ctx.lineWidth = 1.45
|
||||
ctx.beginPath()
|
||||
roundedRect(ctx, 8.6, 5.6, 9.4, 9.4, 1.7)
|
||||
ctx.stroke()
|
||||
ctx.beginPath()
|
||||
roundedRect(ctx, 5.6, 8.6, 9.4, 9.4, 1.7)
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
function paintClose(ctx) {
|
||||
ctx.lineWidth = 1.7
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(7.3, 7.3)
|
||||
ctx.lineTo(16.7, 16.7)
|
||||
ctx.moveTo(16.7, 7.3)
|
||||
ctx.lineTo(7.3, 16.7)
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
function paintApplication(ctx) {
|
||||
ctx.lineWidth = 1.5
|
||||
ctx.beginPath()
|
||||
|
||||
@@ -7,6 +7,7 @@ Item {
|
||||
property bool active: false
|
||||
property bool danger: false
|
||||
readonly property bool hovered: hover.containsMouse
|
||||
readonly property bool pressed: hover.pressed
|
||||
readonly property color iconColor: {
|
||||
if (danger && hovered)
|
||||
return Theme.danger
|
||||
@@ -23,16 +24,32 @@ Item {
|
||||
|
||||
implicitWidth: Theme.controlHeight
|
||||
implicitHeight: Theme.controlHeight
|
||||
activeFocusOnTab: true
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: root.label
|
||||
Accessible.onPressAction: root.clicked()
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
event.accepted = true
|
||||
root.clicked()
|
||||
}
|
||||
Keys.onSpacePressed: {
|
||||
event.accepted = true
|
||||
root.clicked()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.radiusSmall
|
||||
color: {
|
||||
if (root.danger && hover.pressed)
|
||||
return Theme.dangerSoftStrong
|
||||
if (root.danger && hover.containsMouse)
|
||||
return Theme.dangerSoft
|
||||
if (root.active)
|
||||
return Theme.accentSoft
|
||||
if (hover.pressed)
|
||||
return Theme.pressedFill
|
||||
if (hover.containsMouse)
|
||||
return Theme.hoverFill
|
||||
return "transparent"
|
||||
@@ -63,4 +80,9 @@ Item {
|
||||
FocusFrame {
|
||||
radius: Theme.radiusSmall
|
||||
}
|
||||
|
||||
NebulaToolTip {
|
||||
text: root.label
|
||||
active: hover.containsMouse && !hover.pressed && root.label.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string text: ""
|
||||
property bool active: false
|
||||
property int delay: 550
|
||||
property int timeout: 4000
|
||||
|
||||
width: 0
|
||||
height: 0
|
||||
enabled: false
|
||||
Accessible.ignored: true
|
||||
|
||||
Timer {
|
||||
id: showTimer
|
||||
interval: root.delay
|
||||
repeat: false
|
||||
onTriggered: root.open()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: root.timeout
|
||||
repeat: false
|
||||
onTriggered: tip.visible = false
|
||||
}
|
||||
|
||||
Window {
|
||||
id: tip
|
||||
flags: Qt.Popup | Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus
|
||||
color: "transparent"
|
||||
visible: false
|
||||
width: Math.ceil(tipText.implicitWidth) + 16
|
||||
height: Math.ceil(tipText.implicitHeight) + 10
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.radiusSmall
|
||||
color: Theme.surfaceRaised
|
||||
border.color: Theme.borderStrong
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
id: tipText
|
||||
anchors.centerIn: parent
|
||||
text: root.text
|
||||
color: Theme.textPrimary
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onActiveChanged: root.sync()
|
||||
onTextChanged: {
|
||||
if (tip.visible)
|
||||
root.place()
|
||||
else
|
||||
root.sync()
|
||||
}
|
||||
|
||||
function sync() {
|
||||
if (root.active && root.text.length > 0) {
|
||||
showTimer.restart()
|
||||
return
|
||||
}
|
||||
showTimer.stop()
|
||||
hideTimer.stop()
|
||||
tip.visible = false
|
||||
}
|
||||
|
||||
function open() {
|
||||
if (!root.active || root.text.length === 0)
|
||||
return
|
||||
const host = root.parent
|
||||
if (!host)
|
||||
return
|
||||
const win = host.Window.window
|
||||
if (win)
|
||||
tip.transientParent = win
|
||||
root.place()
|
||||
tip.visible = true
|
||||
hideTimer.restart()
|
||||
}
|
||||
|
||||
function place() {
|
||||
const host = root.parent
|
||||
if (!host)
|
||||
return
|
||||
const pos = host.mapToGlobal(host.width / 2, host.height + 6)
|
||||
const screenWidth = Screen.width
|
||||
tip.x = Math.round(Math.max(8, Math.min(pos.x - tip.width / 2, screenWidth - tip.width - 8)))
|
||||
tip.y = Math.round(pos.y)
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,9 @@ QtObject {
|
||||
readonly property color focus: Qt.rgba(126 / 255, 160 / 255, 196 / 255, 0.55)
|
||||
|
||||
readonly property color hoverFill: Qt.rgba(1, 1, 1, 0.06)
|
||||
readonly property color pressedFill: Qt.rgba(1, 1, 1, 0.10)
|
||||
readonly property color dangerSoft: Qt.rgba(201 / 255, 136 / 255, 136 / 255, 0.18)
|
||||
readonly property color dangerSoftStrong: Qt.rgba(201 / 255, 136 / 255, 136 / 255, 0.28)
|
||||
readonly property color tileHover: Qt.rgba(1, 1, 1, 0.05)
|
||||
readonly property color iconWell: Qt.rgba(1, 1, 1, 0.05)
|
||||
readonly property color iconWellHighlight: Qt.rgba(1, 1, 1, 0.06)
|
||||
|
||||
Reference in New Issue
Block a user