forked from Shinonome/dots-hyprland
wbar: add right click menus
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3.28 2.22a.75.75 0 0 0-1.06 1.06l5.905 5.905L4.81 10.33a1.25 1.25 0 0 0-.476 2.065L7.439 15.5 3 19.94V21h1.06l4.44-4.44 3.105 3.105a1.25 1.25 0 0 0 2.065-.476l1.145-3.313 5.905 5.904a.75.75 0 0 0 1.06-1.06L3.28 2.22Zm10.355 12.476-1.252 3.626-6.705-6.705 3.626-1.252 4.331 4.331Zm6.048-3.876-3.787 1.894 1.118 1.118 3.34-1.67a2.75 2.75 0 0 0 .714-4.404l-4.825-4.826a2.75 2.75 0 0 0-4.405.715l-1.67 3.34 1.118 1.117 1.894-3.787a1.25 1.25 0 0 1 2.002-.325l4.826 4.826a1.25 1.25 0 0 1-.325 2.002Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 622 B |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m16.242 2.932 4.826 4.826a2.75 2.75 0 0 1-.715 4.404l-4.87 2.435a.75.75 0 0 0-.374.426l-1.44 4.166a1.25 1.25 0 0 1-2.065.476L8.5 16.561 4.06 21H3v-1.06l4.44-4.44-3.105-3.104a1.25 1.25 0 0 1 .476-2.066l4.166-1.44a.75.75 0 0 0 .426-.373l2.435-4.87a2.75 2.75 0 0 1 4.405-.715Zm3.766 5.886-4.826-4.826a1.25 1.25 0 0 0-2.002.325l-2.435 4.871a2.25 2.25 0 0 1-1.278 1.12l-3.789 1.31 6.705 6.704 1.308-3.789a2.25 2.25 0 0 1 1.12-1.277l4.872-2.436a1.25 1.25 0 0 0 .325-2.002Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 594 B |
@@ -563,6 +563,10 @@ Singleton {
|
||||
}
|
||||
|
||||
property JsonObject waffles: JsonObject {
|
||||
// Animations on Windoes are kinda janky. Set the following to
|
||||
// false will make (some) stuff also be like that for accuracy.
|
||||
// Example: the right-click menu of the Start button
|
||||
property bool smootherAnimations: true
|
||||
property JsonObject bar: JsonObject {
|
||||
property bool bottom: true
|
||||
property bool leftAlignApps: false
|
||||
|
||||
@@ -285,4 +285,14 @@ Singleton {
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function toTitleCase(str) {
|
||||
// Replace "-" and "_" with space, then capitalize each word
|
||||
return str.replace(/[-_]/g, " ").replace(
|
||||
/\w\S*/g,
|
||||
function(txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +1,40 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Loader {
|
||||
BarPopup {
|
||||
id: root
|
||||
default property var menuData
|
||||
property var model: [
|
||||
{iconName: "start-here", text: "Start", action: () => {print("hello")}}
|
||||
]
|
||||
padding: 2
|
||||
|
||||
property Item anchorItem: parent
|
||||
property real visualMargin: 12
|
||||
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
|
||||
property real ambientShadowWidth: 1
|
||||
contentItem: ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
active: false
|
||||
visible: active
|
||||
sourceComponent: PopupWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
Component.onCompleted: {
|
||||
openAnim.start();
|
||||
}
|
||||
Repeater {
|
||||
model: root.model
|
||||
delegate: WButton {
|
||||
id: btn
|
||||
Layout.fillWidth: true
|
||||
|
||||
anchor {
|
||||
adjustment: PopupAdjustment.Slide
|
||||
item: root.anchorItem
|
||||
gravity: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
edges: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
}
|
||||
required property var modelData
|
||||
icon.name: modelData.iconName ? modelData.iconName : ""
|
||||
monochromeIcon: modelData.monochromeIcon ?? true
|
||||
text: modelData.text ? modelData.text : ""
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: focusGrab
|
||||
active: true
|
||||
windows: [popupWindow]
|
||||
onCleared: {
|
||||
closeAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: realContent.implicitWidth + (ambientShadow.border.width * 2) + (root.visualMargin * 2)
|
||||
implicitHeight: realContent.implicitHeight + (ambientShadow.border.width * 2) + (root.visualMargin * 2)
|
||||
|
||||
property real sourceEdgeMargin: -implicitHeight
|
||||
PropertyAnimation {
|
||||
id: openAnim
|
||||
target: popupWindow
|
||||
property: "sourceEdgeMargin"
|
||||
to: (root.ambientShadowWidth + root.visualMargin)
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: closeAnim
|
||||
PropertyAnimation {
|
||||
target: popupWindow
|
||||
property: "sourceEdgeMargin"
|
||||
to: -implicitHeight
|
||||
duration: 150
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.active = false;
|
||||
onClicked: {
|
||||
if (modelData.action) modelData.action();
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
Rectangle {
|
||||
id: ambientShadow
|
||||
z: 0
|
||||
anchors {
|
||||
fill: realContent
|
||||
margins: -border.width
|
||||
}
|
||||
border.color: ColorUtils.transparentize(Looks.colors.bg0Border, Looks.shadowTransparency)
|
||||
border.width: root.ambientShadowWidth
|
||||
color: "transparent"
|
||||
radius: realContent.radius + border.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realContent
|
||||
z: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.barAtBottom ? undefined : parent.top
|
||||
bottom: root.barAtBottom ? parent.bottom : undefined
|
||||
margins: root.ambientShadowWidth + root.visualMargin
|
||||
// Opening anim
|
||||
bottomMargin: root.barAtBottom ? popupWindow.sourceEdgeMargin : (root.ambientShadowWidth + root.visualMargin)
|
||||
topMargin: root.barAtBottom ? (root.ambientShadowWidth + root.visualMargin) : popupWindow.sourceEdgeMargin
|
||||
}
|
||||
color: Looks.colors.bg1
|
||||
radius: Looks.radius.large
|
||||
|
||||
// test
|
||||
implicitWidth: 300
|
||||
implicitHeight: 400
|
||||
|
||||
Menu {
|
||||
id: menu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
required property var contentItem
|
||||
property real padding: Looks.radius.large - Looks.radius.medium
|
||||
property bool noSmoothClosing: !Config.options.waffles.smootherAnimations
|
||||
|
||||
property Item anchorItem: parent
|
||||
property real visualMargin: 12
|
||||
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
|
||||
property real ambientShadowWidth: 1
|
||||
|
||||
function close() {
|
||||
item.close();
|
||||
}
|
||||
|
||||
active: false
|
||||
visible: active
|
||||
sourceComponent: PopupWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
Component.onCompleted: {
|
||||
openAnim.start();
|
||||
}
|
||||
|
||||
anchor {
|
||||
adjustment: PopupAdjustment.ResizeY | PopupAdjustment.SlideX
|
||||
item: root.anchorItem
|
||||
gravity: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
edges: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: focusGrab
|
||||
active: true
|
||||
windows: [popupWindow]
|
||||
onCleared: {
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (root.noSmoothClosing) root.active = false;
|
||||
else closeAnim.start();
|
||||
}
|
||||
|
||||
implicitWidth: realContent.implicitWidth + (ambientShadow.border.width * 2) + (root.visualMargin * 2)
|
||||
implicitHeight: realContent.implicitHeight + (ambientShadow.border.width * 2) + (root.visualMargin * 2)
|
||||
|
||||
property real sourceEdgeMargin: -implicitHeight
|
||||
PropertyAnimation {
|
||||
id: openAnim
|
||||
target: popupWindow
|
||||
property: "sourceEdgeMargin"
|
||||
to: (root.ambientShadowWidth + root.visualMargin)
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: closeAnim
|
||||
PropertyAnimation {
|
||||
target: popupWindow
|
||||
property: "sourceEdgeMargin"
|
||||
to: -implicitHeight
|
||||
duration: 150
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
Rectangle {
|
||||
id: ambientShadow
|
||||
z: 0
|
||||
anchors {
|
||||
fill: realContent
|
||||
margins: -border.width
|
||||
}
|
||||
border.color: ColorUtils.transparentize(Looks.colors.bg0Border, Looks.shadowTransparency)
|
||||
border.width: root.ambientShadowWidth
|
||||
color: "transparent"
|
||||
radius: realContent.radius + border.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realContent
|
||||
z: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.barAtBottom ? undefined : parent.top
|
||||
bottom: root.barAtBottom ? parent.bottom : undefined
|
||||
margins: root.ambientShadowWidth + root.visualMargin
|
||||
// Opening anim
|
||||
bottomMargin: root.barAtBottom ? popupWindow.sourceEdgeMargin : (root.ambientShadowWidth + root.visualMargin)
|
||||
topMargin: root.barAtBottom ? (root.ambientShadowWidth + root.visualMargin) : popupWindow.sourceEdgeMargin
|
||||
}
|
||||
color: Looks.colors.bg1
|
||||
radius: Looks.radius.large
|
||||
|
||||
// test
|
||||
implicitWidth: root.contentItem.implicitWidth + (root.padding * 2)
|
||||
implicitHeight: root.contentItem.implicitHeight + (root.padding * 2)
|
||||
|
||||
children: [root.contentItem]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
@@ -24,10 +24,43 @@ AppButton {
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
contextMenu.active = !contextMenu.active;
|
||||
contextMenu.active = true;
|
||||
}
|
||||
|
||||
BarMenu {
|
||||
id: contextMenu
|
||||
|
||||
model: [
|
||||
{
|
||||
text: Translation.tr("Terminal"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.terminal]);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("Task Manager"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.taskManager]);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("Settings"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("settings.qml")]);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("File Explorer"),
|
||||
action: () => {
|
||||
Qt.openUrlExternally(Directories.home);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("Search"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "overview", "toggle"]);
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
import Quickshell
|
||||
@@ -16,6 +17,7 @@ AppButton {
|
||||
property bool hasWindows: appEntry.toplevels.length > 0
|
||||
|
||||
signal hoverPreviewRequested()
|
||||
signal hoverPreviewDismissed()
|
||||
|
||||
multiple: appEntry.toplevels.length > 1
|
||||
checked: active
|
||||
@@ -43,6 +45,12 @@ AppButton {
|
||||
}
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
root.hoverPreviewDismissed()
|
||||
root.hoverTimer.stop()
|
||||
contextMenu.active = true;
|
||||
}
|
||||
|
||||
// Active indicator
|
||||
Rectangle {
|
||||
id: activeIndicator
|
||||
@@ -74,4 +82,37 @@ AppButton {
|
||||
extraVisibleCondition: root.shouldShowTooltip && !root.hasWindows
|
||||
text: desktopEntry ? desktopEntry.name : appEntry.appId
|
||||
}
|
||||
|
||||
BarMenu {
|
||||
id: contextMenu
|
||||
|
||||
model: [
|
||||
{
|
||||
iconName: root.iconName,
|
||||
text: root.desktopEntry ? root.desktopEntry.name : StringUtils.toTitleCase(appEntry.appId),
|
||||
monochromeIcon: false,
|
||||
action: () => {
|
||||
if (root.desktopEntry) {
|
||||
root.desktopEntry.execute()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
iconName: root.appEntry.pinned ? "pin-off" : "pin",
|
||||
text: root.appEntry.pinned ? qsTr("Unpin from taskbar") : qsTr("Pin to taskbar"),
|
||||
action: () => {
|
||||
TaskbarApps.togglePin(root.appEntry.appId);
|
||||
}
|
||||
},
|
||||
...(root.appEntry.toplevels.length > 0 ? [{
|
||||
iconName: "dismiss",
|
||||
text: root.multiple ? qsTr("Close all windows") : qsTr("Close window"),
|
||||
action: () => {
|
||||
for (let toplevel of root.appEntry.toplevels) {
|
||||
toplevel.close();
|
||||
}
|
||||
}
|
||||
}] : []),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ MouseArea {
|
||||
previewPopup.show(appEntry, button);
|
||||
}
|
||||
|
||||
function showContextMenu(appEntry, button) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Apps row
|
||||
RowLayout {
|
||||
id: row
|
||||
@@ -40,9 +36,8 @@ MouseArea {
|
||||
onHoverPreviewRequested: {
|
||||
root.showPreviewPopup(appEntry, this)
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
root.showContextMenu(appEntry, this)
|
||||
onHoverPreviewDismissed: {
|
||||
previewPopup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,5 +50,4 @@ MouseArea {
|
||||
anchor.window: root.QsWindow.window
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ Singleton {
|
||||
}
|
||||
property QtObject pixelSize: QtObject {
|
||||
property real normal: 11
|
||||
property real large: 15
|
||||
property real large: 14
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
// Generic button with background
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property color colBackgroundHover: Looks.colors.bg2Hover
|
||||
property color colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
|
||||
property alias monochromeIcon: buttonIcon.monochrome
|
||||
|
||||
property var altAction: () => {}
|
||||
property var middleClickAction: () => {}
|
||||
|
||||
property real inset: 2
|
||||
topInset: inset
|
||||
bottomInset: inset
|
||||
leftInset: inset
|
||||
rightInset: inset
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 6
|
||||
implicitHeight: contentItem.implicitHeight + verticalPadding * 2
|
||||
implicitWidth: contentItem.implicitWidth + horizontalPadding * 2
|
||||
|
||||
background: Rectangle {
|
||||
radius: Looks.radius.medium
|
||||
color: {
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else if ((root.hovered && !root.down) || root.checked) {
|
||||
return root.colBackgroundHover;
|
||||
} else {
|
||||
return root.colBackground;
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton | Qt.MiddleButton
|
||||
onClicked: (event) => {
|
||||
if (event.button === Qt.LeftButton) root.clicked();
|
||||
if (event.button === Qt.RightButton) root.altAction();
|
||||
if (event.button === Qt.MiddleButton) root.middleClickAction();
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: root.inset
|
||||
}
|
||||
implicitWidth: contentLayout.implicitWidth
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: root.horizontalPadding
|
||||
rightMargin: root.horizontalPadding
|
||||
}
|
||||
spacing: 12
|
||||
FluentIcon {
|
||||
id: buttonIcon
|
||||
monochrome: true
|
||||
implicitSize: 16
|
||||
Layout.leftMargin: 6
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
visible: root.icon.name !== ""
|
||||
icon: root.icon.name
|
||||
}
|
||||
WText {
|
||||
Layout.rightMargin: 12
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font {
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user