forked from Shinonome/dots-hyprland
263 lines
11 KiB
QML
263 lines
11 KiB
QML
import "root:/"
|
|
import "root:/services"
|
|
import "root:/modules/common"
|
|
import "root:/modules/common/widgets"
|
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
|
import Qt5Compat.GraphicalEffects
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Effects
|
|
import QtQuick.Layouts
|
|
import Quickshell.Io
|
|
import Quickshell
|
|
import Quickshell.Widgets
|
|
import Quickshell.Wayland
|
|
import Quickshell.Hyprland
|
|
|
|
Item {
|
|
id: root
|
|
property real maxWindowPreviewHeight: 200
|
|
property real maxWindowPreviewWidth: 300
|
|
property real windowControlsHeight: 30
|
|
|
|
property Item lastHoveredButton
|
|
property bool buttonHovered: false
|
|
property bool requestDockShow: previewPopup.show
|
|
|
|
Layout.fillHeight: true
|
|
Layout.topMargin: Appearance.sizes.hyprlandGapsOut // why does this work
|
|
implicitWidth: listView.implicitWidth
|
|
|
|
StyledListView {
|
|
id: listView
|
|
spacing: 2
|
|
orientation: ListView.Horizontal
|
|
anchors {
|
|
top: parent.top
|
|
bottom: parent.bottom
|
|
}
|
|
implicitWidth: contentWidth
|
|
|
|
Behavior on implicitWidth {
|
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
}
|
|
|
|
model: ScriptModel {
|
|
objectProp: "appId"
|
|
values: {
|
|
var map = new Map();
|
|
|
|
// Pinned apps
|
|
const pinnedApps = Config.options?.dock.pinnedApps ?? [];
|
|
for (const appId of pinnedApps) {
|
|
if (!map.has(appId.toLowerCase())) map.set(appId.toLowerCase(), ({
|
|
pinned: true,
|
|
toplevels: []
|
|
}));
|
|
}
|
|
|
|
// Separator
|
|
if (pinnedApps.length > 0) {
|
|
map.set("SEPARATOR", { pinned: false, toplevels: [] });
|
|
}
|
|
|
|
// Open windows
|
|
for (const toplevel of ToplevelManager.toplevels.values) {
|
|
if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), ({
|
|
pinned: false,
|
|
toplevels: []
|
|
}));
|
|
map.get(toplevel.appId.toLowerCase()).toplevels.push(toplevel);
|
|
}
|
|
|
|
var values = [];
|
|
|
|
for (const [key, value] of map) {
|
|
values.push({ appId: key, toplevels: value.toplevels, pinned: value.pinned });
|
|
}
|
|
|
|
return values;
|
|
}
|
|
}
|
|
delegate: DockAppButton {
|
|
required property var modelData
|
|
appToplevel: modelData
|
|
appListRoot: root
|
|
}
|
|
}
|
|
|
|
PopupWindow {
|
|
id: previewPopup
|
|
property var appTopLevel: root.lastHoveredButton?.appToplevel
|
|
property bool allPreviewsReady: false
|
|
Connections {
|
|
target: root
|
|
function onLastHoveredButtonChanged() {
|
|
previewPopup.allPreviewsReady = false; // Reset readiness when the hovered button changes
|
|
}
|
|
}
|
|
function updatePreviewReadiness() {
|
|
for(var i = 0; i < previewRowLayout.children.length; i++) {
|
|
const view = previewRowLayout.children[i];
|
|
if (view.hasContent === false) {
|
|
allPreviewsReady = false;
|
|
return;
|
|
}
|
|
}
|
|
allPreviewsReady = true;
|
|
}
|
|
property bool shouldShow: {
|
|
const hoverConditions = (popupMouseArea.containsMouse || root.buttonHovered)
|
|
return hoverConditions && allPreviewsReady;
|
|
}
|
|
property bool show: false
|
|
|
|
onShouldShowChanged: {
|
|
if (shouldShow) {
|
|
// show = true;
|
|
updateTimer.restart();
|
|
} else {
|
|
updateTimer.restart();
|
|
}
|
|
}
|
|
Timer {
|
|
id: updateTimer
|
|
interval: 100
|
|
onTriggered: {
|
|
previewPopup.show = previewPopup.shouldShow
|
|
}
|
|
}
|
|
anchor {
|
|
window: root.QsWindow.window
|
|
adjustment: PopupAdjustment.None
|
|
gravity: Edges.Top | Edges.Right
|
|
edges: Edges.Top | Edges.Left
|
|
|
|
}
|
|
visible: popupBackground.visible
|
|
color: "transparent"
|
|
implicitWidth: root.QsWindow.window?.width ?? 1
|
|
implicitHeight: popupMouseArea.implicitHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
|
|
|
|
MouseArea {
|
|
id: popupMouseArea
|
|
anchors.bottom: parent.bottom
|
|
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
|
|
implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
|
|
hoverEnabled: true
|
|
x: {
|
|
const itemCenter = root.QsWindow?.mapFromItem(root.lastHoveredButton, root.lastHoveredButton?.width / 2, 0);
|
|
return itemCenter.x - width / 2
|
|
}
|
|
StyledRectangularShadow {
|
|
target: popupBackground
|
|
opacity: previewPopup.show ? 1 : 0
|
|
visible: opacity > 0
|
|
Behavior on opacity {
|
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
}
|
|
}
|
|
Rectangle {
|
|
id: popupBackground
|
|
property real padding: 5
|
|
opacity: previewPopup.show ? 1 : 0
|
|
visible: opacity > 0
|
|
Behavior on opacity {
|
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
}
|
|
clip: true
|
|
color: Appearance.colors.colSurfaceContainer
|
|
radius: Appearance.rounding.normal
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: Appearance.sizes.elevationMargin
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
implicitHeight: previewRowLayout.implicitHeight + padding * 2
|
|
implicitWidth: previewRowLayout.implicitWidth + padding * 2
|
|
Behavior on implicitWidth {
|
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
}
|
|
Behavior on implicitHeight {
|
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
}
|
|
|
|
RowLayout {
|
|
id: previewRowLayout
|
|
anchors.centerIn: parent
|
|
Repeater {
|
|
model: ScriptModel {
|
|
values: previewPopup.appTopLevel?.toplevels ?? []
|
|
}
|
|
RippleButton {
|
|
id: windowButton
|
|
required property var modelData
|
|
padding: 0
|
|
middleClickAction: () => {
|
|
windowButton.modelData?.close();
|
|
}
|
|
onClicked: {
|
|
windowButton.modelData?.activate();
|
|
}
|
|
contentItem: ColumnLayout {
|
|
implicitWidth: screencopyView.implicitWidth
|
|
implicitHeight: screencopyView.implicitHeight
|
|
|
|
ButtonGroup {
|
|
contentWidth: parent.width - anchors.margins * 2
|
|
WrapperRectangle {
|
|
Layout.fillWidth: true
|
|
color: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
|
|
radius: Appearance.rounding.small
|
|
margin: 5
|
|
StyledText {
|
|
Layout.fillWidth: true
|
|
font.pixelSize: Appearance.font.pixelSize.small
|
|
text: windowButton.modelData?.title
|
|
elide: Text.ElideRight
|
|
color: Appearance.m3colors.m3onSurface
|
|
}
|
|
}
|
|
GroupButton {
|
|
id: closeButton
|
|
colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
|
|
baseWidth: windowControlsHeight
|
|
baseHeight: windowControlsHeight
|
|
buttonRadius: Appearance.rounding.full
|
|
contentItem: MaterialSymbol {
|
|
anchors.centerIn: parent
|
|
horizontalAlignment: Text.AlignHCenter
|
|
text: "close"
|
|
iconSize: Appearance.font.pixelSize.normal
|
|
color: Appearance.m3colors.m3onSurface
|
|
}
|
|
onClicked: {
|
|
windowButton.modelData?.close();
|
|
}
|
|
}
|
|
}
|
|
ScreencopyView {
|
|
id: screencopyView
|
|
captureSource: previewPopup ? windowButton.modelData : null
|
|
live: true
|
|
paintCursor: true
|
|
constraintSize: Qt.size(root.maxWindowPreviewWidth, root.maxWindowPreviewHeight)
|
|
onHasContentChanged: {
|
|
previewPopup.updatePreviewReadiness();
|
|
}
|
|
layer.enabled: true
|
|
layer.effect: OpacityMask {
|
|
maskSource: Rectangle {
|
|
width: screencopyView.width
|
|
height: screencopyView.height
|
|
radius: Appearance.rounding.small
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|