forked from Shinonome/dots-hyprland
Merge branch 'main' into all-tooltips
This commit is contained in:
@@ -26,13 +26,13 @@ Variants {
|
||||
required property var modelData
|
||||
|
||||
// Hide when fullscreen
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
property bool focusingThisMonitor: HyprlandData.activeWorkspace?.monitor == monitor.name
|
||||
visible: !(activeWindow?.fullscreen && activeWindow?.activated && focusingThisMonitor)
|
||||
property list<HyprlandWorkspace> workspacesForMonitor: Hyprland.workspaces.values.filter(workspace=>workspace.monitor && workspace.monitor.name == monitor.name)
|
||||
property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace=>((workspace.toplevels.values.filter(window=>window.wayland.fullscreen)[0] != undefined) && workspace.active))[0]
|
||||
visible: !(activeWorkspaceWithFullscreen != undefined)
|
||||
|
||||
// Workspaces
|
||||
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
||||
property list<var> relevantWindows: HyprlandData.windowList.filter(win => win.monitor == monitor.id && win.workspace.id >= 0).sort((a, b) => a.workspace.id - b.workspace.id)
|
||||
property list<var> relevantWindows: HyprlandData.windowList.filter(win => win.monitor == monitor?.id && win.workspace.id >= 0).sort((a, b) => a.workspace.id - b.workspace.id)
|
||||
property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1
|
||||
property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10
|
||||
// Wallpaper
|
||||
@@ -148,8 +148,13 @@ Variants {
|
||||
// Wallpaper
|
||||
Image {
|
||||
id: wallpaper
|
||||
visible: !bgRoot.wallpaperIsVideo
|
||||
visible: opacity > 0
|
||||
opacity: (status === Image.Ready && !bgRoot.wallpaperIsVideo) ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
property real value // 0 to 1, for offset
|
||||
asynchronous: true
|
||||
value: {
|
||||
// Range = groups that workspaces span on
|
||||
const chunkSize = Config?.options.bar.workspaces.shown ?? 10;
|
||||
@@ -278,8 +283,8 @@ Variants {
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
text: "Enter password"
|
||||
color: CF.ColorUtils.transparentize(bgRoot.colText, 0.3)
|
||||
text: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password")
|
||||
color: GlobalStates.screenUnlockFailed ? Appearance.colors.colError : bgRoot.colText
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
text: root.focusingThisMonitor && root.activeWindow?.activated && root.biggestWindow ?
|
||||
root.activeWindow?.title :
|
||||
(root.biggestWindow?.title) ?? `${Translation.tr("Workspace")} ${monitor?.activeWorkspace?.id}`
|
||||
(root.biggestWindow?.title) ?? `${Translation.tr("Workspace")} ${monitor?.activeWorkspace?.id ?? 1}`
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,8 +39,30 @@ Scope {
|
||||
property real useShortenedForm: (Appearance.sizes.barHellaShortenScreenWidthThreshold >= screen.width) ? 2 : (Appearance.sizes.barShortenScreenWidthThreshold >= screen.width) ? 1 : 0
|
||||
readonly property int centerSideModuleWidth: (useShortenedForm == 2) ? Appearance.sizes.barCenterSideModuleWidthHellaShortened : (useShortenedForm == 1) ? Appearance.sizes.barCenterSideModuleWidthShortened : Appearance.sizes.barCenterSideModuleWidth
|
||||
|
||||
Timer {
|
||||
id: showBarTimer
|
||||
interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100)
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
barRoot.superShow = true
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onSuperDownChanged() {
|
||||
if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) return;
|
||||
if (GlobalStates.superDown) showBarTimer.restart();
|
||||
else {
|
||||
showBarTimer.stop();
|
||||
barRoot.superShow = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
property bool superShow: false
|
||||
property bool mustShow: hoverRegion.containsMouse || superShow
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
exclusiveZone: Appearance.sizes.baseBarHeight + (Config.options.bar.cornerStyle === 1 ? Appearance.sizes.hyprlandGapsOut : 0)
|
||||
exclusiveZone: (Config?.options.bar.autoHide.enable && (!mustShow || !Config?.options.bar.autoHide.pushWindows)) ? 0 :
|
||||
Appearance.sizes.baseBarHeight + (Config.options.bar.cornerStyle === 1 ? Appearance.sizes.hyprlandGapsOut : 0)
|
||||
WlrLayershell.namespace: "quickshell:bar"
|
||||
implicitHeight: Appearance.sizes.barHeight + Appearance.rounding.screenRounding
|
||||
mask: Region {
|
||||
@@ -55,90 +77,116 @@ Scope {
|
||||
right: true
|
||||
}
|
||||
|
||||
BarContent {
|
||||
id: barContent
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: undefined
|
||||
}
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
MouseArea {
|
||||
id: hoverRegion
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
AnchorChanges {
|
||||
target: barContent
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: undefined
|
||||
bottom: parent.bottom
|
||||
BarContent {
|
||||
id: barContent
|
||||
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: undefined
|
||||
topMargin: (Config?.options.bar.autoHide.enable && !mustShow) ? -Appearance.sizes.barHeight + 1 : 0
|
||||
bottomMargin: 0
|
||||
}
|
||||
Behavior on anchors.topMargin {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on anchors.bottomMargin {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
AnchorChanges {
|
||||
target: barContent
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: undefined
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
target: barContent
|
||||
anchors.topMargin: 0
|
||||
anchors.bottomMargin: (Config?.options.bar.autoHide.enable && !mustShow) ? -Appearance.sizes.barHeight + 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Round decorators
|
||||
Loader {
|
||||
id: roundDecorators
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
y: Appearance.sizes.barHeight
|
||||
width: parent.width
|
||||
height: Appearance.rounding.screenRounding
|
||||
active: showBarBackground && Config.options.bar.cornerStyle === 0 // Hug
|
||||
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
roundDecorators.y: 0
|
||||
// Round decorators
|
||||
Loader {
|
||||
id: roundDecorators
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: barContent.bottom
|
||||
bottom: undefined
|
||||
}
|
||||
}
|
||||
width: parent.width
|
||||
height: Appearance.rounding.screenRounding
|
||||
active: showBarBackground && Config.options.bar.cornerStyle === 0 // Hug
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: Appearance.rounding.screenRounding
|
||||
RoundCorner {
|
||||
id: leftCorner
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
implicitSize: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopLeft
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
leftCorner.corner: RoundCorner.CornerEnum.BottomLeft
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
AnchorChanges {
|
||||
target: roundDecorators
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: undefined
|
||||
bottom: barContent.top
|
||||
}
|
||||
}
|
||||
}
|
||||
RoundCorner {
|
||||
id: rightCorner
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: !Config.options.bar.bottom ? parent.top : undefined
|
||||
bottom: Config.options.bar.bottom ? parent.bottom : undefined
|
||||
}
|
||||
implicitSize: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopRight
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
rightCorner.corner: RoundCorner.CornerEnum.BottomRight
|
||||
sourceComponent: Item {
|
||||
implicitHeight: Appearance.rounding.screenRounding
|
||||
RoundCorner {
|
||||
id: leftCorner
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
implicitSize: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopLeft
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
leftCorner.corner: RoundCorner.CornerEnum.BottomLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
RoundCorner {
|
||||
id: rightCorner
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: !Config.options.bar.bottom ? parent.top : undefined
|
||||
bottom: Config.options.bar.bottom ? parent.bottom : undefined
|
||||
}
|
||||
implicitSize: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopRight
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
rightCorner.corner: RoundCorner.CornerEnum.BottomRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,9 +129,9 @@ Item {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 0
|
||||
text: switch(PowerProfiles.profile) {
|
||||
case PowerProfile.PowerSaver: return "battery_saver"
|
||||
case PowerProfile.Balanced: return "dynamic_form"
|
||||
case PowerProfile.Performance: return "speed"
|
||||
case PowerProfile.PowerSaver: return "energy_savings_leaf"
|
||||
case PowerProfile.Balanced: return "settings_slow_motion"
|
||||
case PowerProfile.Performance: return "local_fire_department"
|
||||
}
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer2
|
||||
|
||||
@@ -28,6 +28,30 @@ Item {
|
||||
property real workspaceIconMarginShrinked: -4
|
||||
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown
|
||||
|
||||
property bool showNumbers: false
|
||||
Timer {
|
||||
id: showNumbersTimer
|
||||
interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100)
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.showNumbers = true
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onSuperDownChanged() {
|
||||
if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) return;
|
||||
if (GlobalStates.superDown) showNumbersTimer.restart();
|
||||
else {
|
||||
showNumbersTimer.stop();
|
||||
root.showNumbers = false;
|
||||
}
|
||||
}
|
||||
function onSuperReleaseMightTriggerChanged() {
|
||||
showNumbersTimer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update workspaceOccupied
|
||||
function updateWorkspaceOccupied() {
|
||||
workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => {
|
||||
@@ -176,9 +200,9 @@ Item {
|
||||
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
|
||||
|
||||
StyledText { // Workspace number text
|
||||
opacity: GlobalStates.workspaceShowNumbers
|
||||
|| ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || GlobalStates.workspaceShowNumbers))
|
||||
|| (GlobalStates.workspaceShowNumbers && !Config.options?.bar.workspaces.showAppIcons)
|
||||
opacity: root.showNumbers
|
||||
|| ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || root.showNumbers))
|
||||
|| (root.showNumbers && !Config.options?.bar.workspaces.showAppIcons)
|
||||
) ? 1 : 0
|
||||
z: 3
|
||||
|
||||
@@ -200,7 +224,7 @@ Item {
|
||||
Rectangle { // Dot instead of ws number
|
||||
id: wsDot
|
||||
opacity: (Config.options?.bar.workspaces.alwaysShowNumbers
|
||||
|| GlobalStates.workspaceShowNumbers
|
||||
|| root.showNumbers
|
||||
|| (Config.options?.bar.workspaces.showAppIcons && workspaceButtonBackground.biggestWindow)
|
||||
) ? 0 : 1
|
||||
visible: opacity > 0
|
||||
@@ -222,20 +246,20 @@ Item {
|
||||
width: workspaceButtonWidth
|
||||
height: workspaceButtonWidth
|
||||
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
|
||||
(workspaceButtonBackground.biggestWindow && !GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(workspaceButtonBackground.biggestWindow && !root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
|
||||
visible: opacity > 0
|
||||
IconImage {
|
||||
id: mainAppIcon
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: (!GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
anchors.bottomMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
anchors.rightMargin: (!GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
anchors.rightMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
|
||||
source: workspaceButtonBackground.mainAppIconSource
|
||||
implicitSize: (!GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
|
||||
implicitSize: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
|
||||
@@ -31,7 +31,7 @@ MouseArea {
|
||||
visible: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: Weather.data.temp
|
||||
text: Weather.data?.temp ?? "--°"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,26 @@ Singleton {
|
||||
property QtObject sizes
|
||||
property string syntaxHighlightingTheme
|
||||
|
||||
// Extremely conservative transparency values for consistency and readability
|
||||
property real transparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0.07) : 0
|
||||
property real contentTransparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0.55) : 0
|
||||
// Transparency. The quadratic functions were derived from analysis of hand-picked transparency values.
|
||||
ColorQuantizer {
|
||||
id: wallColorQuant
|
||||
source: Qt.resolvedUrl(Config.options.background.wallpaperPath)
|
||||
depth: 0 // 2^0 = 1 color
|
||||
rescaleSize: 10
|
||||
}
|
||||
property real wallpaperVibrancy: (wallColorQuant.colors[0]?.hslSaturation + wallColorQuant.colors[0]?.hslLightness) / 2
|
||||
property real autoBackgroundTransparency: { // y = 0.5768x^2 - 0.759x + 0.2896
|
||||
let x = wallpaperVibrancy
|
||||
let y = 0.5768 * (x * x) - 0.759 * (x) + 0.2896
|
||||
return Math.max(0, Math.min(0.22, y))
|
||||
}
|
||||
property real autoContentTransparency: { // y = -10.1734x^2 + 3.4457x + 0.1872
|
||||
let x = autoBackgroundTransparency
|
||||
let y = -10.1734 * (x * x) + 3.4457 * (x) + 0.1872
|
||||
return Math.max(0, Math.min(0.6, y))
|
||||
}
|
||||
property real backgroundTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoBackgroundTransparency : Config?.options.appearance.transparency.backgroundTransparency : 0
|
||||
property real contentTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency : 0
|
||||
|
||||
m3colors: QtObject {
|
||||
property bool darkmode: false
|
||||
@@ -100,26 +117,30 @@ Singleton {
|
||||
|
||||
colors: QtObject {
|
||||
property color colSubtext: m3colors.m3outline
|
||||
property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.transparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1)
|
||||
property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.backgroundTransparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1)
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency))
|
||||
property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency))
|
||||
property color colLayer0Border: ColorUtils.mix(root.m3colors.m3outlineVariant, colLayer0, 0.4)
|
||||
property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colLayer1: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency);
|
||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
|
||||
property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.1), root.contentTransparency)
|
||||
property color colLayer2: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
||||
property color colOnLayer2: m3colors.m3onSurface;
|
||||
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
property color colLayer3: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96), root.contentTransparency)
|
||||
property color colOnLayer3: m3colors.m3onSurface;
|
||||
property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency)
|
||||
property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency);
|
||||
property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency)
|
||||
property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency);
|
||||
property color colLayer2Disabled: ColorUtils.transparentize(ColorUtils.mix(colLayer2, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colLayer3: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency)
|
||||
property color colOnLayer3: m3colors.m3onSurface;
|
||||
property color colLayer3Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.90), root.contentTransparency)
|
||||
property color colLayer3Active: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.80), root.contentTransparency);
|
||||
property color colLayer4: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency)
|
||||
property color colOnLayer4: m3colors.m3onSurface;
|
||||
property color colLayer4Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.90), root.contentTransparency)
|
||||
property color colLayer4Active: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.80), root.contentTransparency);
|
||||
property color colPrimary: m3colors.m3primary
|
||||
property color colOnPrimary: m3colors.m3onPrimary
|
||||
property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87)
|
||||
@@ -148,6 +169,14 @@ Singleton {
|
||||
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
|
||||
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7)
|
||||
property color colOutlineVariant: m3colors.m3outlineVariant
|
||||
property color colError: m3colors.m3error
|
||||
property color colErrorHover: ColorUtils.mix(m3colors.m3error, colLayer1Hover, 0.85)
|
||||
property color colErrorActive: ColorUtils.mix(m3colors.m3error, colLayer1Active, 0.7)
|
||||
property color colOnError: m3colors.m3onError
|
||||
property color colErrorContainer: m3colors.m3errorContainer
|
||||
property color colErrorContainerHover: ColorUtils.mix(m3colors.m3errorContainer, m3colors.m3onErrorContainer, 0.90)
|
||||
property color colErrorContainerActive: ColorUtils.mix(m3colors.m3errorContainer, m3colors.m3onErrorContainer, 0.70)
|
||||
property color colOnErrorContainer: m3colors.m3onErrorContainer
|
||||
}
|
||||
|
||||
rounding: QtObject {
|
||||
@@ -268,7 +297,6 @@ Singleton {
|
||||
easing.bezierCurve: root.animation.elementMoveFast.bezierCurve
|
||||
}}
|
||||
}
|
||||
|
||||
property QtObject clickBounce: QtObject {
|
||||
property int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
@@ -281,7 +309,7 @@ Singleton {
|
||||
}}
|
||||
}
|
||||
property QtObject scroll: QtObject {
|
||||
property int duration: 400
|
||||
property int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.standardDecel
|
||||
}
|
||||
|
||||
@@ -81,7 +81,12 @@ Singleton {
|
||||
property JsonObject appearance: JsonObject {
|
||||
property bool extraBackgroundTint: true
|
||||
property int fakeScreenRounding: 2 // 0: None | 1: Always | 2: When not fullscreen
|
||||
property bool transparency: false
|
||||
property JsonObject transparency: JsonObject {
|
||||
property bool enable: true
|
||||
property bool automatic: true
|
||||
property real backgroundTransparency: 0.11
|
||||
property real contentTransparency: 0.57
|
||||
}
|
||||
property JsonObject wallpaperTheming: JsonObject {
|
||||
property bool enableAppsAndShell: true
|
||||
property bool enableQtApps: true
|
||||
@@ -124,6 +129,14 @@ Singleton {
|
||||
}
|
||||
|
||||
property JsonObject bar: JsonObject {
|
||||
property JsonObject autoHide: JsonObject {
|
||||
property bool enable: false
|
||||
property bool pushWindows: false
|
||||
property JsonObject showWhenPressingSuper: JsonObject {
|
||||
property bool enable: true
|
||||
property int delay: 140
|
||||
}
|
||||
}
|
||||
property bool bottom: false // Instead of top
|
||||
property int cornerStyle: 0 // 0: Hug | 1: Float | 2: Plain rectangle
|
||||
property bool borderless: false // true for no grouping of items
|
||||
@@ -181,6 +194,15 @@ Singleton {
|
||||
property list<string> ignoredAppRegexes: []
|
||||
}
|
||||
|
||||
property JsonObject interactions: JsonObject {
|
||||
property JsonObject scrolling: JsonObject {
|
||||
property bool fasterTouchpadScroll: true // Enable faster scrolling with touchpad
|
||||
property int mouseScrollDeltaThreshold: 120 // delta >= this then it gets detected as mouse scroll rather than touchpad
|
||||
property int mouseScrollFactor: 120
|
||||
property int touchpadScrollFactor: 450
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject language: JsonObject {
|
||||
property JsonObject translator: JsonObject {
|
||||
property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google
|
||||
@@ -198,6 +220,11 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject media: JsonObject {
|
||||
// Attempt to remove dupes (the aggregator playerctl one and browsers' native ones when there's plasma browser integration)
|
||||
property bool filterDuplicatePlayers: true
|
||||
}
|
||||
|
||||
property JsonObject networking: JsonObject {
|
||||
property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
||||
}
|
||||
@@ -253,6 +280,13 @@ Singleton {
|
||||
// https://doc.qt.io/qt-6/qtime.html#toString
|
||||
property string format: "hh:mm"
|
||||
property string dateFormat: "ddd, dd/MM"
|
||||
property JsonObject pomodoro: JsonObject {
|
||||
property string alertSound: ""
|
||||
property int breakTime: 300
|
||||
property int cyclesBeforeLongBreak: 4
|
||||
property int focus: 1500
|
||||
property int longBreak: 900
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject windows: JsonObject {
|
||||
|
||||
@@ -11,18 +11,35 @@ Singleton {
|
||||
property string fileName: "states.json"
|
||||
property string filePath: `${root.fileDir}/${root.fileName}`
|
||||
|
||||
Timer {
|
||||
id: fileReloadTimer
|
||||
interval: 100
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
persistentStatesFileView.reload()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: fileWriteTimer
|
||||
interval: 100
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
persistentStatesFileView.writeAdapter()
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: persistentStatesFileView
|
||||
path: root.filePath
|
||||
|
||||
watchChanges: true
|
||||
onFileChanged: reload()
|
||||
onAdapterUpdated: {
|
||||
writeAdapter()
|
||||
}
|
||||
onFileChanged: fileReloadTimer.restart()
|
||||
onAdapterUpdated: fileWriteTimer.restart()
|
||||
onLoadFailed: error => {
|
||||
console.log("Failed to load persistent states file:", error);
|
||||
if (error == FileViewError.FileNotFound) {
|
||||
writeAdapter();
|
||||
fileWriteTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +61,20 @@ Singleton {
|
||||
property bool allowNsfw: false
|
||||
property string provider: "yandere"
|
||||
}
|
||||
|
||||
property JsonObject timer: JsonObject {
|
||||
property JsonObject pomodoro: JsonObject {
|
||||
property bool running: false
|
||||
property int start: 0
|
||||
property bool isBreak: false
|
||||
property int cycle: 0
|
||||
}
|
||||
property JsonObject stopwatch: JsonObject {
|
||||
property bool running: false
|
||||
property int start: 0
|
||||
property list<var> laps: []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import QtQuick.Layouts
|
||||
Rectangle {
|
||||
id: root
|
||||
default property alias data: rowLayout.data
|
||||
property alias uniformCellSizes: rowLayout.uniformCellSizes
|
||||
property real spacing: 5
|
||||
property real padding: 0
|
||||
property int clickIndex: rowLayout.clickIndex
|
||||
|
||||
@@ -12,9 +12,9 @@ RippleButton {
|
||||
leftPadding: 15
|
||||
rightPadding: 15
|
||||
buttonRadius: Appearance.rounding.small
|
||||
colBackground: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainer : Appearance.colors.colSurfaceContainerHighest
|
||||
colBackgroundHover: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSurfaceContainerHighestHover
|
||||
colRipple: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colSurfaceContainerHighestActive
|
||||
colBackground: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainer : Appearance.colors.colLayer4
|
||||
colBackgroundHover: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colLayer4Hover
|
||||
colRipple: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colLayer4Active
|
||||
|
||||
contentItem: StyledText {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
@@ -13,9 +13,9 @@ Rectangle { // App icon
|
||||
property var urgency: NotificationUrgency.Normal
|
||||
property var image: ""
|
||||
property real scale: 1
|
||||
property real size: 45 * scale
|
||||
property real size: 38 * scale
|
||||
property real materialIconScale: 0.57
|
||||
property real appIconScale: 0.7
|
||||
property real appIconScale: 0.8
|
||||
property real smallAppIconScale: 0.49
|
||||
property real materialIconSize: size * materialIconScale
|
||||
property real appIconSize: size * appIconScale
|
||||
|
||||
@@ -105,7 +105,7 @@ Item { // Notification group area
|
||||
id: background
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
color: Appearance.colors.colSurfaceContainer
|
||||
color: Appearance.colors.colLayer2
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.leftMargin: root.xOffset
|
||||
|
||||
|
||||
@@ -140,8 +140,8 @@ Item { // Notification item area
|
||||
color: (expanded && !onlyNotification) ?
|
||||
(notificationObject.urgency == NotificationUrgency.Critical) ?
|
||||
ColorUtils.mix(Appearance.colors.colSecondaryContainer, Appearance.colors.colLayer2, 0.35) :
|
||||
(Appearance.colors.colSurfaceContainerHigh) :
|
||||
ColorUtils.transparentize(Appearance.colors.colSurfaceContainerHighest)
|
||||
(Appearance.colors.colLayer3) :
|
||||
ColorUtils.transparentize(Appearance.colors.colLayer3)
|
||||
|
||||
implicitHeight: expanded ? (contentColumn.implicitHeight + padding * 2) : summaryRow.implicitHeight
|
||||
Behavior on implicitHeight {
|
||||
@@ -168,7 +168,7 @@ Item { // Notification item area
|
||||
id: summaryText
|
||||
visible: !root.onlyNotification
|
||||
font.pixelSize: root.fontSize
|
||||
color: Appearance.colors.colOnLayer2
|
||||
color: Appearance.colors.colOnLayer3
|
||||
elide: Text.ElideRight
|
||||
text: root.notificationObject.summary || ""
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ TabButton {
|
||||
|
||||
RippleAnim {
|
||||
id: rippleFadeAnim
|
||||
duration: rippleDuration * 2
|
||||
target: ripple
|
||||
property: "opacity"
|
||||
to: 0
|
||||
|
||||
@@ -29,6 +29,7 @@ Button {
|
||||
property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2"
|
||||
property color colRippleToggled: Appearance?.colors.colPrimaryActive ?? "#D6CEE2"
|
||||
|
||||
opacity: root.enabled ? 1 : 0.4
|
||||
property color buttonColor: root.enabled ? (root.toggled ?
|
||||
(root.hovered ? colBackgroundToggledHover :
|
||||
colBackgroundToggled) :
|
||||
@@ -91,6 +92,7 @@ Button {
|
||||
|
||||
RippleAnim {
|
||||
id: rippleFadeAnim
|
||||
duration: rippleDuration * 2
|
||||
target: ripple
|
||||
property: "opacity"
|
||||
to: 0
|
||||
|
||||
@@ -51,6 +51,7 @@ TabButton {
|
||||
|
||||
RippleAnim {
|
||||
id: rippleFadeAnim
|
||||
duration: rippleDuration * 2
|
||||
target: ripple
|
||||
property: "opacity"
|
||||
to: 0
|
||||
@@ -106,13 +107,29 @@ TabButton {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: ripple
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
color: root.colRipple
|
||||
width: ripple.implicitWidth
|
||||
height: ripple.implicitHeight
|
||||
opacity: 0
|
||||
|
||||
property real implicitWidth: 0
|
||||
property real implicitHeight: 0
|
||||
visible: width > 0 && height > 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: root.colRipple }
|
||||
GradientStop { position: 0.3; color: root.colRipple }
|
||||
GradientStop { position: 0.5 ; color: Qt.rgba(root.colRipple.r, root.colRipple.g, root.colRipple.b, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
transform: Translate {
|
||||
x: -ripple.width / 2
|
||||
y: -ripple.height / 2
|
||||
|
||||
@@ -63,7 +63,7 @@ Item {
|
||||
Layout.rightMargin: dialogPadding
|
||||
}
|
||||
|
||||
ListView {
|
||||
StyledListView {
|
||||
id: choiceListView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
@@ -71,9 +71,6 @@ Item {
|
||||
currentIndex: root.defaultChoice !== undefined ? root.items.indexOf(root.defaultChoice) : -1
|
||||
spacing: 6
|
||||
|
||||
maximumFlickVelocity: 3500
|
||||
boundsBehavior: Flickable.DragOverBounds
|
||||
|
||||
model: ScriptModel {
|
||||
id: choiceModel
|
||||
}
|
||||
|
||||
@@ -1,6 +1,50 @@
|
||||
import QtQuick
|
||||
import qs.modules.common
|
||||
|
||||
Flickable {
|
||||
id: root
|
||||
maximumFlickVelocity: 3500
|
||||
boundsBehavior: Flickable.DragOverBounds
|
||||
|
||||
property real touchpadScrollFactor: Config?.options.interactions.scrolling.touchpadScrollFactor ?? 100
|
||||
property real mouseScrollFactor: Config?.options.interactions.scrolling.mouseScrollFactor ?? 50
|
||||
property real mouseScrollDeltaThreshold: Config?.options.interactions.scrolling.mouseScrollDeltaThreshold ?? 120
|
||||
// Accumulated scroll destination so wheel deltas stack while animating
|
||||
property real scrollTargetY: 0
|
||||
|
||||
MouseArea {
|
||||
visible: Config?.options.interactions.scrolling.fasterTouchpadScroll
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: function(wheelEvent) {
|
||||
const delta = wheelEvent.angleDelta.y / root.mouseScrollDeltaThreshold;
|
||||
// The angleDelta.y of a touchpad is usually small and continuous,
|
||||
// while that of a mouse wheel is typically in multiples of ±120.
|
||||
var scrollFactor = Math.abs(wheelEvent.angleDelta.y) >= root.mouseScrollDeltaThreshold ? root.mouseScrollFactor : root.touchpadScrollFactor;
|
||||
|
||||
const maxY = Math.max(0, root.contentHeight - root.height);
|
||||
const base = scrollAnim.running ? root.scrollTargetY : root.contentY;
|
||||
var targetY = Math.max(0, Math.min(base - delta * scrollFactor, maxY));
|
||||
|
||||
root.scrollTargetY = targetY;
|
||||
root.contentY = targetY;
|
||||
wheelEvent.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on contentY {
|
||||
NumberAnimation {
|
||||
id: scrollAnim
|
||||
duration: Appearance.animation.scroll.duration
|
||||
easing.type: Appearance.animation.scroll.type
|
||||
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
// Keep target synced when not animating (e.g., drag/flick or programmatic changes)
|
||||
onContentYChanged: {
|
||||
if (!scrollAnim.running) {
|
||||
root.scrollTargetY = root.contentY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ ListView {
|
||||
property int dragIndex: -1
|
||||
property real dragDistance: 0
|
||||
property bool popin: true
|
||||
// Accumulated scroll destination so wheel deltas stack while animating
|
||||
property real scrollTargetY: 0
|
||||
|
||||
property real touchpadScrollFactor: Config?.options.interactions.scrolling.touchpadScrollFactor ?? 100
|
||||
property real mouseScrollFactor: Config?.options.interactions.scrolling.mouseScrollFactor ?? 50
|
||||
property real mouseScrollDeltaThreshold: Config?.options.interactions.scrolling.mouseScrollDeltaThreshold ?? 120
|
||||
|
||||
function resetDrag() {
|
||||
root.dragIndex = -1
|
||||
@@ -23,6 +29,42 @@ ListView {
|
||||
maximumFlickVelocity: 3500
|
||||
boundsBehavior: Flickable.DragOverBounds
|
||||
|
||||
MouseArea {
|
||||
visible: Config?.options.interactions.scrolling.fasterTouchpadScroll
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: function(wheelEvent) {
|
||||
const delta = wheelEvent.angleDelta.y / root.mouseScrollDeltaThreshold;
|
||||
// The angleDelta.y of a touchpad is usually small and continuous,
|
||||
// while that of a mouse wheel is typically in multiples of ±120.
|
||||
var scrollFactor = Math.abs(wheelEvent.angleDelta.y) >= root.mouseScrollDeltaThreshold ? root.mouseScrollFactor : root.touchpadScrollFactor;
|
||||
|
||||
const maxY = Math.max(0, root.contentHeight - root.height);
|
||||
const base = scrollAnim.running ? root.scrollTargetY : root.contentY;
|
||||
var targetY = Math.max(0, Math.min(base - delta * scrollFactor, maxY));
|
||||
|
||||
root.scrollTargetY = targetY;
|
||||
root.contentY = targetY;
|
||||
wheelEvent.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on contentY {
|
||||
NumberAnimation {
|
||||
id: scrollAnim
|
||||
duration: Appearance.animation.scroll.duration
|
||||
easing.type: Appearance.animation.scroll.type
|
||||
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
// Keep target synced when not animating (e.g., drag/flick or programmatic changes)
|
||||
onContentYChanged: {
|
||||
if (!scrollAnim.running) {
|
||||
root.scrollTargetY = root.contentY;
|
||||
}
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
|
||||
@@ -19,6 +19,9 @@ Scope {
|
||||
// Unlock the screen before exiting, or the compositor will display a
|
||||
// fallback lock you can't interact with.
|
||||
GlobalStates.screenLocked = false;
|
||||
|
||||
// Refocus last focused window on unlock (hack)
|
||||
Quickshell.execDetached(["bash", "-c", `sleep 0.2; hyprctl --batch "dispatch togglespecialworkspace; dispatch togglespecialworkspace"`])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@ Scope {
|
||||
}
|
||||
|
||||
onCurrentTextChanged: {
|
||||
showFailure = false; // Clear the failure text once the user starts typing.
|
||||
if (currentText.length > 0) {
|
||||
showFailure = false;
|
||||
GlobalStates.screenUnlockFailed = false;
|
||||
}
|
||||
GlobalStates.screenLockContainsCharacters = currentText.length > 0;
|
||||
passwordClearTimer.restart();
|
||||
}
|
||||
@@ -58,6 +61,7 @@ Scope {
|
||||
root.unlocked();
|
||||
} else {
|
||||
root.showFailure = true;
|
||||
GlobalStates.screenUnlockFailed = true;
|
||||
}
|
||||
|
||||
root.currentText = "";
|
||||
|
||||
@@ -75,17 +75,10 @@ MouseArea {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.colors.colLayer2
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
implicitWidth: 160
|
||||
implicitHeight: 44
|
||||
|
||||
StyledText {
|
||||
visible: root.context.showFailure && passwordBox.text.length == 0
|
||||
anchors.centerIn: parent
|
||||
text: "Incorrect"
|
||||
color: Appearance.m3colors.m3error
|
||||
}
|
||||
|
||||
StyledTextInput {
|
||||
id: passwordBox
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Scope {
|
||||
property real artRounding: Appearance.rounding.verysmall
|
||||
property list<real> visualizerPoints: []
|
||||
|
||||
property bool hasPlasmaIntegration: true
|
||||
property bool hasPlasmaIntegration: false
|
||||
Process {
|
||||
id: plasmaIntegrationAvailabilityCheckProc
|
||||
running: true
|
||||
@@ -35,7 +35,9 @@ Scope {
|
||||
}
|
||||
}
|
||||
function isRealPlayer(player) {
|
||||
// return true
|
||||
if (!Config.options.media.filterDuplicatePlayers) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
// Remove unecessary native buses from browsers if there's plasma integration
|
||||
!(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||
|
||||
@@ -21,7 +21,7 @@ Scope {
|
||||
required property var modelData
|
||||
property string searchingText: ""
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
||||
screen: modelData
|
||||
visible: GlobalStates.overviewOpen
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Item {
|
||||
property var windows: HyprlandData.windowList
|
||||
property var windowByAddress: HyprlandData.windowByAddress
|
||||
property var windowAddresses: HyprlandData.addresses
|
||||
property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id)
|
||||
property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor?.id)
|
||||
property real scale: Config.options.overview.scale
|
||||
property color activeBorderColor: Appearance.colors.colSecondary
|
||||
|
||||
@@ -149,14 +149,15 @@ Item {
|
||||
const address = `0x${toplevel.HyprlandToplevel.address}`
|
||||
var win = windowByAddress[address]
|
||||
const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown)
|
||||
const inMonitor = root.monitor.id === win.monitor
|
||||
return inWorkspaceGroup && inMonitor;
|
||||
return inWorkspaceGroup;
|
||||
})
|
||||
}
|
||||
}
|
||||
delegate: OverviewWindow {
|
||||
id: window
|
||||
required property var modelData
|
||||
property int monitorId: windowData?.monitor
|
||||
property var monitor: HyprlandData.monitors[monitorId]
|
||||
property var address: `0x${modelData.HyprlandToplevel.address}`
|
||||
windowData: windowByAddress[address]
|
||||
toplevel: modelData
|
||||
@@ -164,9 +165,7 @@ Item {
|
||||
scale: root.scale
|
||||
availableWorkspaceWidth: root.workspaceImplicitWidth
|
||||
availableWorkspaceHeight: root.workspaceImplicitHeight
|
||||
|
||||
property int monitorId: windowData?.monitor
|
||||
property var monitor: HyprlandData.monitors[monitorId]
|
||||
widgetMonitorId: root.monitor.id
|
||||
|
||||
property bool atInitPosition: (initX == x && initY == y)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
@@ -22,6 +21,7 @@ Item { // Window
|
||||
property real initY: Math.max((windowData?.at[1] - (monitorData?.y ?? 0) - monitorData?.reserved[1]) * root.scale, 0) + yOffset
|
||||
property real xOffset: 0
|
||||
property real yOffset: 0
|
||||
property int widgetMonitorId: 0
|
||||
|
||||
property var targetWindowWidth: windowData?.size[0] * scale
|
||||
property var targetWindowHeight: windowData?.size[1] * scale
|
||||
@@ -40,6 +40,7 @@ Item { // Window
|
||||
y: initY
|
||||
width: windowData?.size[0] * root.scale
|
||||
height: windowData?.size[1] * root.scale
|
||||
opacity: windowData.monitor == widgetMonitorId ? 1 : 0.4
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
@@ -69,6 +70,7 @@ Item { // Window
|
||||
captureSource: GlobalStates.overviewOpen ? root.toplevel : null
|
||||
live: true
|
||||
|
||||
// Color overlay for interactions
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
|
||||
@@ -13,7 +13,8 @@ Scope {
|
||||
|
||||
component CornerPanelWindow: PanelWindow {
|
||||
id: cornerPanelWindow
|
||||
visible: (Config.options.appearance.fakeScreenRounding === 1 || (Config.options.appearance.fakeScreenRounding === 2 && !activeWindow?.fullscreen))
|
||||
property bool fullscreen
|
||||
visible: (Config.options.appearance.fakeScreenRounding === 1 || (Config.options.appearance.fakeScreenRounding === 2 && !fullscreen))
|
||||
property var corner
|
||||
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
@@ -44,22 +45,34 @@ Scope {
|
||||
model: Quickshell.screens
|
||||
|
||||
Scope {
|
||||
id: monitorScope
|
||||
required property var modelData
|
||||
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
||||
|
||||
// Hide when fullscreen
|
||||
property list<HyprlandWorkspace> workspacesForMonitor: Hyprland.workspaces.values.filter(workspace=>workspace.monitor && workspace.monitor.name == monitor.name)
|
||||
property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace=>((workspace.toplevels.values.filter(window=>window.wayland.fullscreen)[0] != undefined) && workspace.active))[0]
|
||||
property bool fullscreen: activeWorkspaceWithFullscreen != undefined
|
||||
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.TopLeft
|
||||
fullscreen: monitorScope.fullscreen
|
||||
}
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.TopRight
|
||||
fullscreen: monitorScope.fullscreen
|
||||
}
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.BottomLeft
|
||||
fullscreen: monitorScope.fullscreen
|
||||
}
|
||||
CornerPanelWindow {
|
||||
screen: modelData
|
||||
corner: RoundCorner.CornerEnum.BottomRight
|
||||
fullscreen: monitorScope.fullscreen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,23 @@ ContentPage {
|
||||
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Overall appearance")
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Automatically hide")
|
||||
checked: Config.options.bar.autoHide.enable
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.autoHide.enable = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Place at the bottom")
|
||||
checked: Config.options.bar.bottom
|
||||
onCheckedChanged: {
|
||||
Config.options.bar.bottom = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
|
||||
@@ -140,9 +140,9 @@ ContentPage {
|
||||
ConfigRow {
|
||||
ConfigSwitch {
|
||||
text: Translation.tr("Enable")
|
||||
checked: Config.options.appearance.transparency
|
||||
checked: Config.options.appearance.transparency.enable
|
||||
onCheckedChanged: {
|
||||
Config.options.appearance.transparency = checked;
|
||||
Config.options.appearance.transparency.enable = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
content: Translation.tr("Might look ass. Unsupported.")
|
||||
|
||||
@@ -282,6 +282,9 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
spacing: 10
|
||||
popin: false
|
||||
|
||||
touchpadScrollFactor: Config.options.interactions.scrolling.touchpadScrollFactor * 1.4
|
||||
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
|
||||
|
||||
property int lastResponseLength: 0
|
||||
|
||||
clip: true
|
||||
@@ -296,15 +299,6 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
|
||||
add: null // Prevent function calls from being janky
|
||||
|
||||
Behavior on contentY {
|
||||
NumberAnimation {
|
||||
id: scrollAnim
|
||||
duration: Appearance.animation.scroll.duration
|
||||
easing.type: Appearance.animation.scroll.type
|
||||
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: Ai.messageIDs.filter(id => {
|
||||
const message = Ai.messageByID[id];
|
||||
|
||||
@@ -137,6 +137,9 @@ Item {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
touchpadScrollFactor: Config.options.interactions.scrolling.touchpadScrollFactor * 1.4
|
||||
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
|
||||
|
||||
property int lastResponseLength: 0
|
||||
|
||||
clip: true
|
||||
@@ -149,15 +152,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on contentY {
|
||||
NumberAnimation {
|
||||
id: scrollAnim
|
||||
duration: Appearance.animation.scroll.duration
|
||||
easing.type: Appearance.animation.scroll.type
|
||||
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
if(root.responses.length > booruResponseListView.lastResponseLength) {
|
||||
|
||||
@@ -15,7 +15,7 @@ Button {
|
||||
id: root
|
||||
property var imageData
|
||||
property var rowHeight
|
||||
property bool manualDownload: true
|
||||
property bool manualDownload: false
|
||||
property string previewDownloadPath
|
||||
property string downloadPath
|
||||
property string nsfwPath
|
||||
@@ -63,13 +63,17 @@ Button {
|
||||
anchors.fill: parent
|
||||
width: root.rowHeight * modelData.aspect_ratio
|
||||
height: root.rowHeight
|
||||
visible: opacity > 0
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: modelData.preview_url
|
||||
sourceSize.width: root.rowHeight * modelData.aspect_ratio
|
||||
sourceSize.height: root.rowHeight
|
||||
|
||||
visible: opacity > 0
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
@@ -78,10 +82,6 @@ Button {
|
||||
radius: imageRadius
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
RippleButton {
|
||||
|
||||
@@ -237,7 +237,7 @@ Rectangle {
|
||||
rowHeight: imageRow.rowHeight
|
||||
imageRadius: imageRow.modelData.images.length == 1 ? 50 : Appearance.rounding.normal
|
||||
// Download manually to reduce redundant requests or make sure downloading works
|
||||
// manualDownload: ["danbooru", "waifu.im", "t.alcy.cc"].includes(root.responseData.provider)
|
||||
manualDownload: ["danbooru", "waifu.im", "t.alcy.cc"].includes(root.responseData.provider)
|
||||
previewDownloadPath: root.previewDownloadPath
|
||||
downloadPath: root.downloadPath
|
||||
nsfwPath: root.nsfwPath
|
||||
|
||||
@@ -4,6 +4,7 @@ import qs
|
||||
import qs.services
|
||||
import "./calendar"
|
||||
import "./todo"
|
||||
import "./pomodoro"
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
@@ -17,7 +18,8 @@ Rectangle {
|
||||
property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed
|
||||
property var tabs: [
|
||||
{"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": calendarWidget},
|
||||
{"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": todoWidget}
|
||||
{"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": todoWidget},
|
||||
{"type": "timer", "name": Translation.tr("Timer"), "icon": "schedule", "widget": pomodoroWidget},
|
||||
]
|
||||
|
||||
Behavior on implicitHeight {
|
||||
@@ -238,4 +240,13 @@ Rectangle {
|
||||
anchors.margins: 5
|
||||
}
|
||||
}
|
||||
|
||||
// Pomodoro component
|
||||
Component {
|
||||
id: pomodoroWidget
|
||||
PomodoroWidget {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ Rectangle {
|
||||
color: Appearance.colors.colLayer1
|
||||
|
||||
property int selectedTab: 0
|
||||
property var tabButtonList: [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
|
||||
property var tabButtonList: [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Audio")}]
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitHeight: contentColumn.implicitHeight
|
||||
implicitWidth: contentColumn.implicitWidth
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
// The Pomodoro timer circle
|
||||
CircularProgress {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
lineWidth: 8
|
||||
value: {
|
||||
return TimerService.pomodoroSecondsLeft / TimerService.pomodoroLapDuration;
|
||||
}
|
||||
implicitSize: 200
|
||||
enableAnimation: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: {
|
||||
let minutes = Math.floor(TimerService.pomodoroSecondsLeft / 60).toString().padStart(2, '0');
|
||||
let seconds = Math.floor(TimerService.pomodoroSecondsLeft % 60).toString().padStart(2, '0');
|
||||
return `${minutes}:${seconds}`;
|
||||
}
|
||||
font.pixelSize: 40
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: TimerService.pomodoroLongBreak ? Translation.tr("Long break") : TimerService.pomodoroBreak ? Translation.tr("Break") : Translation.tr("Focus")
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.colors.colLayer2
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
implicitWidth: 36
|
||||
implicitHeight: implicitWidth
|
||||
|
||||
StyledText {
|
||||
id: cycleText
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.colors.colOnLayer2
|
||||
text: TimerService.pomodoroCycle + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The Start/Stop and Reset buttons
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 10
|
||||
|
||||
RippleButton {
|
||||
contentItem: StyledText {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: TimerService.pomodoroRunning ? Translation.tr("Pause") : (TimerService.pomodoroSecondsLeft === TimerService.focusTime) ? Translation.tr("Start") : Translation.tr("Resume")
|
||||
color: TimerService.pomodoroRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
|
||||
}
|
||||
implicitHeight: 35
|
||||
implicitWidth: 90
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
onClicked: TimerService.togglePomodoro()
|
||||
colBackground: TimerService.pomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
|
||||
colBackgroundHover: TimerService.pomodoroRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
|
||||
}
|
||||
|
||||
RippleButton {
|
||||
implicitHeight: 35
|
||||
implicitWidth: 90
|
||||
|
||||
onClicked: TimerService.resetPomodoro()
|
||||
enabled: (TimerService.pomodoroSecondsLeft < TimerService.pomodoroLapDuration) || TimerService.pomodoroCycle > 0 || TimerService.pomodoroBreak
|
||||
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
colBackground: Appearance.colors.colErrorContainer
|
||||
colBackgroundHover: Appearance.colors.colErrorContainerHover
|
||||
colRipple: Appearance.colors.colErrorContainerActive
|
||||
|
||||
contentItem: StyledText {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Translation.tr("Reset")
|
||||
color: Appearance.colors.colOnErrorContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property int currentTab: 0
|
||||
property var tabButtonList: [
|
||||
{"name": Translation.tr("Pomodoro"), "icon": "search_activity"},
|
||||
{"name": Translation.tr("Stopwatch"), "icon": "timer"}
|
||||
]
|
||||
|
||||
// These are keybinds for stopwatch and pomodoro
|
||||
Keys.onPressed: (event) => {
|
||||
if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.NoModifier) { // Switch tabs
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1)
|
||||
} else if (event.key === Qt.Key_PageUp) {
|
||||
currentTab = Math.max(currentTab - 1, 0)
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Space || event.key === Qt.Key_S) { // Pause/resume with Space or S
|
||||
if (currentTab === 0) {
|
||||
TimerService.togglePomodoro()
|
||||
} else {
|
||||
TimerService.toggleStopwatch()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_R) { // Reset with R
|
||||
if (currentTab === 0) {
|
||||
TimerService.resetPomodoro()
|
||||
} else {
|
||||
TimerService.stopwatchReset()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_L) { // Record lap with L
|
||||
TimerService.stopwatchRecordLap()
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
TabBar {
|
||||
id: tabBar
|
||||
Layout.fillWidth: true
|
||||
currentIndex: currentTab
|
||||
onCurrentIndexChanged: currentTab = currentIndex
|
||||
|
||||
background: Item {
|
||||
WheelHandler {
|
||||
onWheel: (event) => {
|
||||
if (event.angleDelta.y < 0)
|
||||
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
|
||||
else if (event.angleDelta.y > 0)
|
||||
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.tabButtonList
|
||||
delegate: SecondaryTabButton {
|
||||
selected: (index == currentTab)
|
||||
buttonText: modelData.name
|
||||
buttonIcon: modelData.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Tab indicator
|
||||
id: tabIndicator
|
||||
Layout.fillWidth: true
|
||||
height: 3
|
||||
property bool enableIndicatorAnimation: false
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentTabChanged() {
|
||||
tabIndicator.enableIndicatorAnimation = true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
property int tabCount: root.tabButtonList.length
|
||||
property real fullTabSize: root.width / tabCount;
|
||||
property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
||||
|
||||
implicitWidth: targetWidth
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
||||
|
||||
color: Appearance.colors.colPrimary
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Behavior on x {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Tabbar bottom border
|
||||
id: tabBarBottomBorder
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
Layout.topMargin: 10
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
clip: true
|
||||
currentIndex: currentTab
|
||||
onCurrentIndexChanged: {
|
||||
tabIndicator.enableIndicatorAnimation = true
|
||||
currentTab = currentIndex
|
||||
}
|
||||
|
||||
// Tabs
|
||||
PomodoroTimer {}
|
||||
Stopwatch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: stopwatchTab
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Item {
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 8
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
}
|
||||
|
||||
RowLayout { // Elapsed
|
||||
id: elapsedIndicator
|
||||
|
||||
anchors {
|
||||
top: undefined
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: controlButtons.left
|
||||
leftMargin: 6
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "hasLaps"
|
||||
when: TimerService.stopwatchLaps.length > 0
|
||||
AnchorChanges {
|
||||
target: elapsedIndicator
|
||||
anchors.top: parent.top
|
||||
anchors.verticalCenter: undefined
|
||||
anchors.left: controlButtons.left
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
StyledText {
|
||||
// Layout.preferredWidth: elapsedIndicator.width * 0.6 // Prevent shakiness
|
||||
font.pixelSize: 40
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
text: {
|
||||
let totalSeconds = Math.floor(TimerService.stopwatchTime) / 100
|
||||
let minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
|
||||
let seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
|
||||
return `${minutes}:${seconds}`
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 40
|
||||
color: Appearance.colors.colSubtext
|
||||
text: {
|
||||
return `:<sub>${(Math.floor(TimerService.stopwatchTime) % 100).toString().padStart(2, '0')}</sub>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Laps
|
||||
StyledListView {
|
||||
id: lapsList
|
||||
anchors {
|
||||
top: elapsedIndicator.bottom
|
||||
bottom: controlButtons.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
topMargin: 16
|
||||
bottomMargin: 16
|
||||
}
|
||||
spacing: 4
|
||||
clip: true
|
||||
popin: true
|
||||
|
||||
model: ScriptModel {
|
||||
values: TimerService.stopwatchLaps.map((v, i, arr) => arr[arr.length - 1 - i])
|
||||
}
|
||||
|
||||
delegate: Rectangle {
|
||||
id: lapItem
|
||||
required property int index
|
||||
required property var modelData
|
||||
property var horizontalPadding: 10
|
||||
property var verticalPadding: 6
|
||||
width: lapsList.width
|
||||
implicitHeight: lapRow.implicitHeight + verticalPadding * 2
|
||||
implicitWidth: lapRow.implicitWidth + horizontalPadding * 2
|
||||
color: Appearance.colors.colLayer2
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
RowLayout {
|
||||
id: lapRow
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: lapItem.horizontalPadding
|
||||
rightMargin: lapItem.horizontalPadding
|
||||
topMargin: lapItem.verticalPadding
|
||||
bottomMargin: lapItem.verticalPadding
|
||||
}
|
||||
|
||||
StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colSubtext
|
||||
text: `${TimerService.stopwatchLaps.length - lapItem.index}.`
|
||||
}
|
||||
|
||||
StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
text: {
|
||||
const lapTime = lapItem.modelData
|
||||
const _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
|
||||
const totalSeconds = Math.floor(lapTime) / 100
|
||||
const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
|
||||
const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
|
||||
return `${minutes}:${seconds}.${_10ms}`
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: Appearance.colors.colPrimary
|
||||
text: {
|
||||
const originalIndex = TimerService.stopwatchLaps.length - lapItem.index - 1
|
||||
const lastTime = originalIndex > 0 ? TimerService.stopwatchLaps[originalIndex - 1] : 0
|
||||
const lapTime = lapItem.modelData - lastTime
|
||||
const _10ms = (Math.floor(lapTime) % 100).toString().padStart(2, '0')
|
||||
const totalSeconds = Math.floor(lapTime) / 100
|
||||
const minutes = Math.floor(totalSeconds / 60).toString().padStart(2, '0')
|
||||
const seconds = Math.floor(totalSeconds % 60).toString().padStart(2, '0')
|
||||
return `+${minutes == "00" ? "" : minutes + ":"}${seconds}.${_10ms}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: controlButtons
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 6
|
||||
}
|
||||
spacing: 4
|
||||
|
||||
RippleButton {
|
||||
Layout.preferredHeight: 35
|
||||
Layout.preferredWidth: 90
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
|
||||
onClicked: {
|
||||
TimerService.toggleStopwatch()
|
||||
}
|
||||
|
||||
colBackground: TimerService.stopwatchRunning ? Appearance.colors.colSecondaryContainer : Appearance.colors.colPrimary
|
||||
colBackgroundHover: TimerService.stopwatchRunning ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colPrimaryHover
|
||||
colRipple: TimerService.stopwatchRunning ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colPrimaryActive
|
||||
|
||||
contentItem: StyledText {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: TimerService.stopwatchRunning ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnPrimary
|
||||
text: TimerService.stopwatchRunning ? Translation.tr("Pause") : TimerService.stopwatchTime === 0 ? Translation.tr("Start") : Translation.tr("Resume")
|
||||
}
|
||||
}
|
||||
|
||||
RippleButton {
|
||||
implicitHeight: 35
|
||||
implicitWidth: 90
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
|
||||
onClicked: {
|
||||
if (TimerService.stopwatchRunning)
|
||||
TimerService.stopwatchRecordLap()
|
||||
else
|
||||
TimerService.stopwatchReset()
|
||||
}
|
||||
enabled: TimerService.stopwatchTime > 0 || Persistent.states.timer.stopwatch.laps.length > 0
|
||||
|
||||
colBackground: TimerService.stopwatchRunning ? Appearance.colors.colLayer2 : Appearance.colors.colErrorContainer
|
||||
colBackgroundHover: TimerService.stopwatchRunning ? Appearance.colors.colLayer2Hover : Appearance.colors.colErrorContainerHover
|
||||
colRipple: TimerService.stopwatchRunning ? Appearance.colors.colLayer2Active : Appearance.colors.colErrorContainerActive
|
||||
|
||||
contentItem: StyledText {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: TimerService.stopwatchRunning ? Translation.tr("Lap") : Translation.tr("Reset")
|
||||
color: TimerService.stopwatchRunning ? Appearance.colors.colOnLayer2 : Appearance.colors.colOnErrorContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
import qs.modules.common.widgets
|
||||
import qs
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
|
||||
QuickToggleButton {
|
||||
id: root
|
||||
toggled: false
|
||||
visible: false
|
||||
toggled: EasyEffects.active
|
||||
visible: EasyEffects.available
|
||||
buttonIcon: "instant_mix"
|
||||
|
||||
Component.onCompleted: {
|
||||
EasyEffects.fetchActiveState()
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (toggled) {
|
||||
root.toggled = false
|
||||
Quickshell.execDetached(["pkill", "easyeffects"])
|
||||
} else {
|
||||
root.toggled = true
|
||||
Quickshell.execDetached(["easyeffects", "--gapplication-service"])
|
||||
}
|
||||
EasyEffects.toggle()
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
@@ -25,24 +25,6 @@ QuickToggleButton {
|
||||
GlobalStates.sidebarRightOpen = false
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fetchAvailability
|
||||
running: true
|
||||
command: ["bash", "-c", "command -v easyeffects"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.visible = exitCode === 0
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fetchActiveState
|
||||
running: true
|
||||
command: ["pidof", "easyeffects"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.toggled = exitCode === 0
|
||||
}
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
content: Translation.tr("EasyEffects | Right-click to configure")
|
||||
}
|
||||
|
||||
+5
-3
@@ -6,15 +6,17 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
GroupButton {
|
||||
RippleButton {
|
||||
id: button
|
||||
required property bool input
|
||||
|
||||
buttonRadius: Appearance.rounding.small
|
||||
colBackground: Appearance.colors.colLayer2
|
||||
colBackgroundHover: Appearance.colors.colLayer2Hover
|
||||
colBackgroundActive: Appearance.colors.colLayer2Active
|
||||
clickedWidth: baseWidth + 30
|
||||
colRipple: Appearance.colors.colLayer2Active
|
||||
|
||||
implicitHeight: contentItem.implicitHeight + 6 * 2
|
||||
implicitWidth: contentItem.implicitWidth + 6 * 2
|
||||
|
||||
contentItem: RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -99,19 +99,13 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Separator
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outlineVariant
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
|
||||
// Device selector
|
||||
ButtonGroup {
|
||||
RowLayout {
|
||||
id: deviceSelectorRowLayout
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
uniformCellSizes: true
|
||||
|
||||
AudioDeviceSelectorButton {
|
||||
Layout.fillWidth: true
|
||||
input: false
|
||||
|
||||
Reference in New Issue
Block a user