adds overview, removes old stuff
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# Overview module - credits
|
||||
|
||||
The files in this directory (`Overview.qml`, `OverviewWidget.qml`, `OverviewWindow.qml`) and
|
||||
their support singletons (`../../common/Appearance.qml`, `../../common/Config.qml`,
|
||||
`../../common/functions/ColorUtils.qml`, `../../common/widgets/Styled*.qml`,
|
||||
`../../services/HyprlandData.qml`) are copied from
|
||||
[Shanu-Kumawat/quickshell-overview](https://github.com/Shanu-Kumawat/quickshell-overview),
|
||||
which was itself extracted from the overview feature in
|
||||
[end-4/dots-hyprland](https://github.com/end-4/dots-hyprland) ("illogical-impulse") by
|
||||
[end-4](https://github.com/end-4).
|
||||
|
||||
Released under the GPL by the upstream project.
|
||||
|
||||
### Local deviations from upstream
|
||||
|
||||
- `Overview.qml` / `OverviewWidget.qml` / `OverviewWindow.qml`: added one `import "../.."` line
|
||||
each, so `GlobalStates.overviewOpen` (merged into `own/GlobalStates.qml`, see below) resolves
|
||||
from this subdirectory. No other logic changed.
|
||||
- `../../common/Appearance.qml`: `defaultColors` was retuned to pull from this shell's own
|
||||
`Theme`/`Colors` wallust singletons instead of the stock M3 purple palette, so the overview
|
||||
matches the bar's colors (needed one added `import ".."`). This is the module's own documented
|
||||
way to customize its palette (see upstream README, "Theme & Colors").
|
||||
- `../../common/Config.qml`: untouched. Transparency/rounding are instead tuned via
|
||||
`~/.config/quickshell/overview/config.json` (the module's own documented user-override file),
|
||||
kept separate from the upstream defaults it reads.
|
||||
|
||||
## Why a copy instead of a symlink/submodule
|
||||
|
||||
The module was merged into this shell's own process (`own/shell.qml`) so it runs in the same
|
||||
`quickshell -c own` instance as the bar, rather than as a second `qs -c overview` process.
|
||||
|
||||
An untouched clone of the upstream repo is kept side by side at
|
||||
`~/.config/quickshell/overview` - use it as the reference copy when checking for upstream
|
||||
updates (`git -C ~/.config/quickshell/overview pull`) and re-sync the files above by hand if
|
||||
anything changes there.
|
||||
|
||||
## Configuration
|
||||
|
||||
Runtime options (grid size, scale, effects, etc.) are unaffected by the merge - `Config.qml`
|
||||
still reads user overrides from `$XDG_CONFIG_HOME/quickshell/overview/config.json` regardless
|
||||
of where the QML itself lives. See `~/.config/quickshell/overview/config.example.json` and
|
||||
`~/.config/quickshell/overview/README.md` (in the untouched upstream clone) for the full list
|
||||
of options.
|
||||
|
||||
## Toggling it
|
||||
|
||||
The merged module still registers the same IPC target, just under the `own` shell instance:
|
||||
|
||||
```bash
|
||||
qs ipc -c own call overview toggle
|
||||
qs ipc -c own call overview open
|
||||
qs ipc -c own call overview close
|
||||
```
|
||||
@@ -0,0 +1,253 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import "../../common"
|
||||
import "../../services"
|
||||
import "../.."
|
||||
import "."
|
||||
|
||||
Scope {
|
||||
id: overviewScope
|
||||
Variants {
|
||||
id: overviewVariants
|
||||
model: Quickshell.screens
|
||||
PanelWindow {
|
||||
id: root
|
||||
required property var modelData
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
||||
property bool blurEnabled: Config.options.overview.effects.enableBlur
|
||||
property bool backdropEnabled: Config.options.overview.effects.enableBackdrop
|
||||
property real backdropOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.backdropOpacity))
|
||||
property bool closeOnFocusLoss: Config.options.overview.closeOnFocusLoss ?? true
|
||||
screen: modelData
|
||||
visible: GlobalStates.overviewOpen
|
||||
|
||||
WlrLayershell.namespace: blurEnabled ? "quickshell:overview-blur" : "quickshell:overview"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [root]
|
||||
property bool canBeActive: root.monitorIsFocused
|
||||
active: false
|
||||
onCleared: () => {
|
||||
// Only the monitor that owns the grab may close the overview
|
||||
if (root.closeOnFocusLoss && !active && canBeActive)
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
if (GlobalStates.overviewOpen) {
|
||||
delayedGrabTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-evaluate grab ownership when focused monitor changes
|
||||
Connections {
|
||||
target: Hyprland
|
||||
function onFocusedMonitorChanged() {
|
||||
if (!GlobalStates.overviewOpen)
|
||||
return;
|
||||
// Transfer the grab to the newly focused monitor
|
||||
if (root.monitorIsFocused && !grab.active) {
|
||||
grab.active = true;
|
||||
} else if (!root.monitorIsFocused && grab.active) {
|
||||
grab.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedGrabTimer
|
||||
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!grab.canBeActive)
|
||||
return;
|
||||
grab.active = GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the layershell surface full-screen so backdrop/blur are not constrained by content size.
|
||||
implicitWidth: screen.width
|
||||
implicitHeight: screen.height
|
||||
|
||||
Item {
|
||||
id: keyHandler
|
||||
anchors.fill: parent
|
||||
visible: GlobalStates.overviewOpen
|
||||
focus: GlobalStates.overviewOpen
|
||||
z: 0
|
||||
|
||||
Rectangle {
|
||||
id: backdropLayer
|
||||
anchors.fill: parent
|
||||
visible: root.backdropEnabled
|
||||
color: "#000000"
|
||||
opacity: root.backdropOpacity
|
||||
z: 0
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: outsideClickCatcher
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
enabled: root.closeOnFocusLoss && GlobalStates.overviewOpen
|
||||
z: 0
|
||||
onPressed: mouse => {
|
||||
GlobalStates.overviewOpen = false;
|
||||
mouse.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
// close: Escape or Enter
|
||||
if (event.key === Qt.Key_Escape || event.key === Qt.Key_Return) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper: compute current group bounds
|
||||
const workspacesPerGroup = Config.options.overview.rows * Config.options.overview.columns;
|
||||
const currentId = Hyprland.focusedMonitor?.activeWorkspace?.id ?? 1;
|
||||
const useWorkspaceMap = Config.options.overview.useWorkspaceMap;
|
||||
const workspaceMap = Config.options.overview.workspaceMap ?? [];
|
||||
const focusedMonitorId = Hyprland.focusedMonitor?.id ?? root.monitor?.id ?? 0;
|
||||
const workspaceOffset = useWorkspaceMap ? Number(workspaceMap[focusedMonitorId] ?? 0) : 0;
|
||||
const currentGroup = Math.floor((currentId - workspaceOffset - 1) / workspacesPerGroup);
|
||||
const minWorkspaceId = currentGroup * workspacesPerGroup + 1 + workspaceOffset;
|
||||
const maxWorkspaceId = minWorkspaceId + workspacesPerGroup - 1;
|
||||
|
||||
const rows = Config.options.overview.rows;
|
||||
const columns = Config.options.overview.columns;
|
||||
const reverseColumns = Config.options.overview.orderRightLeft;
|
||||
const reverseRows = Config.options.overview.orderBottomUp;
|
||||
|
||||
const clampedIndex = Math.max(0, Math.min(workspacesPerGroup - 1, currentId - minWorkspaceId));
|
||||
const currentNormalRow = Math.floor(clampedIndex / columns);
|
||||
const currentNormalColumn = clampedIndex % columns;
|
||||
|
||||
function toVisualRow(normalRow) {
|
||||
return reverseRows ? (rows - normalRow - 1) : normalRow;
|
||||
}
|
||||
|
||||
function toVisualColumn(normalColumn) {
|
||||
return reverseColumns ? (columns - normalColumn - 1) : normalColumn;
|
||||
}
|
||||
|
||||
function toNormalRow(visualRow) {
|
||||
return reverseRows ? (rows - visualRow - 1) : visualRow;
|
||||
}
|
||||
|
||||
function toNormalColumn(visualColumn) {
|
||||
return reverseColumns ? (columns - visualColumn - 1) : visualColumn;
|
||||
}
|
||||
|
||||
let targetVisualRow = toVisualRow(currentNormalRow);
|
||||
let targetVisualColumn = toVisualColumn(currentNormalColumn);
|
||||
|
||||
let targetId = null;
|
||||
|
||||
// Arrow keys and vim-style hjkl
|
||||
if (event.key === Qt.Key_Left || event.key === Qt.Key_H) {
|
||||
targetVisualColumn = (targetVisualColumn - 1 + columns) % columns;
|
||||
} else if (event.key === Qt.Key_Right || event.key === Qt.Key_L) {
|
||||
targetVisualColumn = (targetVisualColumn + 1) % columns;
|
||||
} else if (event.key === Qt.Key_Up || event.key === Qt.Key_K) {
|
||||
targetVisualRow = (targetVisualRow - 1 + rows) % rows;
|
||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_J) {
|
||||
targetVisualRow = (targetVisualRow + 1) % rows;
|
||||
}
|
||||
|
||||
// Number keys: jump to workspace within the current group
|
||||
// 1-9 map to positions 1-9, 0 maps to position 10
|
||||
else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) {
|
||||
const position = event.key - Qt.Key_0; // 1-9
|
||||
if (position <= workspacesPerGroup) {
|
||||
targetId = minWorkspaceId + position - 1;
|
||||
}
|
||||
} else if (event.key === Qt.Key_0) {
|
||||
// 0 = 10th workspace in the group (if group has 10+ workspaces)
|
||||
if (workspacesPerGroup >= 10) {
|
||||
targetId = minWorkspaceId + 9; // 10th position = offset 9
|
||||
}
|
||||
}
|
||||
|
||||
if (targetId === null && (
|
||||
event.key === Qt.Key_Left || event.key === Qt.Key_H ||
|
||||
event.key === Qt.Key_Right || event.key === Qt.Key_L ||
|
||||
event.key === Qt.Key_Up || event.key === Qt.Key_K ||
|
||||
event.key === Qt.Key_Down || event.key === Qt.Key_J
|
||||
)) {
|
||||
const targetNormalRow = toNormalRow(targetVisualRow);
|
||||
const targetNormalColumn = toNormalColumn(targetVisualColumn);
|
||||
targetId = minWorkspaceId + targetNormalRow * columns + targetNormalColumn;
|
||||
}
|
||||
|
||||
if (targetId !== null) {
|
||||
const clampedTarget = Math.max(minWorkspaceId, Math.min(maxWorkspaceId, targetId));
|
||||
if (Hyprland.usingLua) {
|
||||
Hyprland.dispatch(`hl.dsp.focus({workspace = '${clampedTarget}'})`);
|
||||
} else {
|
||||
Hyprland.dispatch("workspace " + clampedTarget);
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
visible: GlobalStates.overviewOpen
|
||||
z: 1
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin: Config.options.position.topMargin
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: overviewLoader
|
||||
active: Config?.options.overview.enable ?? true
|
||||
sourceComponent: OverviewWidget {
|
||||
panelWindow: root
|
||||
visible: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "overview"
|
||||
|
||||
function toggle() {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
function close() {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
function open() {
|
||||
GlobalStates.overviewOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,265 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import "../../common"
|
||||
import "../../common/functions"
|
||||
import "../../services"
|
||||
import "../.."
|
||||
|
||||
Item { // Window
|
||||
id: root
|
||||
property var toplevel
|
||||
property var windowData
|
||||
property var monitorData
|
||||
property var widgetMonitorData
|
||||
property var scale
|
||||
property var availableWorkspaceWidth
|
||||
property var availableWorkspaceHeight
|
||||
property real positionBaseX: (monitorData?.x ?? 0) + (monitorData?.reserved?.[0] ?? 0)
|
||||
property real positionBaseY: (monitorData?.y ?? 0) + (monitorData?.reserved?.[1] ?? 0)
|
||||
property int recaptureToken: 0
|
||||
property bool restrictToWorkspace: true
|
||||
property real widthRatio: {
|
||||
if (!widgetMonitorData || !monitorData)
|
||||
return 1;
|
||||
|
||||
const widgetWidth = (widgetMonitorData.transform % 2 === 1) ? (widgetMonitorData.height ?? 1) : (widgetMonitorData.width ?? 1);
|
||||
const sourceWidth = (monitorData.transform % 2 === 1) ? (monitorData.height ?? 1) : (monitorData.width ?? 1);
|
||||
const sourceScale = monitorData.scale ?? 1;
|
||||
const widgetScale = widgetMonitorData.scale ?? 1;
|
||||
return (widgetWidth * sourceScale) / (sourceWidth * widgetScale);
|
||||
}
|
||||
property real heightRatio: {
|
||||
if (!widgetMonitorData || !monitorData)
|
||||
return 1;
|
||||
|
||||
const widgetHeight = (widgetMonitorData.transform % 2 === 1) ? (widgetMonitorData.width ?? 1) : (widgetMonitorData.height ?? 1);
|
||||
const sourceHeight = (monitorData.transform % 2 === 1) ? (monitorData.width ?? 1) : (monitorData.height ?? 1);
|
||||
const sourceScale = monitorData.scale ?? 1;
|
||||
const widgetScale = widgetMonitorData.scale ?? 1;
|
||||
return (widgetHeight * sourceScale) / (sourceHeight * widgetScale);
|
||||
}
|
||||
property real initX: Math.max(((windowData?.at[0] ?? 0) - positionBaseX) * root.scale * geometryScaleX, 0) + xOffset
|
||||
property real initY: Math.max(((windowData?.at[1] ?? 0) - positionBaseY) * root.scale * geometryScaleY, 0) + yOffset
|
||||
property real xOffset: 0
|
||||
property real yOffset: 0
|
||||
property int widgetMonitorId: 0
|
||||
property real geometryScaleX: widthRatio
|
||||
property real geometryScaleY: heightRatio
|
||||
|
||||
property var targetWindowWidth: (windowData?.size[0] ?? 100) * scale * geometryScaleX
|
||||
property var targetWindowHeight: (windowData?.size[1] ?? 100) * scale * geometryScaleY
|
||||
property bool hovered: false
|
||||
property bool pressed: false
|
||||
|
||||
property bool showIcons: Config.options.windowPreview.showIcons
|
||||
property var iconToWindowRatio: Config.options.windowPreview.iconToWindowRatio
|
||||
property var xwaylandIndicatorToIconRatio: Config.options.windowPreview.xwaylandIndicatorToIconRatio
|
||||
property var iconToWindowRatioCompact: Config.options.windowPreview.iconToWindowRatioCompact
|
||||
property bool cropToFill: Config.options.windowPreview.cropToFill
|
||||
property bool previewsEnabled: Config.options.overview.previewsEnabled
|
||||
property bool includeInactiveMonitorPreviews: Config.options.overview.includeInactiveMonitorPreviews
|
||||
property int previewRecaptureDelayMs: Config.options.overview.previewRecaptureDelayMs
|
||||
property real windowOverlayOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.windowOverlayOpacity))
|
||||
property bool glassMode: Config.options.overview.effects.glassMode
|
||||
property real glassShineOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.glassShineOpacity))
|
||||
property real effectiveWindowOverlayOpacity: glassMode ? Math.min(windowOverlayOpacity, 0.10) : windowOverlayOpacity
|
||||
property string previewModeRaw: Config.options.overview.previewMode
|
||||
property string previewMode: {
|
||||
const mode = `${previewModeRaw ?? "live"}`.trim().toLowerCase();
|
||||
return (mode === "event" || mode === "snapshot") ? "event" : "live";
|
||||
}
|
||||
property bool livePreviewEnabled: previewsEnabled && previewMode === "live"
|
||||
property bool shouldCapturePreview: {
|
||||
if (!GlobalStates.overviewOpen || !previewsEnabled || !previewCaptureEnabled)
|
||||
return false;
|
||||
if (includeInactiveMonitorPreviews)
|
||||
return true;
|
||||
return (windowData?.monitor ?? -1) === widgetMonitorId;
|
||||
}
|
||||
property var entry: {
|
||||
DesktopEntries.applications.values; // re-run when the entry index updates
|
||||
return DesktopEntries.heuristicLookup(windowData?.class);
|
||||
}
|
||||
property string iconName: {
|
||||
const raw = `${entry?.icon ?? ""}`.trim();
|
||||
const withoutProviderPrefix = raw.replace(/^image:\/\/icon\//, "");
|
||||
const withoutQuery = withoutProviderPrefix.split("?")[0].trim();
|
||||
return withoutQuery.length > 0 ? withoutQuery : "application-x-executable";
|
||||
}
|
||||
property var iconPath: Quickshell.iconPath(iconName, "image-missing")
|
||||
property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
|
||||
|
||||
property bool indicateXWayland: windowData?.xwayland ?? false
|
||||
property bool previewCaptureEnabled: true
|
||||
property bool initialized: false
|
||||
property bool dragInProgress: false
|
||||
property bool suspendPositionAnimation: false
|
||||
property bool animateSize: true
|
||||
|
||||
x: initX
|
||||
y: initY
|
||||
width: Math.min(targetWindowWidth, availableWorkspaceWidth)
|
||||
height: Math.min(targetWindowHeight, availableWorkspaceHeight)
|
||||
opacity: (windowData?.monitor ?? -1) == widgetMonitorId ? 1 : Config.options.windowPreview.inactiveMonitorOpacity
|
||||
visible: {
|
||||
const thisWsId = windowData?.workspace?.id;
|
||||
const isFullscreen = (windowData?.fullscreen ?? 0) > 0;
|
||||
if (isFullscreen || thisWsId === undefined) return true;
|
||||
return !HyprlandData.windowList.some(w => w.workspace?.id === thisWsId && (w.fullscreen ?? 0) > 0);
|
||||
}
|
||||
|
||||
clip: true
|
||||
Component.onCompleted: Qt.callLater(() => root.initialized = true)
|
||||
|
||||
Behavior on x {
|
||||
enabled: root.initialized && !root.dragInProgress && !root.suspendPositionAnimation
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on y {
|
||||
enabled: root.initialized && !root.dragInProgress && !root.suspendPositionAnimation
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on width {
|
||||
enabled: root.initialized && root.animateSize && !root.dragInProgress && !root.suspendPositionAnimation
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
enabled: root.initialized && root.animateSize && !root.dragInProgress && !root.suspendPositionAnimation
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
// Opaque background for windows on the active monitor.
|
||||
// The simplest solution for making those windows fully opaque and not interacting with actual
|
||||
// windows behind the overview, e.g., applying blur to them.
|
||||
Rectangle {
|
||||
visible: (root.windowData?.monitor ?? -1) === root.widgetMonitorId
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
color: root.glassMode
|
||||
? ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer0, 0.38)
|
||||
: Appearance.colors.colLayer2
|
||||
}
|
||||
|
||||
ScreencopyView {
|
||||
id: windowPreview
|
||||
readonly property real srcAspect: {
|
||||
const w = root.windowData?.size?.[0] ?? 0;
|
||||
const h = root.windowData?.size?.[1] ?? 0;
|
||||
return (w > 0 && h > 0) ? (w / h) : 1;
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
width: root.cropToFill
|
||||
? Math.max(parent.width, parent.height * srcAspect)
|
||||
: Math.min(parent.width, parent.height * srcAspect)
|
||||
height: root.cropToFill
|
||||
? Math.max(parent.height, parent.width / srcAspect)
|
||||
: Math.min(parent.height, parent.width / srcAspect)
|
||||
captureSource: shouldCapturePreview ? root.toplevel : null
|
||||
live: livePreviewEnabled
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
layer.effect: MultiEffect {
|
||||
maskEnabled: true
|
||||
maskSource: previewMask
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1.0
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
color: pressed ? ColorUtils.applyAlpha(Appearance.colors.colLayer2Active, Math.min(1, root.effectiveWindowOverlayOpacity + 0.30)) :
|
||||
hovered ? ColorUtils.applyAlpha(Appearance.colors.colLayer2Hover, Math.min(1, root.effectiveWindowOverlayOpacity + 0.20)) :
|
||||
ColorUtils.applyAlpha(
|
||||
root.glassMode ? ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer0, 0.38) : Appearance.colors.colLayer2,
|
||||
root.effectiveWindowOverlayOpacity
|
||||
)
|
||||
border.color: root.glassMode
|
||||
? ColorUtils.applyAlpha(Appearance.m3colors.m3outline, 0.62)
|
||||
: ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7)
|
||||
border.width: 1
|
||||
|
||||
Rectangle {
|
||||
visible: root.glassMode
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: "transparent"
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: ColorUtils.applyAlpha("#FFFFFF", root.glassShineOpacity * 0.24) }
|
||||
GradientStop { position: 0.5; color: ColorUtils.applyAlpha("#FFFFFF", 0.0) }
|
||||
GradientStop { position: 1.0; color: ColorUtils.applyAlpha("#000000", root.glassShineOpacity * 0.14) }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.glassMode
|
||||
anchors.fill: parent
|
||||
anchors.margins: 1
|
||||
radius: Math.max(parent.radius - 1, 0)
|
||||
color: "transparent"
|
||||
border.width: 1
|
||||
border.color: ColorUtils.applyAlpha("#FFFFFF", root.glassShineOpacity * 0.32)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.font.pixelSize.smaller * 0.5
|
||||
|
||||
Image {
|
||||
id: windowIcon
|
||||
visible: root.showIcons
|
||||
property var iconSize: {
|
||||
const renderedSize = Math.min(root.width, root.height);
|
||||
return renderedSize * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / (root.monitorData?.scale ?? 1);
|
||||
}
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
source: root.iconPath
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
sourceSize: Qt.size(Math.max(1, Math.round(iconSize)), Math.max(1, Math.round(iconSize)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: previewMask
|
||||
width: windowPreview.width
|
||||
height: windowPreview.height
|
||||
anchors.centerIn: parent
|
||||
visible: false
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: root.width
|
||||
height: root.height
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
}
|
||||
}
|
||||
|
||||
function refreshCapture() {
|
||||
if (!GlobalStates.overviewOpen || livePreviewEnabled || !previewsEnabled)
|
||||
return;
|
||||
|
||||
root.previewCaptureEnabled = false;
|
||||
previewResetTimer.restart();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: previewResetTimer
|
||||
interval: Math.max(1, previewRecaptureDelayMs)
|
||||
repeat: false
|
||||
onTriggered: root.previewCaptureEnabled = true
|
||||
}
|
||||
|
||||
onRecaptureTokenChanged: {
|
||||
if (recaptureToken > 0)
|
||||
root.refreshCapture();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
Overview 1.0 Overview.qml
|
||||
OverviewWidget 1.0 OverviewWidget.qml
|
||||
OverviewWindow 1.0 OverviewWindow.qml
|
||||
Reference in New Issue
Block a user