forked from Shinonome/dots-hyprland
dock: join pinned apps and open windows
This commit is contained in:
@@ -120,26 +120,6 @@ Scope { // Scope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DockSeparator {}
|
DockSeparator {}
|
||||||
// Pinned apps
|
|
||||||
Repeater {
|
|
||||||
model: ConfigOptions?.dock.pinnedApps ?? []
|
|
||||||
|
|
||||||
DockButton {
|
|
||||||
id: pinnedAppButton
|
|
||||||
required property string modelData
|
|
||||||
property DesktopEntry entry: DesktopEntries.byId(modelData)
|
|
||||||
onClicked: {
|
|
||||||
pinnedAppButton?.entry.execute();
|
|
||||||
}
|
|
||||||
contentItem: IconImage {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
source: Quickshell.iconPath(AppSearch.guessIcon(modelData), "image-missing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DockSeparator { visible: (ConfigOptions?.dock.pinnedApps ?? []).length > 0 }
|
|
||||||
|
|
||||||
DockApps { id: dockApps }
|
DockApps { id: dockApps }
|
||||||
DockSeparator {}
|
DockSeparator {}
|
||||||
DockButton {
|
DockButton {
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import Quickshell.Wayland
|
|||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
DockButton {
|
DockButton {
|
||||||
id: appButton
|
id: root
|
||||||
required property var appToplevel
|
property var appToplevel
|
||||||
property var appListRoot
|
property var appListRoot
|
||||||
property int lastFocused: -1
|
property int lastFocused: -1
|
||||||
property real iconSize: 35
|
property real iconSize: 35
|
||||||
@@ -23,56 +23,86 @@ DockButton {
|
|||||||
property real countDotHeight: 4
|
property real countDotHeight: 4
|
||||||
property bool appIsActive: appToplevel.toplevels.find(t => (t.activated == true)) !== undefined
|
property bool appIsActive: appToplevel.toplevels.find(t => (t.activated == true)) !== undefined
|
||||||
|
|
||||||
MouseArea {
|
property bool isSeparator: appToplevel.appId === "SEPARATOR"
|
||||||
id: mouseArea
|
enabled: !isSeparator
|
||||||
anchors.fill: parent
|
implicitWidth: isSeparator ? 1 : implicitHeight - topInset - bottomInset
|
||||||
hoverEnabled: true
|
|
||||||
acceptedButtons: Qt.NoButton
|
Loader {
|
||||||
onEntered: {
|
active: isSeparator
|
||||||
appListRoot.lastHoveredButton = appButton
|
anchors {
|
||||||
appListRoot.buttonHovered = true
|
fill: parent
|
||||||
lastFocused = appToplevel.toplevels.length - 1
|
topMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
|
||||||
|
bottomMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
|
||||||
}
|
}
|
||||||
onExited: {
|
sourceComponent: DockSeparator {}
|
||||||
if (appListRoot.lastHoveredButton === appButton) {
|
}
|
||||||
appListRoot.buttonHovered = false
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: appToplevel.toplevels.length > 0
|
||||||
|
sourceComponent: MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
onEntered: {
|
||||||
|
appListRoot.lastHoveredButton = root
|
||||||
|
appListRoot.buttonHovered = true
|
||||||
|
lastFocused = appToplevel.toplevels.length - 1
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
if (appListRoot.lastHoveredButton === root) {
|
||||||
|
appListRoot.buttonHovered = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (appToplevel.toplevels.length === 0) {
|
||||||
|
DesktopEntries.byId(root.appToplevel.appId)?.execute();
|
||||||
|
return;
|
||||||
|
}
|
||||||
lastFocused = (lastFocused + 1) % appToplevel.toplevels.length
|
lastFocused = (lastFocused + 1) % appToplevel.toplevels.length
|
||||||
appToplevel.toplevels[lastFocused].activate()
|
appToplevel.toplevels[lastFocused].activate()
|
||||||
}
|
}
|
||||||
contentItem: Item {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
IconImage {
|
contentItem: Loader {
|
||||||
id: iconImage
|
active: !isSeparator
|
||||||
anchors {
|
sourceComponent: Item {
|
||||||
left: parent.left
|
anchors.centerIn: parent
|
||||||
right: parent.right
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing")
|
|
||||||
implicitSize: appButton.iconSize
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
Loader {
|
||||||
spacing: 3
|
id: iconImageLoader
|
||||||
anchors {
|
anchors {
|
||||||
top: iconImage.bottom
|
left: parent.left
|
||||||
topMargin: 2
|
right: parent.right
|
||||||
horizontalCenter: parent.horizontalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
active: !root.isSeparator
|
||||||
|
sourceComponent: IconImage {
|
||||||
|
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing")
|
||||||
|
implicitSize: root.iconSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Repeater {
|
|
||||||
model: Math.min(appToplevel.toplevels.length, 3)
|
RowLayout {
|
||||||
delegate: Rectangle {
|
spacing: 3
|
||||||
required property int index
|
anchors {
|
||||||
radius: Appearance.rounding.full
|
top: iconImageLoader.bottom
|
||||||
implicitWidth: (appToplevel.toplevels.length <= 3) ?
|
topMargin: 2
|
||||||
appButton.countDotWidth : appButton.countDotHeight // Circles when too many
|
horizontalCenter: parent.horizontalCenter
|
||||||
implicitHeight: appButton.countDotHeight
|
}
|
||||||
color: appIsActive ? Appearance.m3colors.m3primary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4)
|
Repeater {
|
||||||
|
model: Math.min(appToplevel.toplevels.length, 3)
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property int index
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
implicitWidth: (appToplevel.toplevels.length <= 3) ?
|
||||||
|
root.countDotWidth : root.countDotHeight // Circles when too many
|
||||||
|
implicitHeight: root.countDotHeight
|
||||||
|
color: appIsActive ? Appearance.m3colors.m3primary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ Item {
|
|||||||
property Item lastHoveredButton
|
property Item lastHoveredButton
|
||||||
property bool buttonHovered: false
|
property bool buttonHovered: false
|
||||||
property bool requestDockShow: previewPopup.show
|
property bool requestDockShow: previewPopup.show
|
||||||
property real popupX: parentWindow.mapFromItem(root.lastHoveredButton, root.lastHoveredButton.width / 2, root.lastHoveredButton.height / 2).x - implicitWidth / 2
|
property var parentWindow: root.QsWindow
|
||||||
|
property real popupX: parentWindow?.mapFromItem(root.lastHoveredButton, root.lastHoveredButton?.width / 2, root.lastHoveredButton?.height / 2).x - implicitWidth / 2
|
||||||
|
?? 0
|
||||||
implicitWidth: rowLayout.implicitWidth
|
implicitWidth: rowLayout.implicitWidth
|
||||||
implicitHeight: rowLayout.implicitHeight
|
implicitHeight: rowLayout.implicitHeight
|
||||||
|
|
||||||
@@ -38,15 +39,33 @@ Item {
|
|||||||
values: {
|
values: {
|
||||||
var map = new Map();
|
var map = new Map();
|
||||||
|
|
||||||
|
// Pinned apps
|
||||||
|
const pinnedApps = ConfigOptions?.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) {
|
for (const toplevel of ToplevelManager.toplevels.values) {
|
||||||
if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), []);
|
if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), ({
|
||||||
map.get(toplevel.appId.toLowerCase()).push(toplevel);
|
pinned: false,
|
||||||
|
toplevels: []
|
||||||
|
}));
|
||||||
|
map.get(toplevel.appId.toLowerCase()).toplevels.push(toplevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
var values = [];
|
var values = [];
|
||||||
|
|
||||||
for (const [key, value] of map) {
|
for (const [key, value] of map) {
|
||||||
values.push({ appId: key, toplevels: value });
|
values.push({ appId: key, toplevels: value.toplevels, pinned: value.pinned });
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
@@ -118,14 +137,9 @@ Item {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
|
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||||
implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
|
implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
|
||||||
// anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
// x: previewPopup.width / 2 + root.popupX
|
|
||||||
// Behavior on x {
|
|
||||||
// animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
// }
|
|
||||||
x: {
|
x: {
|
||||||
const itemCenter = root.QsWindow.mapFromItem(root.lastHoveredButton, root.lastHoveredButton.width / 2, 0);
|
const itemCenter = root.QsWindow?.mapFromItem(root.lastHoveredButton, root.lastHoveredButton?.width / 2, 0);
|
||||||
return itemCenter.x - width / 2
|
return itemCenter.x - width / 2
|
||||||
}
|
}
|
||||||
StyledRectangularShadow {
|
StyledRectangularShadow {
|
||||||
@@ -163,7 +177,9 @@ Item {
|
|||||||
id: previewRowLayout
|
id: previewRowLayout
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
Repeater {
|
Repeater {
|
||||||
model: previewPopup.appTopLevel?.toplevels ?? []
|
model: ScriptModel {
|
||||||
|
values: previewPopup.appTopLevel?.toplevels ?? []
|
||||||
|
}
|
||||||
RippleButton {
|
RippleButton {
|
||||||
id: windowButton
|
id: windowButton
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|||||||
Reference in New Issue
Block a user