Initial commit
remove syncthing folder bar/popops: fix menu window anims and positioning bar/popops: change anims a little and add dropshadow Update README.md widgets/coloredicon: move to colorization, looks worse but..., yea bar/popops: make popup window dissapear on menu close README: add todo list, and brief desc Update README.md Update README.md Update README.md bar/systray: issue recreate on interact bar/systray: hide popup on interact bar/systray: add arrow for entries with children bar/battery: start of battery widget wallpaper/matugen: add foot template extra sizing conditions for sys tray bar/systray: add some more margin to text update settings schema bar/workspaces: filter by monitor, switch to scriptmodel settings: fix settings lol bar/systray: fix right item feat: screenshot tool clipboard one day... feat: init lockscreen mpris: add ipc handler for multimedia keys mpris stuff save progress put shell in subdir, and add nix package move readme back woops bar/volume: make tool bar smaller greeter: init greeter greeter: fixed resource links readme: update checklist progress maybe, maybe not fix: fixed screenshot tool not working fix: bar layout issues progress save progress update track styled popup still broken but getting there still broken but getting there fix: gitignore qmlls.ini fix: remove qmlls.ini progress save new popup system new popup system new popup system more work on popups fix: mask issues on popups update readme
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
shell/.qmlls.ini
|
||||||
16
README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# kossLAN's personal quickshell dots, for personal use.
|
||||||
|
|
||||||
|
The idea is to eventually be minimal but also use material 3 design language, low padding, low margin, and not distracting.
|
||||||
|
|
||||||
|
## TODO List
|
||||||
|
- [x] Custom Popup Window Surface for smooth anims on top bar
|
||||||
|
- [x] Lockscreen (WIP)
|
||||||
|
- [x] Make volume mixer
|
||||||
|
- [x] Screenshot tool (WIP - kinda scuffed, but is functional)
|
||||||
|
- [ ] Recording/Clip widget with gpuscreenrecorder
|
||||||
|
- [x] Session Manager
|
||||||
|
- [ ] Battery Profile Popup
|
||||||
|
- [ ] REDO Volume OSD (WIP)
|
||||||
|
- [ ] REDO Launcher (wallpaper picker, calculator, commands, etc...)
|
||||||
|
- [ ] Music Player Popup V4 lol
|
||||||
|
- [ ] Add bluetooth module support
|
||||||
26
flake.lock
generated
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1750178436,
|
||||||
|
"narHash": "sha256-t1lcWocjeNT3kYqxYUj3R/O/9PbNsvYFzW50NRkx6X4=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "479251543fd2a1256569108a5dee5c79e6caf8bd",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
31
flake.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
description = "kossLAN's quickshell dots";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
}: let
|
||||||
|
forEachSystem = fn:
|
||||||
|
nixpkgs.lib.genAttrs
|
||||||
|
["x86_64-linux" "aarch64-linux"]
|
||||||
|
(system: fn system nixpkgs.legacyPackages.${system});
|
||||||
|
in {
|
||||||
|
packages = forEachSystem (system: pkgs: rec {
|
||||||
|
default = minmat;
|
||||||
|
minmat = pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "minmat";
|
||||||
|
version = "0.1.0";
|
||||||
|
src = ./shell;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/etc/quickshell
|
||||||
|
cp -r * $out/etc/quickshell
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
46
shell/ShellSettings.qml
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property alias settings: jsonAdapter.settings
|
||||||
|
property alias sizing: jsonAdapter.sizing
|
||||||
|
|
||||||
|
property QtObject colors: QtObject {
|
||||||
|
property color surface: Qt.rgba(1.0, 1.0, 1.0, 1.0)
|
||||||
|
property color surface_translucent: Qt.rgba(0.0, 0.0, 0.0, 0.15)
|
||||||
|
property color surface_container: Qt.rgba(0.25, 0.25, 0.25, 1.0)
|
||||||
|
property color surface_container_translucent: Qt.rgba(0.25, 0.25, 0.25, 0.25)
|
||||||
|
property color highlight: Qt.rgba(1.0, 1.0, 1.0, 0.85)
|
||||||
|
// property color primary: "#2EADC6"
|
||||||
|
property color active: Qt.rgba(1.0, 1.0, 1.0, 1.0)
|
||||||
|
property color active_translucent: Qt.rgba(1.0, 1.0, 1.0, 0.15)
|
||||||
|
property color border_translucent: Qt.rgba(1.0, 1.0, 1.0, 0.05)
|
||||||
|
property color inactive: Qt.rgba(0.25, 0.25, 0.25, 1.0)
|
||||||
|
property color inactive_translucent: Qt.rgba(0.25, 0.25, 0.25, 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
FileView {
|
||||||
|
path: `${Quickshell.dataPath("settings")}/quickshell/settings.json`
|
||||||
|
watchChanges: true
|
||||||
|
onFileChanged: reload()
|
||||||
|
onAdapterUpdated: writeAdapter()
|
||||||
|
blockLoading: true
|
||||||
|
|
||||||
|
JsonAdapter {
|
||||||
|
id: jsonAdapter
|
||||||
|
|
||||||
|
property JsonObject settings: JsonObject {
|
||||||
|
property string wallpaperUrl: Qt.resolvedUrl("root:resources/wallpapers/pixelart0.jpg")
|
||||||
|
property string screenshotPath: "/home/koss/Pictures"
|
||||||
|
property real opacity: 0.55
|
||||||
|
}
|
||||||
|
|
||||||
|
property JsonObject sizing: JsonObject {
|
||||||
|
property int barHeight: 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
shell/bar/ActiveWindow.qml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: windowText
|
||||||
|
text: ToplevelManager.activeToplevel?.title ?? ""
|
||||||
|
color: ShellSettings.colors.active
|
||||||
|
font.pointSize: 11
|
||||||
|
visible: text !== ""
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
101
shell/bar/Bar.qml
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import "power"
|
||||||
|
// import "volume"
|
||||||
|
import "systray"
|
||||||
|
// import qs.widgets
|
||||||
|
import qs
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
|
||||||
|
delegate: PanelWindow {
|
||||||
|
id: root
|
||||||
|
color: ShellSettings.colors.surface_translucent
|
||||||
|
implicitHeight: ShellSettings.sizing.barHeight
|
||||||
|
screen: modelData
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property Popup popup: Popup {
|
||||||
|
bar: root
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: 5
|
||||||
|
rightMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left side of bar
|
||||||
|
RowLayout {
|
||||||
|
spacing: 15
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
|
||||||
|
Workspaces {
|
||||||
|
screen: root.screen
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveWindow {
|
||||||
|
id: activeWindow
|
||||||
|
Layout.preferredWidth: 400
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PowerMenu {
|
||||||
|
// bar: root
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Right side of bar
|
||||||
|
RowLayout {
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|
||||||
|
SysTray {
|
||||||
|
bar: root
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeIndicator {
|
||||||
|
// id: volumeIndicator
|
||||||
|
// popup: root.popup
|
||||||
|
// Layout.preferredWidth: this.height
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
// Layout.topMargin: 2
|
||||||
|
// Layout.bottomMargin: 2
|
||||||
|
// }
|
||||||
|
|
||||||
|
// PowerMenu {
|
||||||
|
// bar: root
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Widgets.Separator {
|
||||||
|
// Layout.leftMargin: 5
|
||||||
|
// Layout.rightMargin: 5
|
||||||
|
// }
|
||||||
|
|
||||||
|
Clock {
|
||||||
|
id: clock
|
||||||
|
color: ShellSettings.colors.active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
shell/bar/Clock.qml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Text {
|
||||||
|
property string ap: sysClock.hours >= 12 ? "PM" : "AM"
|
||||||
|
property string minutes: sysClock.minutes.toString().padStart(2, '0')
|
||||||
|
property string hours: {
|
||||||
|
var value = sysClock.hours % 12;
|
||||||
|
if (value === 0)
|
||||||
|
return 12;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemClock {
|
||||||
|
id: sysClock
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
text: `${hours}:${minutes} ${ap}`
|
||||||
|
font.pointSize: 11
|
||||||
|
}
|
||||||
197
shell/bar/Popup.qml
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs.widgets
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var bar
|
||||||
|
|
||||||
|
property real gaps: 5
|
||||||
|
|
||||||
|
property Item parentItem
|
||||||
|
property PopupItem activeItem
|
||||||
|
property PopupItem lastActiveItem
|
||||||
|
|
||||||
|
property PopupItem shownItem: activeItem ?? lastActiveItem
|
||||||
|
|
||||||
|
onActiveItemChanged: {
|
||||||
|
if (activeItem != null) {
|
||||||
|
activeItem.targetVisible = true;
|
||||||
|
|
||||||
|
if (parentItem) {
|
||||||
|
activeItem.parent = parentItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastActiveItem != null && lastActiveItem != activeItem) {
|
||||||
|
lastActiveItem.targetVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeItem != null) {
|
||||||
|
lastActiveItem = activeItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setItem(item: PopupItem) {
|
||||||
|
activeItem = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeItem(item: PopupItem) {
|
||||||
|
if (activeItem == item) {
|
||||||
|
activeItem = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHidden(item: PopupItem) {
|
||||||
|
if (item == lastActiveItem) {
|
||||||
|
console.log("triggered");
|
||||||
|
lastActiveItem = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property real scaleMul: lastActiveItem && lastActiveItem.targetVisible ? 1 : 0
|
||||||
|
|
||||||
|
Behavior on scaleMul {
|
||||||
|
SmoothedAnimation {
|
||||||
|
velocity: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: popupLoader
|
||||||
|
activeAsync: root.shownItem != null
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: popup
|
||||||
|
visible: true
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: root.bar.width
|
||||||
|
implicitHeight: Math.max(800, parentItem.targetHeight)
|
||||||
|
|
||||||
|
anchor {
|
||||||
|
window: root.bar
|
||||||
|
rect: Qt.rect(0, 0, root.bar.width, root.bar.height)
|
||||||
|
edges: Edges.Bottom | Edges.Left
|
||||||
|
gravity: Edges.Bottom | Edges.Right
|
||||||
|
adjustment: PopupAdjustment.None
|
||||||
|
}
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: parentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
id: grab
|
||||||
|
active: true
|
||||||
|
windows: [popup, root.bar]
|
||||||
|
onCleared: {
|
||||||
|
if (!active) {
|
||||||
|
root.shownItem.closed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HyprlandWindow.opacity: root.scaleMul
|
||||||
|
|
||||||
|
HyprlandWindow.visibleMask: popup.mask
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
|
||||||
|
function onScaleMulChanged() {
|
||||||
|
popup.mask.changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRectangle {
|
||||||
|
id: parentItem
|
||||||
|
width: targetWidth
|
||||||
|
height: targetHeight
|
||||||
|
x: targetX
|
||||||
|
y: root.gaps
|
||||||
|
|
||||||
|
transform: Scale {
|
||||||
|
origin.x: parentItem.targetX
|
||||||
|
origin.y: 0
|
||||||
|
xScale: 1
|
||||||
|
yScale: root.scaleMul
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var targetWidth: root.shownItem?.implicitWidth ?? 0
|
||||||
|
readonly property var targetHeight: root.shownItem?.implicitHeight ?? 0
|
||||||
|
|
||||||
|
readonly property var targetX: {
|
||||||
|
if (root.shownItem == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let owner = root.shownItem.owner;
|
||||||
|
let bar = root.bar;
|
||||||
|
let isCentered = root.shownItem.centered;
|
||||||
|
let xPos = owner.mapToItem(bar.contentItem, 0, bar.height, owner.width, 0).x;
|
||||||
|
|
||||||
|
let rightEdge = xPos + targetWidth;
|
||||||
|
let maxRightEdge = popup.width;
|
||||||
|
|
||||||
|
if (isCentered) {
|
||||||
|
return xPos - (targetWidth / 2) + (owner.width / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightEdge > maxRightEdge) {
|
||||||
|
// touching right edge, reposition
|
||||||
|
// console.log("touching right edge");
|
||||||
|
return maxRightEdge - targetWidth - root.gaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
root.parentItem = this;
|
||||||
|
|
||||||
|
if (root.activeItem) {
|
||||||
|
root.activeItem.parent = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make a close animation, a little complicated, will need to track if an animation is running
|
||||||
|
// and stop unload from occuring until its done, in the LazyLoader.
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
enabled: root.lastActiveItem != null
|
||||||
|
SmoothedAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
enabled: root.lastActiveItem != null
|
||||||
|
SmoothedAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
enabled: root.lastActiveItem != null
|
||||||
|
SmoothedAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmoothedAnimation on height {
|
||||||
|
// duration: 200
|
||||||
|
// easing.type: Easing.InOutQuad
|
||||||
|
// to: parentItem.targetHeight
|
||||||
|
// onToChanged: restart()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
shell/bar/PopupItem.qml
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import QtQuick
|
||||||
|
import qs.widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
visible: false
|
||||||
|
opacity: root.targetOpacity
|
||||||
|
|
||||||
|
onShowChanged: {
|
||||||
|
if (show) {
|
||||||
|
popup.setItem(this);
|
||||||
|
} else {
|
||||||
|
popup.removeItem(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTargetVisibleChanged: {
|
||||||
|
if (targetVisible) {
|
||||||
|
visible = true;
|
||||||
|
targetOpacity = 1;
|
||||||
|
} else {
|
||||||
|
console.log("closed");
|
||||||
|
closed();
|
||||||
|
targetOpacity = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTargetOpacityChanged: {
|
||||||
|
if (!targetVisible && targetOpacity == 0) {
|
||||||
|
visible = false;
|
||||||
|
this.parent = null;
|
||||||
|
if (popup)
|
||||||
|
popup.onHidden(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property alias contentItem: contentItem
|
||||||
|
default property alias data: contentItem.data
|
||||||
|
readonly property Item item: contentItem
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentItem
|
||||||
|
anchors.fill: parent
|
||||||
|
// anchors.margins: 5
|
||||||
|
|
||||||
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
required property var popup
|
||||||
|
required property var owner
|
||||||
|
property bool centered: false
|
||||||
|
property bool show: false
|
||||||
|
|
||||||
|
signal closed
|
||||||
|
|
||||||
|
property bool targetVisible: false
|
||||||
|
property real targetOpacity: 0
|
||||||
|
|
||||||
|
Behavior on targetOpacity {
|
||||||
|
id: opacityAnimation
|
||||||
|
SmoothedAnimation {
|
||||||
|
velocity: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
shell/bar/Workspaces.qml
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 6
|
||||||
|
visible: Hyprland.monitors.values.length != 0
|
||||||
|
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: workspaceButtons
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: Hyprland.workspaces.values.slice().filter(workspace => workspace.monitor === Hyprland.monitorFor(screen))
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
radius: height / 2
|
||||||
|
|
||||||
|
color: {
|
||||||
|
let value = ShellSettings.colors.active_translucent;
|
||||||
|
|
||||||
|
if (!modelData?.id || !Hyprland.focusedMonitor?.activeWorkspace?.id)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (workspaceButton.containsMouse) {
|
||||||
|
value = ShellSettings.colors.highlight;
|
||||||
|
} else if (Hyprland.focusedMonitor.activeWorkspace.id === modelData.id) {
|
||||||
|
value = ShellSettings.colors.highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredHeight: 12
|
||||||
|
Layout.preferredWidth: {
|
||||||
|
if (Hyprland.focusedMonitor?.activeWorkspace?.id === modelData?.id)
|
||||||
|
return 25;
|
||||||
|
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Behavior on Layout.preferredWidth {
|
||||||
|
SmoothedAnimation {
|
||||||
|
duration: 150
|
||||||
|
velocity: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 100
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: workspaceButton
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onPressed: Hyprland.dispatch(`workspace ${parent.modelData.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
shell/bar/mpris/Button.qml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import QtQuick
|
||||||
|
import "../../mpris" as Mpris
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Widgets.MaterialButton {
|
||||||
|
id: root
|
||||||
|
radius: 6
|
||||||
|
implicitWidth: mediaInfo.contentWidth + 8
|
||||||
|
implicitHeight: parent.height
|
||||||
|
// onClicked: {
|
||||||
|
// popup.visible = !popup.visible;
|
||||||
|
// }
|
||||||
|
|
||||||
|
required property var bar
|
||||||
|
property var player: Mpris.Controller.trackedPlayer
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: mediaInfo
|
||||||
|
text: root.player?.trackTitle ?? ""
|
||||||
|
color: root.containsMouse ? ShellSettings.colors["inverse_primary"] : ShellSettings.colors["inverse_surface"]
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.pointSize: 11
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WidgetWindow {
|
||||||
|
// id: popup
|
||||||
|
// visible: false
|
||||||
|
// parentWindow: root.bar
|
||||||
|
//
|
||||||
|
// // anchor.window: root.bar
|
||||||
|
// }
|
||||||
9
shell/bar/mpris/WidgetWindow.qml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
color: "red"
|
||||||
|
implicitWidth: 500
|
||||||
|
implicitHeight: 500
|
||||||
|
|
||||||
|
}
|
||||||
70
shell/bar/notifications/NotificationButton.qml
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var bar
|
||||||
|
property var implicitSize: 0
|
||||||
|
readonly property real actualSize: Math.min(root.width, root.height)
|
||||||
|
|
||||||
|
implicitWidth: parent.height
|
||||||
|
implicitHeight: parent.height
|
||||||
|
|
||||||
|
NotificationCenter {
|
||||||
|
id: notificationCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: mouseArea.containsMouse ? ShellSettings.colors["primary"] : "transparent"
|
||||||
|
radius: 5
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: {
|
||||||
|
if (notificationCenter.visible) {
|
||||||
|
notificationCenter.hide();
|
||||||
|
} else {
|
||||||
|
notificationCenter.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
implicitWidth: root.implicitSize
|
||||||
|
implicitHeight: root.implicitSize
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: root.actualSize
|
||||||
|
height: root.actualSize
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: IconImage {
|
||||||
|
implicitSize: root.actualSize
|
||||||
|
source: "root:resources/general/notification.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: mouseArea.containsMouse ? ShellSettings.colors["inverse_primary"] : ShellSettings.colors["inverse_surface"]
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: notification number overlay
|
||||||
|
}
|
||||||
72
shell/bar/power/PowerMenu.qml
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.UPower
|
||||||
|
import qs.widgets
|
||||||
|
import qs.bar
|
||||||
|
import qs
|
||||||
|
|
||||||
|
// todo: redo the tray icon handling
|
||||||
|
StyledMouseArea {
|
||||||
|
id: root
|
||||||
|
implicitWidth: height + 8 // for margin
|
||||||
|
visible: UPower.displayDevice.isLaptopBattery
|
||||||
|
onClicked: showMenu = !showMenu
|
||||||
|
|
||||||
|
required property var bar
|
||||||
|
property bool showMenu: false
|
||||||
|
|
||||||
|
Item {
|
||||||
|
implicitWidth: parent.height
|
||||||
|
implicitHeight: parent.height
|
||||||
|
anchors.centerIn: parent
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: IconImage {
|
||||||
|
implicitSize: root.width
|
||||||
|
source: "root:resources/battery/battery.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: batteryBackground
|
||||||
|
color: Qt.color(ShellSettings.colors["surface"]).lighter(4)
|
||||||
|
opacity: 0.75
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: batteryPercentage
|
||||||
|
width: (parent.width - 4) * UPower.displayDevice.percentage
|
||||||
|
color: ShellSettings.colors["inverse_surface"]
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: batteryBackground.left
|
||||||
|
top: batteryBackground.top
|
||||||
|
bottom: batteryBackground.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property PopupItem menu: PopupItem {
|
||||||
|
owner: root
|
||||||
|
popup: root.bar.popup
|
||||||
|
show: root.showMenu
|
||||||
|
onClosed: root.showMenu = false
|
||||||
|
centered: true
|
||||||
|
|
||||||
|
implicitWidth: 250
|
||||||
|
implicitHeight: 250
|
||||||
|
}
|
||||||
|
}
|
||||||
96
shell/bar/systray/SysTray.qml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.SystemTray
|
||||||
|
import "../../widgets"
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// 1. Get rid of leftItem/rightItem properties on menu
|
||||||
|
// 2. Load menu properly, right now its pretty buggy
|
||||||
|
// 3. Fix bug that causes close on update (nm-applet wifi networks updating)
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
spacing: 5
|
||||||
|
visible: SystemTray.items.values.length > 0
|
||||||
|
|
||||||
|
required property var bar
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
model: SystemTray.items
|
||||||
|
|
||||||
|
delegate: StyledMouseArea {
|
||||||
|
id: button
|
||||||
|
Layout.preferredWidth: parent.height
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
required property SystemTrayItem modelData
|
||||||
|
property bool showMenu: false
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
menuOpener.menu = modelData.menu;
|
||||||
|
showMenu = !showMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: trayIcon
|
||||||
|
anchors.fill: parent
|
||||||
|
source: {
|
||||||
|
// console.log(trayField.modelData.id);
|
||||||
|
switch (button.modelData.id) {
|
||||||
|
case "obs":
|
||||||
|
return "image://icon/obs-tray";
|
||||||
|
default:
|
||||||
|
return button.modelData.icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
}
|
||||||
|
|
||||||
|
property PopupItem menu: PopupItem {
|
||||||
|
id: menu
|
||||||
|
owner: button
|
||||||
|
popup: root.bar.popup
|
||||||
|
show: button.showMenu
|
||||||
|
onClosed: button.showMenu = false
|
||||||
|
|
||||||
|
implicitWidth: content.implicitWidth + (2 * 8)
|
||||||
|
implicitHeight: content.implicitHeight + (2 * 8)
|
||||||
|
|
||||||
|
property var leftItem: false
|
||||||
|
property var rightItem: false
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
spacing: 2
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: menuOpener.children
|
||||||
|
|
||||||
|
delegate: TrayMenuItem {
|
||||||
|
id: sysTrayContent
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
rootMenu: menu
|
||||||
|
|
||||||
|
onInteracted: {
|
||||||
|
button.showMenu = false;
|
||||||
|
menuOpener.menu = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
177
shell/bar/systray/TrayMenuEntry.qml
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
required property QsMenuEntry menuData
|
||||||
|
required property var rootMenu
|
||||||
|
signal interacted
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (menuData?.buttonType !== QsMenuButtonType.None || menuData?.icon != "") {
|
||||||
|
rootMenu.leftItem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menuData?.hasChildren) {
|
||||||
|
rootMenu.rightItem = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 25
|
||||||
|
radius: 4
|
||||||
|
color: {
|
||||||
|
if (!root.menuData?.enabled)
|
||||||
|
return "transparent";
|
||||||
|
|
||||||
|
if (entryArea.containsMouse) {
|
||||||
|
return ShellSettings.colors.active_translucent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperMouseArea {
|
||||||
|
id: entryArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (!root.menuData?.enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (root.menuData?.hasChildren) {
|
||||||
|
subTrayMenu.visible = !subTrayMenu.visible;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.menuData?.triggered();
|
||||||
|
root.interacted();
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: menuEntry
|
||||||
|
spacing: 5
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: root.rootMenu.leftItem
|
||||||
|
Layout.preferredWidth: 20
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: 5
|
||||||
|
|
||||||
|
RadioButton {
|
||||||
|
id: radioButton
|
||||||
|
visible: (root.menuData?.buttonType === QsMenuButtonType.RadioButton) ?? false
|
||||||
|
checked: (root.menuData?.checkState) ?? false
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: checkBox
|
||||||
|
visible: (root.menuData?.buttonType === QsMenuButtonType.CheckBox) ?? false
|
||||||
|
checked: (root.menuData?.checkState) ?? false
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: entryImage
|
||||||
|
visible: (root.menuData?.buttonType === QsMenuButtonType.None && root.menuData?.icon !== "") ?? false
|
||||||
|
source: (root.menuData?.icon) ?? ""
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: text
|
||||||
|
text: root.menuData?.text ?? ""
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: {
|
||||||
|
let color = Qt.color(ShellSettings.colors.active);
|
||||||
|
|
||||||
|
if (!root.menuData?.enabled)
|
||||||
|
return color.darker(2);
|
||||||
|
|
||||||
|
// if (entryArea.containsMouse)
|
||||||
|
// return Qt.color(ShellSettings.colors["inverse_primary"]);
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: root.rootMenu.rightItem
|
||||||
|
Layout.preferredHeight: 20
|
||||||
|
Layout.preferredWidth: 20
|
||||||
|
Layout.rightMargin: 5
|
||||||
|
|
||||||
|
Widgets.IconButton {
|
||||||
|
id: arrowButton
|
||||||
|
visible: root.menuData?.hasChildren ?? false
|
||||||
|
activeRectangle: false
|
||||||
|
source: "root:resources/general/right-arrow.svg"
|
||||||
|
rotation: subTrayMenu.visible ? 90 : 0
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.expanded = !root.expanded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
id: subTrayMenu
|
||||||
|
color: ShellSettings.colors.surface_container_translucent
|
||||||
|
radius: 8
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
border {
|
||||||
|
width: 1
|
||||||
|
color: ShellSettings.colors.active_translucent
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
menu: root.menuData
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: subTrayContainer
|
||||||
|
spacing: 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: menuOpener.children
|
||||||
|
|
||||||
|
delegate: BoundComponent {
|
||||||
|
id: subMenuEntry
|
||||||
|
source: "TrayMenuItem.qml"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
required property var modelData
|
||||||
|
property var rootMenu: root.rootMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
shell/bar/systray/TrayMenuItem.qml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
required property QsMenuEntry modelData
|
||||||
|
required property var rootMenu
|
||||||
|
property var leftItem
|
||||||
|
signal interacted
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: (root.modelData?.isSeparator ?? false)
|
||||||
|
color: ShellSettings.colors.inactive_translucent
|
||||||
|
// opacity: 0.1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 2
|
||||||
|
Layout.leftMargin: 8
|
||||||
|
Layout.rightMargin: 8
|
||||||
|
}
|
||||||
|
|
||||||
|
TrayMenuEntry {
|
||||||
|
visible: !root.modelData?.isSeparator
|
||||||
|
rootMenu: root.rootMenu
|
||||||
|
menuData: root.modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
onInteracted: root.interacted()
|
||||||
|
}
|
||||||
|
}
|
||||||
70
shell/bar/volume/ApplicationMixer.qml
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: sinkLoader
|
||||||
|
active: sink
|
||||||
|
|
||||||
|
property PwNode sink: Pipewire.defaultAudioSink
|
||||||
|
|
||||||
|
sourceComponent: WrapperItem {
|
||||||
|
PwNodeLinkTracker {
|
||||||
|
id: linkTracker
|
||||||
|
node: sinkLoader.sink
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Repeater {
|
||||||
|
model: linkTracker.linkGroups
|
||||||
|
|
||||||
|
delegate: Loader {
|
||||||
|
id: nodeLoader
|
||||||
|
active: modelData.source !== null
|
||||||
|
Layout.preferredWidth: 350
|
||||||
|
Layout.preferredHeight: 45
|
||||||
|
|
||||||
|
required property PwLinkGroup modelData
|
||||||
|
|
||||||
|
sourceComponent: VolumeCard {
|
||||||
|
id: nodeCard
|
||||||
|
node: nodeLoader.modelData.source
|
||||||
|
text: node.properties["media.name"] ?? ""
|
||||||
|
|
||||||
|
// if icon-name is undefined, just gonna fallback on the application name
|
||||||
|
icon: IconImage {
|
||||||
|
source: {
|
||||||
|
if (nodeCard.node.properties["application.icon-name"] !== undefined)
|
||||||
|
return `image://icon/${nodeCard.node.properties["application.icon-name"]}`;
|
||||||
|
|
||||||
|
let applicationName = nodeCard.node.properties["application.name"];
|
||||||
|
return `image://icon/${applicationName?.toLowerCase() ?? "image-missing"}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button: Widgets.FontIconButton {
|
||||||
|
hoverEnabled: false
|
||||||
|
iconName: nodeCard.node.audio.muted ? "volume_off" : "volume_up"
|
||||||
|
checked: !nodeCard.node.audio.muted
|
||||||
|
inactiveColor: ShellSettings.colors["surface_container_highest"]
|
||||||
|
onClicked: {
|
||||||
|
nodeCard.node.audio.muted = !nodeCard.node.audio.muted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
shell/bar/volume/DeviceMixer.qml
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// headphones
|
||||||
|
// don't load until the node is not null
|
||||||
|
Loader {
|
||||||
|
id: sinkLoader
|
||||||
|
active: sink !== null
|
||||||
|
Layout.preferredWidth: 350
|
||||||
|
Layout.preferredHeight: 45
|
||||||
|
|
||||||
|
property PwNode sink: Pipewire.defaultAudioSink
|
||||||
|
|
||||||
|
sourceComponent: VolumeCard {
|
||||||
|
id: sinkCard
|
||||||
|
node: sinkLoader.sink
|
||||||
|
button: Widgets.FontIconButton {
|
||||||
|
hoverEnabled: false
|
||||||
|
iconName: sinkCard.node.audio.muted ? "volume_off" : "volume_up"
|
||||||
|
checked: !sinkCard.node.audio.muted
|
||||||
|
inactiveColor: ShellSettings.colors["surface_container_highest"]
|
||||||
|
onClicked: {
|
||||||
|
sinkCard.node.audio.muted = !sinkCard.node.audio.muted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// microphone, same as above
|
||||||
|
Loader {
|
||||||
|
id: sourceLoader
|
||||||
|
active: source !== null
|
||||||
|
Layout.preferredWidth: 350
|
||||||
|
Layout.preferredHeight: 45
|
||||||
|
|
||||||
|
property PwNode source: Pipewire.defaultAudioSource
|
||||||
|
|
||||||
|
sourceComponent: VolumeCard {
|
||||||
|
id: sourceCard
|
||||||
|
node: sourceLoader.source
|
||||||
|
button: Widgets.FontIconButton {
|
||||||
|
hoverEnabled: false
|
||||||
|
iconName: sourceCard.node.audio.muted ? "mic_off" : "mic"
|
||||||
|
checked: !sourceCard.node.audio.muted
|
||||||
|
inactiveColor: ShellSettings.colors["surface_container_highest"]
|
||||||
|
onClicked: {
|
||||||
|
sourceCard.node.audio.muted = !sourceCard.node.audio.muted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
shell/bar/volume/VolumeCard.qml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
id: root
|
||||||
|
color: ShellSettings.colors["surface_container"]
|
||||||
|
radius: width / 2
|
||||||
|
margin: 6
|
||||||
|
|
||||||
|
required property PwNode node
|
||||||
|
property string text
|
||||||
|
property Component button
|
||||||
|
property Component icon
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
id: tracker
|
||||||
|
objects: [root.node]
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Widgets.MaterialSlider {
|
||||||
|
value: root.node.audio.volume ?? 0
|
||||||
|
text: root.text
|
||||||
|
icon: root.icon
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
// only allow changes when the node is ready other wise you will combust
|
||||||
|
if (!root.node.ready)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.node.audio.volume = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: buttonLoader
|
||||||
|
sourceComponent: root.button
|
||||||
|
|
||||||
|
Layout.preferredWidth: this.height
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
shell/bar/volume/VolumeControl.qml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
|
||||||
|
WrapperItem {
|
||||||
|
id: root
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Widgets.TabBar {
|
||||||
|
id: tabBar
|
||||||
|
model: ["headphones", "tune"]
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 35
|
||||||
|
}
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
id: page
|
||||||
|
currentIndex: tabBar.currentIndex
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: currentItem ? currentItem.implicitHeight : 0
|
||||||
|
|
||||||
|
readonly property Item currentItem: children[currentIndex]
|
||||||
|
|
||||||
|
DeviceMixer {}
|
||||||
|
ApplicationMixer {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
shell/bar/volume/VolumeIndicator.qml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import QtQuick
|
||||||
|
import "../../widgets/" as Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var popup
|
||||||
|
|
||||||
|
Widgets.FontIconButton {
|
||||||
|
id: button
|
||||||
|
iconName: "volume_up"
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (root.popup.content == volumeMenu) {
|
||||||
|
root.popup.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.popup.set(this, volumeMenu);
|
||||||
|
root.popup.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeControl {
|
||||||
|
id: volumeMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
34
shell/greeter.qml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Services.Greetd
|
||||||
|
import "lockscreen"
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
GreeterContext {
|
||||||
|
id: context
|
||||||
|
|
||||||
|
onLaunch: {
|
||||||
|
lock.locked = false;
|
||||||
|
Greetd.launch(["hyprland"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WlSessionLock {
|
||||||
|
id: lock
|
||||||
|
locked: true
|
||||||
|
|
||||||
|
WlSessionLockSurface {
|
||||||
|
LockSurface {
|
||||||
|
state: context.state
|
||||||
|
|
||||||
|
// TODO: env var for wallpaper
|
||||||
|
wallpaper: "root:resources/wallpapers/wallhaven-96y9qk.jpg"
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
313
shell/launcher/Controller.qml
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool launcherOpen: false
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "launcher"
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
persist.launcherOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
persist.launcherOpen = !persist.launcherOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
activeAsync: persist.launcherOpen
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
implicitWidth: 500
|
||||||
|
implicitHeight: 7 + searchContainer.implicitHeight + list.topMargin * 2 + list.delegateHeight * 10
|
||||||
|
color: "transparent"
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
WlrLayershell.namespace: "shell:launcher"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: container
|
||||||
|
color: ShellSettings.colors["surface"]
|
||||||
|
radius: 18
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 7
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: searchContainer
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: searchbox.implicitHeight + 15
|
||||||
|
radius: 10
|
||||||
|
color: ShellSettings.colors["surface_container"]
|
||||||
|
border.color: ShellSettings.colors["secondary"]
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: searchbox
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 5
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: search
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: ShellSettings.colors["inverse_surface"]
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
Keys.forwardTo: [list]
|
||||||
|
Keys.onEscapePressed: persist.launcherOpen = false
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
if (event.key == Qt.Key_J) {
|
||||||
|
list.currentIndex = list.currentIndex == list.count - 1 ? 0 : list.currentIndex + 1;
|
||||||
|
event.accepted = true;
|
||||||
|
} else if (event.key == Qt.Key_K) {
|
||||||
|
list.currentIndex = list.currentIndex == 0 ? list.count - 1 : list.currentIndex - 1;
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (list.currentItem) {
|
||||||
|
list.currentItem.clicked(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
list.currentIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: list
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
cacheBuffer: 0 // works around QTBUG-131106
|
||||||
|
//reuseItems: true
|
||||||
|
model: ScriptModel {
|
||||||
|
values: DesktopEntries.applications.values.map(object => {
|
||||||
|
const stxt = search.text.toLowerCase();
|
||||||
|
const ntxt = object.name.toLowerCase();
|
||||||
|
let si = 0;
|
||||||
|
let ni = 0;
|
||||||
|
|
||||||
|
let matches = [];
|
||||||
|
let startMatch = -1;
|
||||||
|
|
||||||
|
for (let si = 0; si != stxt.length; ++si) {
|
||||||
|
const sc = stxt[si];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Drop any entries with letters that don't exist in order
|
||||||
|
if (ni == ntxt.length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const nc = ntxt[ni++];
|
||||||
|
|
||||||
|
if (nc == sc) {
|
||||||
|
if (startMatch == -1)
|
||||||
|
startMatch = ni;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch
|
||||||
|
});
|
||||||
|
|
||||||
|
startMatch = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startMatch != -1) {
|
||||||
|
matches.push({
|
||||||
|
index: startMatch,
|
||||||
|
length: ni - startMatch + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
object: object,
|
||||||
|
matches: matches
|
||||||
|
};
|
||||||
|
}).filter(entry => entry !== null).sort((a, b) => {
|
||||||
|
let ai = 0;
|
||||||
|
let bi = 0;
|
||||||
|
let s = 0;
|
||||||
|
|
||||||
|
while (ai != a.matches.length && bi != b.matches.length) {
|
||||||
|
const am = a.matches[ai];
|
||||||
|
const bm = b.matches[bi];
|
||||||
|
|
||||||
|
s = bm.length - am.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
s = am.index - bm.index;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
++ai;
|
||||||
|
++bi;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = a.matches.length - b.matches.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
s = a.object.name.length - b.object.name.length;
|
||||||
|
if (s != 0)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
return a.object.name.localeCompare(b.object.name);
|
||||||
|
}).map(entry => entry.object)
|
||||||
|
|
||||||
|
onValuesChanged: list.currentIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
topMargin: 7
|
||||||
|
bottomMargin: list.count == 0 ? 0 : 7
|
||||||
|
|
||||||
|
add: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displaced: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove: Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
property: "y"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
radius: 12
|
||||||
|
color: ShellSettings.colors["primary"]
|
||||||
|
}
|
||||||
|
|
||||||
|
keyNavigationEnabled: true
|
||||||
|
keyNavigationWraps: true
|
||||||
|
highlightMoveVelocity: -1
|
||||||
|
highlightMoveDuration: 100
|
||||||
|
preferredHighlightBegin: list.topMargin
|
||||||
|
preferredHighlightEnd: list.height - list.bottomMargin
|
||||||
|
highlightRangeMode: ListView.ApplyRange
|
||||||
|
snapMode: ListView.SnapToItem
|
||||||
|
|
||||||
|
readonly property real delegateHeight: 44
|
||||||
|
|
||||||
|
delegate: MouseArea {
|
||||||
|
required property DesktopEntry modelData
|
||||||
|
|
||||||
|
implicitHeight: list.delegateHeight
|
||||||
|
implicitWidth: ListView.view.width
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
modelData.execute();
|
||||||
|
persist.launcherOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: delegateLayout
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
asynchronous: true
|
||||||
|
implicitSize: 30
|
||||||
|
source: Quickshell.iconPath(modelData.icon)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: modelData.name
|
||||||
|
color: ShellSettings.colors["inverse_surface"]
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
53
shell/lockscreen/Controller.qml
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool locked: false
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "lockscreen"
|
||||||
|
|
||||||
|
function lock(): void {
|
||||||
|
persist.locked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LockContext {
|
||||||
|
id: context
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: context.state
|
||||||
|
|
||||||
|
function onUnlocked() {
|
||||||
|
persist.locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WlSessionLock {
|
||||||
|
id: lock
|
||||||
|
locked: persist.locked
|
||||||
|
|
||||||
|
WlSessionLockSurface {
|
||||||
|
LockSurface {
|
||||||
|
state: context.state
|
||||||
|
wallpaper: ShellSettings.settings.wallpaperUrl
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
39
shell/lockscreen/GreeterContext.qml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Greetd
|
||||||
|
import "../lockscreen"
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
signal launch
|
||||||
|
|
||||||
|
property LockState state: LockState {
|
||||||
|
onTryUnlock: {
|
||||||
|
this.unlockInProgress = true;
|
||||||
|
|
||||||
|
// TODO: env var for user
|
||||||
|
Greetd.createSession("koss");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Greetd
|
||||||
|
|
||||||
|
function onAuthMessage(message: string, error: bool, responseRequired: bool, echoResponse: bool) {
|
||||||
|
if (responseRequired) {
|
||||||
|
Greetd.respond(root.state.currentText);
|
||||||
|
} // else ignore - only supporting passwords
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAuthFailure() {
|
||||||
|
root.state.currentText = "";
|
||||||
|
root.state.failed();
|
||||||
|
root.state.unlockInProgress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReadyToLaunch() {
|
||||||
|
root.state.unlockInProgress = false;
|
||||||
|
root.launch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
shell/lockscreen/LockContext.qml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Pam
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property LockState state: LockState {
|
||||||
|
onTryUnlock: {
|
||||||
|
if (this.currentText === "")
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.unlockInProgress = true;
|
||||||
|
pam.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PamContext {
|
||||||
|
id: pam
|
||||||
|
|
||||||
|
// Its best to have a custom pam config for quickshell, as the system one
|
||||||
|
// might not be what your interface expects, and break in some way.
|
||||||
|
// This particular example only supports passwords.
|
||||||
|
configDirectory: "pam"
|
||||||
|
config: "password.conf"
|
||||||
|
|
||||||
|
// pam_unix will ask for a response for the password prompt
|
||||||
|
onPamMessage: {
|
||||||
|
if (this.responseRequired) {
|
||||||
|
this.respond(root.state.currentText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pam_unix won't send any important messages so all we need is the completion status.
|
||||||
|
onCompleted: result => {
|
||||||
|
if (result == PamResult.Success) {
|
||||||
|
root.state.unlocked();
|
||||||
|
root.state.currentText = "";
|
||||||
|
} else {
|
||||||
|
root.state.showFailure = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.state.unlockInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
shell/lockscreen/LockState.qml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
property string currentText: ""
|
||||||
|
property bool unlockInProgress: false
|
||||||
|
property bool showFailure: false
|
||||||
|
signal unlocked
|
||||||
|
signal failed
|
||||||
|
signal tryUnlock
|
||||||
|
|
||||||
|
onCurrentTextChanged: showFailure = false
|
||||||
|
}
|
||||||
197
shell/lockscreen/LockSurface.qml
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property LockState state
|
||||||
|
required property string wallpaper
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: bgImage
|
||||||
|
source: root.wallpaper
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
FastBlur {
|
||||||
|
anchors.fill: bgImage
|
||||||
|
source: bgImage
|
||||||
|
radius: 80
|
||||||
|
transparentBorder: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
opacity: 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop {
|
||||||
|
position: 0.0
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.2)
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 0.5
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.1)
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.0
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Date and time display
|
||||||
|
ColumnLayout {
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 120
|
||||||
|
}
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: clock
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.pointSize: 72
|
||||||
|
font.weight: Font.Light
|
||||||
|
color: "white"
|
||||||
|
text: {
|
||||||
|
const now = this.date;
|
||||||
|
let hours = now.getHours();
|
||||||
|
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||||
|
const ampm = hours >= 12 ? 'PM' : 'AM';
|
||||||
|
hours = hours % 12;
|
||||||
|
hours = hours ? hours : 12; // 0 should be 12
|
||||||
|
return `${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
property var date: new Date()
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
interval: 1000
|
||||||
|
onTriggered: clock.date = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: DropShadow {
|
||||||
|
horizontalOffset: 0
|
||||||
|
verticalOffset: 0
|
||||||
|
radius: 20
|
||||||
|
samples: 41
|
||||||
|
color: Qt.rgba(1, 1, 1, 0.3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// login section
|
||||||
|
ColumnLayout {
|
||||||
|
visible: Window.active
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 30
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: profileImage
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: 120
|
||||||
|
Layout.preferredHeight: 120
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: profileImage.width
|
||||||
|
height: profileImage.height
|
||||||
|
radius: width / 2
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "root:resources/general/pfp.png"
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// password input, should probably split this out into a seperate comp
|
||||||
|
LoginField {
|
||||||
|
id: passwordBox
|
||||||
|
enabled: !root.state.unlockInProgress
|
||||||
|
|
||||||
|
Layout.preferredWidth: 250
|
||||||
|
Layout.preferredHeight: 30
|
||||||
|
Layout.maximumHeight: 30
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
onTextChanged: root.state.currentText = this.text
|
||||||
|
onAccepted: root.state.tryUnlock()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.state
|
||||||
|
|
||||||
|
function onCurrentTextChanged() {
|
||||||
|
if (!passwordBox.shaking) {
|
||||||
|
passwordBox.text = root.state.currentText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShowFailureChanged() {
|
||||||
|
if (root.state.showFailure && !passwordBox.shaking) {
|
||||||
|
passwordBox.shaking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hint text
|
||||||
|
Text {
|
||||||
|
text: "Press Enter to unlock"
|
||||||
|
color: Qt.rgba(1, 1, 1, 0.5)
|
||||||
|
font.pointSize: 12
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
opacity: passwordBox.text.length > 0 ? 1.0 : 0.0
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 60
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testing button
|
||||||
|
Button {
|
||||||
|
visible: false
|
||||||
|
text: "Emergency Unlock"
|
||||||
|
onClicked: root.state.unlocked()
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
margins: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
shell/lockscreen/LoginField.qml
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: root
|
||||||
|
color: "white"
|
||||||
|
scale: activeFocus ? 1.05 : 1.0
|
||||||
|
padding: 8
|
||||||
|
focus: true
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
inputMethodHints: Qt.ImhSensitiveData
|
||||||
|
font.pointSize: 11
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Qt.rgba(1, 1, 1, 0.1)
|
||||||
|
border.color: root.activeFocus ? Qt.rgba(1, 1, 1, 0.5) : Qt.rgba(1, 1, 1, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
radius: 8
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: FastBlur {
|
||||||
|
radius: 10
|
||||||
|
transparentBorder: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
id: shakeTransform
|
||||||
|
x: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool shaking: false
|
||||||
|
|
||||||
|
onShakingChanged: {
|
||||||
|
if (shaking)
|
||||||
|
shakeAnimation.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: shakeAnimation
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: shakeTransform
|
||||||
|
property: "x"
|
||||||
|
to: -8
|
||||||
|
duration: 50
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: shakeTransform
|
||||||
|
property: "x"
|
||||||
|
to: 8
|
||||||
|
duration: 100
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: shakeTransform
|
||||||
|
property: "x"
|
||||||
|
to: -6
|
||||||
|
duration: 80
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: shakeTransform
|
||||||
|
property: "x"
|
||||||
|
to: 6
|
||||||
|
duration: 80
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: shakeTransform
|
||||||
|
property: "x"
|
||||||
|
to: -3
|
||||||
|
duration: 60
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
|
||||||
|
onFinished: {
|
||||||
|
root.shaking = false;
|
||||||
|
root.text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
shell/lockscreen/pam/fingerprint.conf
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
auth sufficient pam_fprintd.so
|
||||||
1
shell/lockscreen/pam/password.conf
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
auth required pam_unix.so
|
||||||
77
shell/mpris/Controller.qml
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
property MprisPlayer trackedPlayer
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "mpris"
|
||||||
|
|
||||||
|
function next(): void {
|
||||||
|
root.trackedPlayer.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
function prev(): void {
|
||||||
|
root.trackedPlayer.previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
function play(): void {
|
||||||
|
root.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause(): void {
|
||||||
|
root.trackedPlayer.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
function play_pause(): void {
|
||||||
|
if (root.trackedPlayer.isPlaying) {
|
||||||
|
root.trackedPlayer.pause();
|
||||||
|
} else {
|
||||||
|
root.trackedPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Instantiator {
|
||||||
|
model: Mpris.players
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
required property MprisPlayer modelData
|
||||||
|
target: modelData
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (root.trackedPlayer == null || modelData.isPlaying) {
|
||||||
|
root.trackedPlayer = modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
if (root.trackedPlayer == null || !root.trackedPlayer.isPlaying) {
|
||||||
|
for (const player of Mpris.players.values) {
|
||||||
|
if (player.playbackState.isPlaying) {
|
||||||
|
root.trackedPlayer = player;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.trackedPlayer == null && Mpris.players.values.length != 0) {
|
||||||
|
root.trackedPlayer = Mpris.players.values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPlaybackStateChanged() {
|
||||||
|
if (root.trackedPlayer !== modelData)
|
||||||
|
root.trackedPlayer = modelData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {}
|
||||||
|
}
|
||||||
218
shell/notifications/ActiveToast.qml
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import "../widgets/" as Widgets
|
||||||
|
import ".."
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var notification
|
||||||
|
signal expired(Notification notification)
|
||||||
|
signal closed(Notification notification)
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: Math.min(row.implicitHeight + 30, 400)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: container
|
||||||
|
radius: 10
|
||||||
|
color: ShellSettings.colors["surface_container"]
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: timerController
|
||||||
|
property int totalDuration: 5000
|
||||||
|
property int remainingTime: totalDuration
|
||||||
|
property bool isRunning: false
|
||||||
|
property real lastTime: 0
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: internalTimer
|
||||||
|
interval: 16
|
||||||
|
repeat: true
|
||||||
|
running: timerController.isRunning
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
var currentTime = Date.now();
|
||||||
|
if (timerController.lastTime > 0) {
|
||||||
|
var delta = currentTime - timerController.lastTime;
|
||||||
|
timerController.remainingTime -= delta;
|
||||||
|
if (timerController.remainingTime <= 0) {
|
||||||
|
timerController.isRunning = false;
|
||||||
|
root.expired(root.notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timerController.lastTime = currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if (!isRunning) {
|
||||||
|
lastTime = Date.now();
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause() {
|
||||||
|
isRunning = false;
|
||||||
|
lastTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onContainsMouseChanged: {
|
||||||
|
progressAnimation.paused = containsMouse;
|
||||||
|
if (containsMouse) {
|
||||||
|
timerController.pause();
|
||||||
|
} else {
|
||||||
|
timerController.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: topRow
|
||||||
|
spacing: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: root.notification.appIcon != ""
|
||||||
|
source: Quickshell.iconPath(root.notification.appIcon)
|
||||||
|
implicitSize: 24
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: appName
|
||||||
|
text: root.notification.appName
|
||||||
|
color: ShellSettings.colors["inverse_surface"]
|
||||||
|
font.pointSize: 11
|
||||||
|
font.bold: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
Layout.preferredWidth: implicitWidth
|
||||||
|
Layout.maximumWidth: topRow.width * 0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: summaryText
|
||||||
|
text: root.notification.summary
|
||||||
|
color: ShellSettings.colors["inverse_surface"]
|
||||||
|
font.pointSize: 11
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: closeButton
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: progressCircle
|
||||||
|
anchors.fill: parent
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
property real progress: 1.0
|
||||||
|
onProgressChanged: requestPaint()
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
|
||||||
|
var centerX = width / 2;
|
||||||
|
var centerY = height / 2;
|
||||||
|
var radius = Math.min(width, height) / 2 - 2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + 2 * Math.PI * progress);
|
||||||
|
ctx.strokeStyle = ShellSettings.colors["primary"];
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: progressAnimation
|
||||||
|
target: progressCircle
|
||||||
|
property: "progress"
|
||||||
|
from: 1.0
|
||||||
|
to: 0.0
|
||||||
|
duration: 5000
|
||||||
|
running: true
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: closeButtonBg
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
color: "#FF474D"
|
||||||
|
radius: 10
|
||||||
|
visible: closeButtonArea.containsMouse
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeButtonArea
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: root.closed(root.notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
source: "image://icon/window-close"
|
||||||
|
implicitSize: 16
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: bodyText
|
||||||
|
text: root.notification.body
|
||||||
|
color: ShellSettings.colors["inverse_surface"]
|
||||||
|
font.pointSize: 11
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 10
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
shell/notifications/Controller.qml
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Notifications.notificationServer
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notificationLoader.item.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: notificationLoader
|
||||||
|
loading: true
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: notificationWindow
|
||||||
|
property var visibleCount: {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < toastList.count; i++) {
|
||||||
|
let item = toastList.itemAt(i);
|
||||||
|
|
||||||
|
if (item && item.visible) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleCountChanged: visible = visibleCount != 0
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: 525
|
||||||
|
visible: false
|
||||||
|
exclusionMode: ExclusionMode.Normal
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: notifLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "length: " + notificationWindow.visibleCount
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: notifLayout
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: toastList
|
||||||
|
model: ScriptModel {
|
||||||
|
values: Notifications.notificationServer.trackedNotifications.values.concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ActiveToast {
|
||||||
|
id: toast
|
||||||
|
required property var modelData
|
||||||
|
notification: modelData
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: toast
|
||||||
|
|
||||||
|
function onExpired(notification) {
|
||||||
|
toast.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClosed(notification) {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
278
shell/notifications/NotificationCenter.qml
Normal file
|
|
@ -0,0 +1,278 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool notificationsOpen: false
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
id: ipc
|
||||||
|
target: "notifications"
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
persist.notificationsOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
persist.notificationsOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
persist.notificationsOpen = !persist.notificationsOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
activeAsync: persist.notificationsOpen
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: notificationPanel
|
||||||
|
color: "red"
|
||||||
|
implicitWidth: 500
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Notifications: " + toastList.count
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: toastList
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: {
|
||||||
|
const notifications = Notifications.notificationServer.trackedNotifications.values.concat();
|
||||||
|
|
||||||
|
const groupedByApp = notifications.reduce((groups, notification) => {
|
||||||
|
const appName = notification.appName;
|
||||||
|
|
||||||
|
if (!groups[appName]) {
|
||||||
|
groups[appName] = {
|
||||||
|
appName: appName,
|
||||||
|
summaryGroups: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const summary = notification.summary;
|
||||||
|
const image = notification.image;
|
||||||
|
|
||||||
|
if (!groups[appName].summaryGroups[summary]) {
|
||||||
|
groups[appName].summaryGroups[summary] = {
|
||||||
|
summary: summary,
|
||||||
|
image: image,
|
||||||
|
notifications: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[appName].summaryGroups[summary].notifications.push(notification);
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.values(groupedByApp).map(appGroup => {
|
||||||
|
return {
|
||||||
|
appName: appGroup.appName,
|
||||||
|
summaryGroups: Object.values(appGroup.summaryGroups)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: toastWrapper
|
||||||
|
required property var modelData
|
||||||
|
width: ListView.view.width
|
||||||
|
height: toastContent.height
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: toastContent
|
||||||
|
width: parent.width
|
||||||
|
height: contentColumn.implicitHeight
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification content
|
||||||
|
Repeater {
|
||||||
|
model: toastWrapper.modelData.summaryGroups
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: summaryGroup
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: groupContent.implicitHeight + 24
|
||||||
|
color: ShellSettings.colors["surface_container"]
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
topLeftRadius: index === 0 ? 25 : 5
|
||||||
|
topRightRadius: index === 0 ? 25 : 5
|
||||||
|
bottomLeftRadius: index === (toastWrapper.modelData.summaryGroups.length - 1) ? 25 : 5
|
||||||
|
bottomRightRadius: index === (toastWrapper.modelData.summaryGroups.length - 1) ? 25 : 5
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: groupContent
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 12
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: imageContainer
|
||||||
|
Layout.preferredWidth: 36
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
visible: summaryGroup.modelData.image != ""
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: notificationImage
|
||||||
|
anchors.fill: parent
|
||||||
|
source: summaryGroup.modelData.image
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: notificationImage.width
|
||||||
|
height: notificationImage.height
|
||||||
|
radius: notificationImage.width / 2
|
||||||
|
antialiasing: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content column
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// Header row
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: summaryGroup.modelData.summary
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: ShellSettings.colors["on_surface"]
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
maximumLineCount: 2
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Separator {}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "now"
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: ShellSettings.colors["on_surface_variant"]
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification bodies
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: summaryGroup.modelData.notifications
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: bodyDelegate
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: bodyDelegate.modelData.body
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: ShellSettings.colors["on_surface_variant"]
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
maximumLineCount: 4
|
||||||
|
elide: Text.ElideRight
|
||||||
|
lineHeight: 1.3
|
||||||
|
visible: bodyDelegate.modelData.body != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HyprlanFocusGrab {
|
||||||
|
// id: grab
|
||||||
|
// windows: [notificationPanel]
|
||||||
|
// onCleared: {
|
||||||
|
// ipc.hide();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
23
shell/notifications/Notifications.qml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property alias notificationServer: notifServer
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notifServer
|
||||||
|
actionsSupported: true
|
||||||
|
persistenceSupported: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: notifServer
|
||||||
|
|
||||||
|
function onNotification(notification) {
|
||||||
|
notification.tracked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
shell/resources/battery/battery-charge.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
<path d="M20,10V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67V14h2V10Zm-8.5,3v2L4,11H9.5V9L17,13Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 448 B |
6
shell/resources/battery/battery.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z"/>
|
||||||
|
<path d="M20,15.67V14h2V10H20V8.33A1.34,1.34,0,0,0,18.67,7H3.34A1.34,1.34,0,0,0,2,8.33v7.33A1.34,1.34,0,0,0,3.33,17H18.67A1.34,1.34,0,0,0,20,15.67Z" fill="white"/>
|
||||||
|
<path d="M24,0V24H0V0Z" fill="none"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 581 B |
7
shell/resources/control/controls-button.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg height="64px" viewBox="0 0 16 16" width="64px" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="white">
|
||||||
|
<path d="m 4.550781 1 c -1.9375 0 -3.5 1.5625 -3.5 3.5 s 1.5625 3.5 3.5 3.5 h 7 c 1.941407 0 3.5 -1.5625 3.5 -3.5 s -1.558593 -3.5 -3.5 -3.5 z m 7 1 c 1.386719 0 2.5 1.113281 2.5 2.5 c 0 1.382812 -1.113281 2.5 -2.5 2.5 c -1.382812 0 -2.5 -1.117188 -2.5 -2.5 c 0 -1.386719 1.117188 -2.5 2.5 -2.5 z m 0 0"/>
|
||||||
|
<path d="m 4.550781 9 c -1.9375 0 -3.5 1.5625 -3.5 3.5 s 1.5625 3.5 3.5 3.5 h 7 c 1.941407 0 3.5 -1.5625 3.5 -3.5 s -1.558593 -3.5 -3.5 -3.5 z m 0 1 c 1.386719 0 2.5 1.113281 2.5 2.5 c 0 1.382812 -1.113281 2.5 -2.5 2.5 c -1.382812 0 -2.5 -1.117188 -2.5 -2.5 c 0 -1.386719 1.117188 -2.5 2.5 -2.5 z m 0 0" fill="white"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 805 B |
2
shell/resources/control/lock.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none"><path fill="white" fill-rule="evenodd" d="M4 5.014v-.93c0-1.078.417-2.114 1.165-2.881A3.96 3.96 0 018 0a3.96 3.96 0 012.835 1.203A4.127 4.127 0 0112 4.083v.93a2.25 2.25 0 012 2.237v5.5A2.25 2.25 0 0111.75 15h-7.5A2.25 2.25 0 012 12.75v-5.5a2.25 2.25 0 012-2.236zM6.239 2.25A2.46 2.46 0 018 1.5c.657 0 1.29.267 1.761.75.471.483.739 1.142.739 1.833V5h-5v-.917c0-.69.268-1.35.739-1.833zM8 9.25a.75.75 0 00-.75.75v1a.75.75 0 001.5 0v-1A.75.75 0 008 9.25z" clip-rule="evenodd"/></svg>
|
||||||
|
After Width: | Height: | Size: 700 B |
1
shell/resources/control/shutdown.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg class="svg-icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M746.089 221.476q39.902 28.374 70.936 64.286t53.202 77.587 33.695 88.226 11.527 94.433q0 82.463-31.478 154.729t-85.123 125.911-125.911 85.123-154.729 31.478q-81.577 0-153.842-31.478t-126.355-85.123-85.123-125.911-31.034-154.729q0-46.995 11.084-92.216t31.478-86.01 50.542-76.256 67.389-63.842q19.507-14.187 42.118-10.64t36.798 22.168 10.64 41.675-22.168 37.241q-55.863 40.789-85.567 100.197t-29.705 127.685q0 58.522 22.168 110.394t60.739 90.442 90.443 61.182 110.394 22.611 110.394-22.611 90.442-61.182 61.182-90.443 22.611-110.394q0-69.163-31.921-130.788t-89.558-101.527q-19.507-13.3-23.497-36.355t9.31-42.562q13.3-18.621 36.355-22.611t42.562 9.31zM518.207 548.668q-23.055 0-39.458-16.404t-16.404-39.458l0-336.945q0-23.055 16.404-39.902t39.458-16.847q23.941 0 40.344 16.847t16.404 39.902l0 336.945q0 23.055-16.404 39.458t-40.344 16.403z" /></svg>
|
||||||
|
After Width: | Height: | Size: 1 KiB |
1
shell/resources/control/sleep.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg class="svg-icon" style="width: 1em; height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M635.882496 934.139904C406.065152 934.139904 219.092992 744.753152 219.092992 511.978496 219.092992 279.202816 406.065152 89.833472 635.882496 89.833472 688.43008 89.833472 739.864576 99.736576 788.757504 119.264256 798.143488 123.024384 804.473856 132.043776 804.888576 142.27456 805.285888 152.505344 799.68768 162.01216 790.617088 166.513664 659.37408 231.692288 577.837056 364.069888 577.837056 511.978496 577.837056 659.877888 659.37408 792.255488 790.617088 857.459712 799.705088 861.969408 805.285888 871.477248 804.888576 881.707008 804.473856 891.938816 798.143488 900.957184 788.757504 904.710144 739.864576 924.246016 688.43008 934.139904 635.882496 934.139904Z" /></svg>
|
||||||
|
After Width: | Height: | Size: 879 B |
4
shell/resources/general/down-arrow.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M7 10L12 15L17 10" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 343 B |
1
shell/resources/general/nixos.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="matrix(1.27565325 0 0 -1.27565325 -.000008 478.03446375)"><path d="m122.453 169.761 97.758-169.34-44.926-.422-26.101 45.496-26.286-45.25-22.32.008-11.433 19.75 37.449 64.394-26.582 46.258z" fill="#ffffff"/><path d="m157.738 239.515-97.777-169.332-22.828 38.699 26.351 45.347-52.332.137-11.152 19.336 11.391 19.777 74.488-.234 26.769 46.152z" fill="#ffffff"/><path d="m165.238 104.155 195.532-.012-22.098-39.117-52.449.145 26.047-45.387-11.168-19.328-22.825-.027-37.039 64.629-53.355.109z" fill="#ffffff"/><path d="m279.043 178.35-97.758 169.34 44.926.422 26.101-45.496 26.286 45.254 22.32-.008 11.434-19.754-37.45-64.39 26.582-46.262z" fill="#ffffff"/><g fill="#ffffff"><path d="m122.453 169.761 97.758-169.34-44.926-.422-26.101 45.496-26.286-45.25-22.32.008-11.433 19.75 37.449 64.394-26.582 46.258z"/><path d="m236 244.386-195.535.011 22.101 39.118 52.45-.149-26.047 45.391 11.168 19.328 22.82.023 37.043-64.625 53.352-.109z"/><path d="m243.625 108.636 97.777 169.328 22.825-38.696-26.348-45.351 52.332-.137 11.152-19.336-11.39-19.777-74.489.238-26.769-46.152z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
12
shell/resources/general/notification.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 0 32 32" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<path d="M26.8,25H5.2c-0.8,0-1.5-0.4-1.9-1.1c-0.4-0.7-0.3-1.5,0.1-2.2L4.5,20c1.8-2.7,2.7-5.8,2.7-9c0-3.7,2.4-7.1,5.9-8.3
|
||||||
|
C13.7,1.6,14.8,1,16,1s2.3,0.6,2.9,1.7c3.5,1.2,5.9,4.6,5.9,8.3c0,3.2,0.9,6.3,2.7,9l1.1,1.7c0.4,0.7,0.5,1.5,0.1,2.2
|
||||||
|
C28.4,24.6,27.6,25,26.8,25z"/>
|
||||||
|
</g>
|
||||||
|
<path d="M11.1,27c0.5,2.3,2.5,4,4.9,4s4.4-1.7,4.9-4H11.1z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 675 B |
BIN
shell/resources/general/pfp.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
4
shell/resources/general/placeholder.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="white" width="800px" height="800px" viewBox="0 0 256 256" id="Flat" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M228,48a20.02229,20.02229,0,0,0-20-20H48a19.89568,19.89568,0,0,0-13.18359,4.999,12.24037,12.24037,0,0,0-.959.8584,12.05585,12.05585,0,0,0-.8584.959A19.89568,19.89568,0,0,0,28,48V208a20.02229,20.02229,0,0,0,20,20H208a19.89568,19.89568,0,0,0,13.18359-4.999,11.64461,11.64461,0,0,0,1.81739-1.81739A19.89568,19.89568,0,0,0,228,208ZM204,187.0293,68.9707,52H204ZM52,68.9707,187.0293,204H52Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 640 B |
4
shell/resources/general/right-arrow.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10 7L15 12L10 17" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 343 B |
BIN
shell/resources/mask.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
19
shell/resources/mpris/next.svg
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 -2 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
|
||||||
|
<title>next [#998]</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
|
||||||
|
</defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Dribbble-Light-Preview" transform="translate(-144.000000, -3805.000000)" fill="white">
|
||||||
|
<g id="icons" transform="translate(56.000000, 160.000000)">
|
||||||
|
<path d="M99.684,3649.69353 L95.207,3652.82453 C94.571,3653.25353 94,3652.84553 94,3652.13153 L94,3650.14053 L89.78,3652.82453 C89.145,3653.25353 88,3652.84553 88,3652.13153 L88,3645.86853 C88,3645.15453 89.145,3644.74653 89.78,3645.17453 L94,3647.85953 L94,3645.86853 C94,3645.15453 94.571,3644.74653 95.207,3645.17453 L99.516,3648.30653 C100.03,3648.65353 100.198,3649.34653 99.684,3649.69353" id="next-[#998]">
|
||||||
|
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
5
shell/resources/mpris/pause.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 5L20 19C20 20.6569 18.6569 22 17 22L16 22C14.3431 22 13 20.6569 13 19L13 5C13 3.34314 14.3431 2 16 2L17 2C18.6569 2 20 3.34315 20 5Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2C9.65685 2 11 3.34315 11 5L11 19C11 20.6569 9.65685 22 8 22L7 22C5.34315 22 4 20.6569 4 19L4 5C4 3.34314 5.34315 2 7 2L8 2Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 628 B |
7
shell/resources/mpris/play.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<g id="SVGRepo_iconCarrier"> <path d="M21.4086 9.35258C23.5305 10.5065 23.5305 13.4935 21.4086 14.6474L8.59662 21.6145C6.53435 22.736 4 21.2763 4 18.9671L4 5.0329C4 2.72368 6.53435 1.26402 8.59661 2.38548L21.4086 9.35258Z" fill="#ffffff"/> </g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 666 B |
19
shell/resources/mpris/previous.svg
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 -2 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
|
||||||
|
<title>previous [#999]</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs>
|
||||||
|
|
||||||
|
</defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Dribbble-Light-Preview" transform="translate(-104.000000, -3805.000000)" fill="white">
|
||||||
|
<g id="icons" transform="translate(56.000000, 160.000000)">
|
||||||
|
<path d="M59.9990013,3645.86816 L59.9990013,3652.13116 C59.9990013,3652.84516 58.8540013,3653.25316 58.2180013,3652.82516 L53.9990013,3650.14016 L53.9990013,3652.13116 C53.9990013,3652.84516 53.4260013,3653.25316 52.7900013,3652.82516 L48.4790013,3649.69316 C47.9650013,3649.34616 47.7980013,3648.65316 48.3120013,3648.30616 L52.7900013,3645.17516 C53.4260013,3644.74616 53.9990013,3645.15416 53.9990013,3645.86816 L53.9990013,3647.85916 L58.2180013,3645.17516 C58.8540013,3644.74616 59.9990013,3645.15416 59.9990013,3645.86816" id="previous-[#999]">
|
||||||
|
|
||||||
|
</path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
6
shell/resources/mpris/shuffle.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="white" width="800px" height="800px" viewBox="-4 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>shuffle</title>
|
||||||
|
<path d="M0 20.688v2c0 0.281 0.219 0.5 0.5 0.5h2.875c1.688 0 3.094-0.781 4.25-1.969 1.188-1.188 2.156-2.781 3.125-4.313 0.781-1.25 1.563-2.438 2.375-3.344 0.781-0.938 1.563-1.5 2.5-1.5h2.656v2.281c0 0.719 0.5 0.844 1.094 0.375l4.344-3.625c0.375-0.313 0.375-0.906 0-1.219l-4.344-3.594c-0.594-0.5-1.094-0.375-1.094 0.375v2.406h-2.656c-1.719 0-3.063 0.75-4.25 1.969-1.156 1.188-2.219 2.781-3.156 4.281-0.813 1.281-1.563 2.5-2.375 3.406-0.781 0.906-1.563 1.469-2.469 1.469h-2.875c-0.281 0-0.5 0.219-0.5 0.5zM0 9.531v2c0 0.281 0.219 0.5 0.5 0.5h2.875c1.406 0 2.531 1.375 3.75 3.156 0.031-0.094 0.063-0.156 0.094-0.219 0.031-0.031 0.125-0.094 0.156-0.156 0.469-0.781 1-1.531 1.5-2.344-0.75-0.969-1.469-1.844-2.406-2.438-0.906-0.625-1.906-0.969-3.094-0.969h-2.875c-0.344 0-0.5 0.156-0.5 0.469zM18.281 20.125h-2.656c-1.375 0-2.563-1.344-3.75-3.094-0.063 0.094-0.094 0.156-0.125 0.219-0.063 0.063-0.094 0.125-0.156 0.219-0.219 0.375-0.5 0.781-0.719 1.156-0.25 0.344-0.5 0.75-0.719 1.094 0.719 0.969 1.469 1.813 2.375 2.406 0.875 0.625 1.906 1.031 3.094 1.031h2.656v2.188c0 0.719 0.5 0.875 1.094 0.375l4.344-3.656c0.375-0.313 0.375-0.875 0-1.188l-4.344-3.594c-0.594-0.469-1.094-0.375-1.094 0.375v2.469z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
2
shell/resources/mpris/stop.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 18a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12z" fill="#000000"/></svg>
|
||||||
|
After Width: | Height: | Size: 366 B |
2
shell/resources/volume/microphone-full.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="white" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 16c2.206 0 4-1.794 4-4V6c0-2.217-1.785-4.021-3.979-4.021a.933.933 0 0 0-.209.025A4.006 4.006 0 0 0 8 6v6c0 2.206 1.794 4 4 4z"/><path d="M11 19.931V22h2v-2.069c3.939-.495 7-3.858 7-7.931h-2c0 3.309-2.691 6-6 6s-6-2.691-6-6H4c0 4.072 3.061 7.436 7 7.931z"/></svg>
|
||||||
|
After Width: | Height: | Size: 497 B |
2
shell/resources/volume/microphone-mute.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="white" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m21.707 20.293-3.4-3.4A7.93 7.93 0 0 0 20 12h-2a5.945 5.945 0 0 1-1.119 3.467l-1.449-1.45A3.926 3.926 0 0 0 16 12V6c0-2.217-1.785-4.021-3.979-4.021-.07 0-.14.009-.209.025A4.006 4.006 0 0 0 8 6v.586L3.707 2.293 2.293 3.707l18 18 1.414-1.414zM6 12H4c0 4.072 3.06 7.436 7 7.931V22h2v-2.069a7.935 7.935 0 0 0 2.241-.63l-1.549-1.548A5.983 5.983 0 0 1 12 18c-3.309 0-6-2.691-6-6z"/><path d="M8.007 12.067a3.996 3.996 0 0 0 3.926 3.926l-3.926-3.926z"/></svg>
|
||||||
|
After Width: | Height: | Size: 682 B |
15
shell/resources/volume/volume-full.svg
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>volume-up-solid</title>
|
||||||
|
<g id="Layer_2" data-name="Layer 2">
|
||||||
|
<g id="invisible_box" data-name="invisible box">
|
||||||
|
<rect width="48" height="48" fill="none"/>
|
||||||
|
</g>
|
||||||
|
<g id="icons_Q2" data-name="icons Q2">
|
||||||
|
<path d="M29,4a.9.9,0,0,0-.7.3L16.7,15H8a2,2,0,0,0-2,2V31a2,2,0,0,0,2,2h8.7L28.3,43.7a.9.9,0,0,0,.7.3,1,1,0,0,0,1-1V5a1,1,0,0,0-1-1Z" fill="white"/>
|
||||||
|
<path d="M36,42a2.1,2.1,0,0,1-1.6-.8,2,2,0,0,1,.4-2.8,18,18,0,0,0,0-28.8,2,2,0,1,1,2.4-3.2A22.4,22.4,0,0,1,46,24a22.4,22.4,0,0,1-8.8,17.6A1.7,1.7,0,0,1,36,42Z" fill="white"/>
|
||||||
|
<path d="M34,15.5v17a.5.5,0,0,0,.9.3,14,14,0,0,0,0-17.6A.5.5,0,0,0,34,15.5Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 906 B |
16
shell/resources/volume/volume-mute.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<title>volume-off-solid</title>
|
||||||
|
<g id="Layer_2" data-name="Layer 2">
|
||||||
|
<g id="invisible_box" data-name="invisible box">
|
||||||
|
<rect width="48" height="48" fill="none"/>
|
||||||
|
</g>
|
||||||
|
<g id="icons_Q2" data-name="icons Q2">
|
||||||
|
<g>
|
||||||
|
<path d="M30,22.2V5a1,1,0,0,0-1-1,1.1,1.1,0,0,0-.7.3l-8.4,7.8Z" fill="white"/>
|
||||||
|
<path d="M40.4,38.6l-32-32A2,2,0,0,0,5.6,9.4L11.2,15H8a2,2,0,0,0-2,2V31a2,2,0,0,0,2,2h8.7L28.3,43.7a1.1,1.1,0,0,0,.7.3,1,1,0,0,0,1-1V33.8l7.6,7.6a1.9,1.9,0,0,0,2.8,0A1.9,1.9,0,0,0,40.4,38.6Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 797 B |
BIN
shell/resources/wallpapers/pixelart0.jpg
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
shell/resources/wallpapers/wallhaven-0w3ej7.jpg
Normal file
|
After Width: | Height: | Size: 480 KiB |
BIN
shell/resources/wallpapers/wallhaven-2yp6gg.png
Normal file
|
After Width: | Height: | Size: 11 MiB |
BIN
shell/resources/wallpapers/wallhaven-5g22q5.png
Normal file
|
After Width: | Height: | Size: 3.9 MiB |
BIN
shell/resources/wallpapers/wallhaven-5w9em7.jpg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
shell/resources/wallpapers/wallhaven-96y9qk.jpg
Normal file
|
After Width: | Height: | Size: 10 MiB |
BIN
shell/resources/wallpapers/wallhaven-od2lwm.jpg
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
shell/resources/wallpapers/wallhaven-zywwky.jpg
Normal file
|
After Width: | Height: | Size: 12 MiB |
72
shell/screencapture/Controller.qml
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool windowOpen: false
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "screencapture"
|
||||||
|
|
||||||
|
function screenshot(): void {
|
||||||
|
root.windowOpen = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
active: root.windowOpen
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: focusedScreen
|
||||||
|
color: "transparent"
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
WlrLayershell.namespace: "shell:screencapture"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
Keys.onEscapePressed: root.windowOpen = false
|
||||||
|
|
||||||
|
SelectionRectangle {
|
||||||
|
id: selection
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onAreaSelected: selection => {
|
||||||
|
let screen = focusedScreen.screen;
|
||||||
|
const x = Math.floor(selection.x) + screen.x;
|
||||||
|
const y = Math.floor(selection.y) + screen.y;
|
||||||
|
const width = Math.floor(selection.width);
|
||||||
|
const height = Math.floor(selection.height);
|
||||||
|
|
||||||
|
let position = `${x},${y} ${width}x${height}`;
|
||||||
|
let path = "/home/koss/Pictures/screenshot.png";
|
||||||
|
|
||||||
|
Quickshell.execDetached({
|
||||||
|
command: ["grim", "-g", position, path]
|
||||||
|
});
|
||||||
|
|
||||||
|
root.windowOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
3
shell/screencapture/Screenshot.qml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Image {}
|
||||||
152
shell/screencapture/SelectionCutout.qml
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: root
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property color overlayColor: "#80000000"
|
||||||
|
property color borderColor: ShellSettings.colors["primary"]
|
||||||
|
property real borderWidth: 3
|
||||||
|
property real handleSize: 16
|
||||||
|
property var screen
|
||||||
|
|
||||||
|
property real centerX: width / 2
|
||||||
|
property real centerY: height / 2
|
||||||
|
property real minWidth: 400
|
||||||
|
property real minHeight: 300
|
||||||
|
|
||||||
|
// rect that holds positional data for the selection
|
||||||
|
property rect selectionRect: Qt.rect(centerX - minWidth / 2, centerY - minHeight / 2, minWidth, minHeight)
|
||||||
|
|
||||||
|
// handle positions
|
||||||
|
property point topLeftHandle: Qt.point(selectionRect.x, selectionRect.y)
|
||||||
|
property point topRightHandle: Qt.point(selectionRect.x + selectionRect.width, selectionRect.y)
|
||||||
|
property point bottomLeftHandle: Qt.point(selectionRect.x, selectionRect.y + selectionRect.height)
|
||||||
|
property point bottomRightHandle: Qt.point(selectionRect.x + selectionRect.width, selectionRect.y + selectionRect.height)
|
||||||
|
|
||||||
|
// dragging state
|
||||||
|
property int activeHandle: -1
|
||||||
|
property point dragStart: Qt.point(0, 0)
|
||||||
|
property rect initialRect: Qt.rect(0, 0, 0, 0)
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// grey overlay
|
||||||
|
ctx.fillStyle = overlayColor;
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// cut out the selection rectangle
|
||||||
|
ctx.globalCompositeOperation = "destination-out";
|
||||||
|
ctx.fillRect(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height);
|
||||||
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
|
||||||
|
// draw border
|
||||||
|
ctx.strokeStyle = borderColor;
|
||||||
|
ctx.lineWidth = borderWidth;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(topLeftHandle.x, topLeftHandle.y);
|
||||||
|
ctx.lineTo(topRightHandle.x, topRightHandle.y);
|
||||||
|
ctx.lineTo(bottomRightHandle.x, bottomRightHandle.y);
|
||||||
|
ctx.lineTo(bottomLeftHandle.x, bottomLeftHandle.y);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// draw handles
|
||||||
|
ctx.fillStyle = borderColor;
|
||||||
|
drawHandle(ctx, topLeftHandle);
|
||||||
|
drawHandle(ctx, topRightHandle);
|
||||||
|
drawHandle(ctx, bottomLeftHandle);
|
||||||
|
drawHandle(ctx, bottomRightHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawHandle(ctx, center) {
|
||||||
|
var radius = handleSize / 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHandleAt(x, y) {
|
||||||
|
var halfSize = handleSize / 2;
|
||||||
|
var handles = [topLeftHandle, topRightHandle, bottomLeftHandle, bottomRightHandle];
|
||||||
|
|
||||||
|
for (var i = 0; i < handles.length; i++) {
|
||||||
|
var handle = handles[i];
|
||||||
|
if (x >= handle.x - halfSize && x <= handle.x + halfSize && y >= handle.y - halfSize && y <= handle.y + halfSize) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function constrainRect(rect) {
|
||||||
|
// Ensure minimum size
|
||||||
|
var width = Math.max(rect.width, minWidth);
|
||||||
|
var height = Math.max(rect.height, minHeight);
|
||||||
|
|
||||||
|
// Ensure within canvas bounds
|
||||||
|
var x = Math.max(0, Math.min(rect.x, root.width - width));
|
||||||
|
var y = Math.max(0, Math.min(rect.y, root.height - height));
|
||||||
|
|
||||||
|
return Qt.rect(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onPressed: function (mouse) {
|
||||||
|
activeHandle = root.getHandleAt(mouse.x, mouse.y);
|
||||||
|
if (root.activeHandle >= 0) {
|
||||||
|
dragStart = Qt.point(mouse.x, mouse.y);
|
||||||
|
initialRect = root.selectionRect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// kinda stupid, should maybe bind a mouse area to each handle I don't know
|
||||||
|
onPositionChanged: function (mouse) {
|
||||||
|
if (root.activeHandle < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dx = mouse.x - root.dragStart.x;
|
||||||
|
var dy = mouse.y - root.dragStart.y;
|
||||||
|
var newRect;
|
||||||
|
|
||||||
|
switch (root.activeHandle) {
|
||||||
|
// top left
|
||||||
|
case 0:
|
||||||
|
var newX = Math.max(0, Math.min(root.initialRect.x + dx, root.initialRect.x + root.initialRect.width - root.minWidth));
|
||||||
|
var newY = Math.max(0, Math.min(root.initialRect.y + dy, root.initialRect.y + root.initialRect.height - minHeight));
|
||||||
|
newRect = Qt.rect(newX, newY, root.initialRect.width - (newX - root.initialRect.x), root.initialRect.height - (newY - root.initialRect.y));
|
||||||
|
break;
|
||||||
|
// top right
|
||||||
|
case 1:
|
||||||
|
var newY = Math.max(0, Math.min(root.initialRect.y + dy, root.initialRect.y + root.initialRect.height - root.minHeight));
|
||||||
|
var newWidth = Math.max(root.minWidth, Math.min(root.initialRect.width + dx, root.width - root.initialRect.x));
|
||||||
|
newRect = Qt.rect(root.initialRect.x, newY, newWidth, root.initialRect.height - (newY - root.initialRect.y));
|
||||||
|
break;
|
||||||
|
// bottom left
|
||||||
|
case 2:
|
||||||
|
var newX = Math.max(0, Math.min(root.initialRect.x + dx, root.initialRect.x + root.initialRect.width - minWidth));
|
||||||
|
var newHeight = Math.max(root.minHeight, Math.min(root.initialRect.height + dy, root.height - root.initialRect.y));
|
||||||
|
newRect = Qt.rect(newX, root.initialRect.y, root.initialRect.width - (newX - root.initialRect.x), newHeight);
|
||||||
|
break;
|
||||||
|
// bottom right
|
||||||
|
case 3:
|
||||||
|
var newWidth = Math.max(root.minWidth, Math.min(root.initialRect.width + dx, root.width - root.initialRect.x));
|
||||||
|
var newHeight = Math.max(root.minHeight, Math.min(root.initialRect.height + dy, root.height - root.initialRect.y));
|
||||||
|
newRect = Qt.rect(root.initialRect.x, root.initialRect.y, newWidth, newHeight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectionRect = root.constrainRect(newRect);
|
||||||
|
root.requestPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: {
|
||||||
|
root.activeHandle = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
shell/screencapture/SelectionRectangle.qml
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property color overlayColor: "#80000000"
|
||||||
|
property color outlineColor: ShellSettings.colors["primary"]
|
||||||
|
property rect selectionRect
|
||||||
|
property point startPosition
|
||||||
|
signal areaSelected(rect selection)
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// grey overlay
|
||||||
|
ctx.fillStyle = overlayColor;
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// cut out the selection rectangle
|
||||||
|
ctx.globalCompositeOperation = "destination-out";
|
||||||
|
ctx.fillRect(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height);
|
||||||
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
ctx.strokeStyle = outlineColor;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeRect(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onPressed: mouse => {
|
||||||
|
root.startPosition = Qt.point(mouse.x, mouse.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
onPositionChanged: mouse => {
|
||||||
|
if (pressed) {
|
||||||
|
var x = Math.min(root.startPosition.x, mouse.x);
|
||||||
|
var y = Math.min(root.startPosition.y, mouse.y);
|
||||||
|
var width = Math.abs(mouse.x - root.startPosition.x);
|
||||||
|
var height = Math.abs(mouse.y - root.startPosition.y);
|
||||||
|
|
||||||
|
root.selectionRect = Qt.rect(x, y, width, height);
|
||||||
|
root.requestPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: mouse => {
|
||||||
|
root.visible = false;
|
||||||
|
root.areaSelected(root.selectionRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
shell/settings/Controller.qml
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "../widgets/" as Widgets
|
||||||
|
import "../"
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
PersistentProperties {
|
||||||
|
id: persist
|
||||||
|
property bool windowOpen: false
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "settings"
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
persist.windowOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
persist.windowOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
persist.windowOpen = !persist.windowOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: loader
|
||||||
|
activeAsync: persist.windowOpen
|
||||||
|
|
||||||
|
FloatingWindow {
|
||||||
|
color: ShellSettings.colors["surface"]
|
||||||
|
implicitWidth: 840
|
||||||
|
implicitHeight: 845
|
||||||
|
|
||||||
|
// onWidthChanged: {
|
||||||
|
// console.log("height: " + height);
|
||||||
|
// console.log("width: " + width);
|
||||||
|
// }
|
||||||
|
|
||||||
|
maximumSize {
|
||||||
|
width: 840
|
||||||
|
height: 845
|
||||||
|
}
|
||||||
|
|
||||||
|
minimumSize {
|
||||||
|
width: 840
|
||||||
|
height: 845
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (!visible) {
|
||||||
|
persist.windowOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 20
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
id: page
|
||||||
|
currentIndex: topBar.currentIndex
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: currentItem ? currentItem.implicitHeight : 0
|
||||||
|
|
||||||
|
readonly property Item currentItem: children[currentIndex]
|
||||||
|
|
||||||
|
WallpaperPicker {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.TopBar {
|
||||||
|
id: topBar
|
||||||
|
model: ["headphones", "tune"]
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 35
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
135
shell/settings/WallpaperPicker.qml
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Qt.labs.folderlistmodel
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: container
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// anchors {
|
||||||
|
// fill: parent
|
||||||
|
// margins: 10
|
||||||
|
// }
|
||||||
|
|
||||||
|
ClippingRectangle {
|
||||||
|
radius: 20
|
||||||
|
Layout.preferredWidth: 464
|
||||||
|
Layout.preferredHeight: 261
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.margins: 20
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: wallpaperImage
|
||||||
|
source: ShellSettings.settings.wallpaperUrl
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.colors["surface_container"]
|
||||||
|
radius: 20
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.colors["surface_container_high"]
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
id: wallpaperGrid
|
||||||
|
cellWidth: 200
|
||||||
|
cellHeight: 200
|
||||||
|
clip: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.margins: 10
|
||||||
|
|
||||||
|
model: FolderListModel {
|
||||||
|
id: folderModel
|
||||||
|
folder: Qt.resolvedUrl("root:resources/wallpapers")
|
||||||
|
nameFilters: ["*.jpg", "*.png"]
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: cell
|
||||||
|
required property var modelData
|
||||||
|
width: 200
|
||||||
|
height: 200
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: border
|
||||||
|
visible: mouseArea.containsMouse
|
||||||
|
color: "transparent"
|
||||||
|
radius: 20
|
||||||
|
|
||||||
|
border {
|
||||||
|
color: ShellSettings.colors["primary"]
|
||||||
|
width: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: image
|
||||||
|
source: cell.modelData.fileUrl
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
sourceSize {
|
||||||
|
height: image.height
|
||||||
|
width: image.width
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: cell.width
|
||||||
|
height: cell.height
|
||||||
|
radius: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
ShellSettings.settings.wallpaperUrl = cell.modelData.fileUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
shell/shaders/mask.frag
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D source;
|
||||||
|
layout(binding = 2) uniform sampler2D mask;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 sourceColor = texture(source, qt_TexCoord0);
|
||||||
|
vec4 maskColor = texture(mask, qt_TexCoord0);
|
||||||
|
|
||||||
|
// Use the mask's luminance to determine opacity
|
||||||
|
float maskValue = dot(maskColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||||
|
|
||||||
|
// Black areas of mask = transparent, white areas = opaque
|
||||||
|
sourceColor.a *= (1.0 - maskValue) * qt_Opacity;
|
||||||
|
|
||||||
|
fragColor = sourceColor;
|
||||||
|
}
|
||||||
BIN
shell/shaders/mask.frag.qsb
Normal file
18
shell/shaders/vertexgradient.frag
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 coord;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
vec4 topLeftColor;
|
||||||
|
vec4 topRightColor;
|
||||||
|
vec4 bottomLeftColor;
|
||||||
|
vec4 bottomRightColor;
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 topColor = mix(ubuf.topLeftColor, ubuf.topRightColor, coord.x);
|
||||||
|
vec4 bottomColor = mix(ubuf.bottomLeftColor, ubuf.bottomRightColor, coord.x);
|
||||||
|
fragColor = mix(topColor, bottomColor, coord.y);
|
||||||
|
}
|
||||||
BIN
shell/shaders/vertexgradient.frag.qsb
Normal file
21
shell/shaders/vertexgradient.vert
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 qt_Vertex;
|
||||||
|
layout(location = 1) in vec2 qt_MultiTexCoord0;
|
||||||
|
|
||||||
|
layout(location = 0) out vec2 coord;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
vec4 topLeftColor;
|
||||||
|
vec4 topRightColor;
|
||||||
|
vec4 bottomLeftColor;
|
||||||
|
vec4 bottomRightColor;
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
out gl_PerVertex { vec4 gl_Position; };
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
coord = qt_MultiTexCoord0;
|
||||||
|
gl_Position = ubuf.qt_Matrix * qt_Vertex;
|
||||||
|
}
|
||||||
BIN
shell/shaders/vertexgradient.vert.qsb
Normal file
33
shell/shaders/wallpapertransition.frag
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 qt_TexCoord0;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 qt_Matrix;
|
||||||
|
float qt_Opacity;
|
||||||
|
float progress;
|
||||||
|
vec2 aspectRatio;
|
||||||
|
vec2 origin;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D fromImage;
|
||||||
|
layout(binding = 2) uniform sampler2D toImage;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = qt_TexCoord0;
|
||||||
|
|
||||||
|
vec2 scaledUV = (uv - origin) * aspectRatio;
|
||||||
|
float distance = length(scaledUV);
|
||||||
|
|
||||||
|
vec2 maxVec = max(origin, vec2(1.0) - origin) * aspectRatio;
|
||||||
|
float maxDistance = length(maxVec);
|
||||||
|
|
||||||
|
float threshold = progress * maxDistance;
|
||||||
|
|
||||||
|
if (distance < threshold) {
|
||||||
|
fragColor = texture(toImage, uv) * qt_Opacity;
|
||||||
|
} else {
|
||||||
|
fragColor = texture(fromImage, uv) * qt_Opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
shell/shell.qml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
//@ pragma UseQApplication
|
||||||
|
//@ pragma IconTheme Papirus-Dark
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import "bar"
|
||||||
|
import "notifications" as Notifications
|
||||||
|
import "mpris" as Mpris
|
||||||
|
import "volume-osd" as VolumeOSD
|
||||||
|
import "settings" as Settings
|
||||||
|
import "launcher" as Launcher
|
||||||
|
import "lockscreen" as LockScreen
|
||||||
|
import "wallpaper" as Wallpaper
|
||||||
|
import "screencapture" as ScreenCapture
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
Bar {}
|
||||||
|
Wallpaper.Controller {}
|
||||||
|
Notifications.Controller {}
|
||||||
|
VolumeOSD.Controller {}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Launcher.Controller.init();
|
||||||
|
Settings.Controller.init();
|
||||||
|
ScreenCapture.Controller.init();
|
||||||
|
Mpris.Controller.init();
|
||||||
|
Notifications.NotificationCenter.init();
|
||||||
|
LockScreen.Controller.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
102
shell/volume-osd/Controller.qml
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Bind the pipewire node so its volume will be tracked
|
||||||
|
PwObjectTracker {
|
||||||
|
objects: [Pipewire.defaultAudioSink]
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Pipewire.defaultAudioSink?.audio
|
||||||
|
|
||||||
|
function onVolumeChanged() {
|
||||||
|
root.shouldShowOsd = true;
|
||||||
|
hideTimer.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool shouldShowOsd: false
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: hideTimer
|
||||||
|
interval: 1000
|
||||||
|
onTriggered: root.shouldShowOsd = false
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
active: root.shouldShowOsd
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
implicitWidth: 50
|
||||||
|
implicitHeight: 275
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
// An empty click mask prevents the window from blocking mouse events.
|
||||||
|
mask: Region {}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 8
|
||||||
|
color: {
|
||||||
|
let color = ShellSettings.colors["surface"];
|
||||||
|
return Qt.rgba(color.r, color.g, color.b, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: 10
|
||||||
|
rightMargin: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
implicitSize: 30
|
||||||
|
source: "root:resources/volume/volume-full.svg"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: sliderBackground
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 10
|
||||||
|
radius: 20
|
||||||
|
color: {
|
||||||
|
let color = ShellSettings.colors["inverse_surface"];
|
||||||
|
return Qt.rgba(color.r, color.g, color.b, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: sliderBackground.width
|
||||||
|
height: sliderBackground.height
|
||||||
|
radius: sliderBackground.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.colors["primary"]
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
shell/wallpaper/Controller.qml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
loading: true
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
required property var modelData
|
||||||
|
color: "black"
|
||||||
|
aboveWindows: false
|
||||||
|
screen: modelData
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: ShellSettings.settings.wallpaperUrl
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ShellSettings.settings
|
||||||
|
|
||||||
|
function onWallpaperUrlChanged() {
|
||||||
|
console.log("Switching wallpaper: " + ShellSettings.settings.wallpaperUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
shell/wallpaper/matugen.toml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
[config.custom_colors]
|
||||||
|
|
||||||
|
[templates.kde]
|
||||||
|
input_path = "templates/BreezeDark.colors"
|
||||||
|
mode = "Dark"
|
||||||
|
output_path = "~/.config/kdeglobals"
|
||||||
|
# post_hook = "systemctl restart --user plasma-xdg-desktop-portal-kde.service"
|
||||||
|
|
||||||
|
[templates.nvim]
|
||||||
|
input_path = "templates/nvim.json"
|
||||||
|
mode = "Dark"
|
||||||
|
output_path = "~/.local/share/nvim-colors.json"
|
||||||
|
|
||||||
|
[templates.qt5ct]
|
||||||
|
input_path = "templates/qtct-colors.conf"
|
||||||
|
mode = "Dark"
|
||||||
|
output_path = "~/.config/qt5ct/colors/matugen.conf"
|
||||||
|
|
||||||
|
[templates.qt6ct]
|
||||||
|
input_path = "templates/BreezeDark.colors"
|
||||||
|
mode = "Dark"
|
||||||
|
output_path = "~/.config/qt6ct/colors/BreezeDark.colors"
|
||||||
|
|
||||||
|
[templates.hyprland]
|
||||||
|
input_path = 'templates/hyprland-colors.conf'
|
||||||
|
output_path = '~/.config/hypr/colors.conf'
|
||||||
|
post_hook = 'hyprctl reload'
|
||||||
|
|
||||||
|
[templates.foot]
|
||||||
|
input_path = 'templates/foot.ini'
|
||||||
|
output_path = '~/.config/foot/foot.ini'
|
||||||
211
shell/wallpaper/templates/BreezeDark.colors
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
# SPDX-FileCopyrightText: Andrew Lake <jamboarder@gmail.com>
|
||||||
|
# SPDX-FileCopyrightText: Marco Martin <notmart@gmail.com>
|
||||||
|
# SPDX-FileCopyrightText: Nate Graham <nate@kde.org>
|
||||||
|
# SPDX-FileCopyrightText: Noah Davis <noahadvs@gmail.com>
|
||||||
|
# SPDX-FileCopyrightText: Neal Gompa <ngompa@kde.org>
|
||||||
|
# SPDX-FileCopyrightText: David Redondo <kde@david-redondo.de>
|
||||||
|
# SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
[ColorEffects:Disabled]
|
||||||
|
Color={{colors.outline.default.red}},{{colors.outline.default.green}},{{colors.outline.default.blue}}
|
||||||
|
ColorAmount=0
|
||||||
|
ColorEffect=0
|
||||||
|
ContrastAmount=0.65
|
||||||
|
ContrastEffect=1
|
||||||
|
IntensityAmount=0.1
|
||||||
|
IntensityEffect=2
|
||||||
|
|
||||||
|
[ColorEffects:Inactive]
|
||||||
|
ChangeSelectionColor=true
|
||||||
|
Color={{colors.outline_variant.default.red}},{{colors.outline_variant.default.green}},{{colors.outline_variant.default.blue}}
|
||||||
|
ColorAmount=0.025
|
||||||
|
ColorEffect=2
|
||||||
|
ContrastAmount=0.1
|
||||||
|
ContrastEffect=2
|
||||||
|
Enable=false
|
||||||
|
IntensityAmount=0
|
||||||
|
IntensityEffect=0
|
||||||
|
|
||||||
|
[Colors:Button]
|
||||||
|
BackgroundAlternate={{colors.surface_container_high.default.red}},{{colors.surface_container_high.default.green}},{{colors.surface_container_high.default.blue}}
|
||||||
|
BackgroundNormal={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Colors:Complementary]
|
||||||
|
BackgroundAlternate={{colors.surface_container_high.default.red}},{{colors.surface_container_high.default.green}},{{colors.surface_container_high.default.blue}}
|
||||||
|
BackgroundNormal={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Colors:Header]
|
||||||
|
BackgroundAlternate={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||||
|
BackgroundNormal={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Colors:Header][Inactive]
|
||||||
|
BackgroundAlternate={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||||
|
BackgroundNormal={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Colors:Selection]
|
||||||
|
BackgroundAlternate={{colors.surface_container_high.default.red}},{{colors.surface_container_high.default.green}},{{colors.surface_container_high.default.blue}}
|
||||||
|
BackgroundNormal={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.background.default.red}},{{colors.background.default.green}},{{colors.background.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Colors:Tooltip]
|
||||||
|
BackgroundAlternate={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||||
|
BackgroundNormal={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Colors:View]
|
||||||
|
BackgroundAlternate={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||||
|
BackgroundNormal={{colors.surface.default.red}},{{colors.surface.default.green}},{{colors.surface.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Colors:Window]
|
||||||
|
BackgroundAlternate={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||||
|
BackgroundNormal={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||||
|
DecorationFocus={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
DecorationHover={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundActive={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundInactive={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
ForegroundLink={{colors.primary.default.red}},{{colors.primary.default.green}},{{colors.primary.default.blue}}
|
||||||
|
ForegroundNegative={{colors.error.default.red}},{{colors.error.default.green}},{{colors.error.default.blue}}
|
||||||
|
ForegroundNeutral={{colors.secondary.default.red}},{{colors.secondary.default.green}},{{colors.secondary.default.blue}}
|
||||||
|
ForegroundNormal={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
ForegroundPositive={{colors.tertiary.default.red}},{{colors.tertiary.default.green}},{{colors.tertiary.default.blue}}
|
||||||
|
ForegroundVisited={{colors.tertiary_container.default.red}},{{colors.tertiary_container.default.green}},{{colors.tertiary_container.default.blue}}
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Theme=breeze-dark
|
||||||
|
|
||||||
|
[General]
|
||||||
|
ColorScheme=BreezeDark
|
||||||
|
Name=Breeze Dark
|
||||||
|
Name[ar]=نسيم داكن
|
||||||
|
Name[az]=Breeze - Tünd
|
||||||
|
Name[bg]=Breeze Тъмен
|
||||||
|
Name[bs]=Breeze tamna
|
||||||
|
Name[ca]=Brisa fosca
|
||||||
|
Name[ca@valencia]=Brisa fosca
|
||||||
|
Name[cs]=Breeze Tmavé
|
||||||
|
Name[da]=Breeze Dark
|
||||||
|
Name[de]=Breeze Dunkel
|
||||||
|
Name[el]=Breeze σκούρο
|
||||||
|
Name[en_GB]=Breeze Dark
|
||||||
|
Name[eo]=Breeze Dark
|
||||||
|
Name[es]=Brisa oscuro
|
||||||
|
Name[et]=Breeze tume
|
||||||
|
Name[eu]=Breeze iluna
|
||||||
|
Name[fi]=Tumma Breeze
|
||||||
|
Name[fr]=Brise sombre
|
||||||
|
Name[gl]=Brisa escura
|
||||||
|
Name[he]=בריזה כהה
|
||||||
|
Name[hi]=ब्रीज़ गहरा
|
||||||
|
Name[hu]=Breeze Dark
|
||||||
|
Name[ia]=Brisa obscure
|
||||||
|
Name[id]=Breeze Gelap
|
||||||
|
Name[is]=Breeze dökkt
|
||||||
|
Name[it]=Brezza scuro
|
||||||
|
Name[ja]=Breeze ダーク
|
||||||
|
Name[ka]=Breeze მუქი
|
||||||
|
Name[ko]=어두운 Breeze
|
||||||
|
Name[lt]=Breeze tamsus
|
||||||
|
Name[lv]=Breeze Dark
|
||||||
|
Name[nb]=Breeze mørk
|
||||||
|
Name[nl]=Breeze Dark
|
||||||
|
Name[nn]=Breeze mørk
|
||||||
|
Name[pa]=ਬਰੀਜ਼ ਗੂੜ੍ਹਾ
|
||||||
|
Name[pl]=Ciemna Bryza
|
||||||
|
Name[pt]=Brisa Escura
|
||||||
|
Name[pt_BR]=Breeze Dark
|
||||||
|
Name[ro]=Briză, întunecat
|
||||||
|
Name[ru]=Breeze, тёмный вариант
|
||||||
|
Name[sa]=वायुः अन्धकारः
|
||||||
|
Name[sk]=Tmavý vánok
|
||||||
|
Name[sl]=Sapica, temna
|
||||||
|
Name[sr]=Поветарац тамни
|
||||||
|
Name[sr@ijekavian]=Поветарац тамни
|
||||||
|
Name[sr@ijekavianlatin]=Povetarac tamni
|
||||||
|
Name[sr@latin]=Povetarac tamni
|
||||||
|
Name[sv]=Breeze mörk
|
||||||
|
Name[tg]=Насими торик
|
||||||
|
Name[tr]=Esinti Koyu
|
||||||
|
Name[uk]=Темна Breeze
|
||||||
|
Name[x-test]=xxBreeze Darkxx
|
||||||
|
Name[zh_CN]=Breeze 微风深色
|
||||||
|
Name[zh_TW]=Breeze Dark
|
||||||
|
shadeSortColumn=true
|
||||||
|
|
||||||
|
[KDE]
|
||||||
|
contrast=4
|
||||||
|
|
||||||
|
[WM]
|
||||||
|
activeBackground={{colors.surface_container.default.red}},{{colors.surface_container.default.green}},{{colors.surface_container.default.blue}}
|
||||||
|
activeBlend={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
activeForeground={{colors.on_surface.default.red}},{{colors.on_surface.default.green}},{{colors.on_surface.default.blue}}
|
||||||
|
inactiveBackground={{colors.surface_container_low.default.red}},{{colors.surface_container_low.default.green}},{{colors.surface_container_low.default.blue}}
|
||||||
|
inactiveBlend={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
|
inactiveForeground={{colors.on_surface_variant.default.red}},{{colors.on_surface_variant.default.green}},{{colors.on_surface_variant.default.blue}}
|
||||||
70
shell/wallpaper/templates/foot.ini
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
[colors]
|
||||||
|
foreground={{colors.inverse_surface.dark.hex_stripped}}
|
||||||
|
background={{colors.surface.dark.hex_stripped}}
|
||||||
|
16=f5a97f
|
||||||
|
17=b7bdf8
|
||||||
|
alpha=1.000000
|
||||||
|
alpha-mode=matching
|
||||||
|
bright0=5b6078
|
||||||
|
bright1=ed8796
|
||||||
|
bright2=a6da95
|
||||||
|
bright3=eed49f
|
||||||
|
bright4=8aadf4
|
||||||
|
bright5=f5bde6
|
||||||
|
bright6=8bd5ca
|
||||||
|
bright7=cad3f5
|
||||||
|
jump-labels=24273a f5a97f
|
||||||
|
regular0=494d64
|
||||||
|
regular1=ed8796
|
||||||
|
regular2=a6da95
|
||||||
|
regular3=eed49f
|
||||||
|
regular4=8aadf4
|
||||||
|
regular5=f5bde6
|
||||||
|
regular6=8bd5ca
|
||||||
|
regular7=b8c0e0
|
||||||
|
search-box-match=24273a a6da95
|
||||||
|
search-box-no-match=24273a ed8796
|
||||||
|
selection-background=8aadf4
|
||||||
|
selection-foreground=24273a
|
||||||
|
urls=8aadf4
|
||||||
|
|
||||||
|
[cursor]
|
||||||
|
color=181818 cdcdcd
|
||||||
|
|
||||||
|
[key-bindings]
|
||||||
|
clipboard-copy=Control+Shift+c XF86Copy
|
||||||
|
clipboard-paste=Control+Shift+v XF86Paste
|
||||||
|
font-decrease=Control+minus Control+KP_Subtract
|
||||||
|
font-increase=Control+plus Control+equal Control+KP_Add
|
||||||
|
font-reset=Control+0 Control+KP_0
|
||||||
|
fullscreen=none
|
||||||
|
maximize=none
|
||||||
|
minimize=none
|
||||||
|
noop=none
|
||||||
|
pipe-command-output=[wl-copy] none
|
||||||
|
pipe-scrollback=[sh -c 'xurls | fuzzel | xargs -r firefox'] none
|
||||||
|
pipe-selected=[xargs -r firefox] none
|
||||||
|
pipe-visible=[sh -c 'xurls | fuzzel | xargs -r firefox'] none
|
||||||
|
primary-paste=Shift+Insert
|
||||||
|
prompt-next=Control+Shift+x
|
||||||
|
prompt-prev=Control+Shift+z
|
||||||
|
scrollback-down-half-page=none
|
||||||
|
scrollback-down-line=none
|
||||||
|
scrollback-down-page=Shift+Page_Down
|
||||||
|
scrollback-end=none
|
||||||
|
scrollback-home=none
|
||||||
|
scrollback-up-half-page=none
|
||||||
|
scrollback-up-line=none
|
||||||
|
scrollback-up-page=Shift+Page_Up
|
||||||
|
search-start=Control+Shift+r
|
||||||
|
show-urls-copy=none
|
||||||
|
show-urls-launch=Control+Shift+o
|
||||||
|
show-urls-persistent=none
|
||||||
|
spawn-terminal=Control+Shift+n
|
||||||
|
unicode-input=Control+Shift+u
|
||||||
|
|
||||||
|
[main]
|
||||||
|
font=DejaVuSansM Nerd Font:size=14
|
||||||
|
gamma-correct-blending=no
|
||||||
|
shell=zsh
|
||||||
|
term=xterm-256color
|
||||||
4
shell/wallpaper/templates/hyprland-colors.conf
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<* for name, value in colors *>
|
||||||
|
$image = {{image}}
|
||||||
|
${{name}} = rgba({{value.default.hex_stripped}}ff)
|
||||||
|
<* endfor *>
|
||||||
20
shell/wallpaper/templates/nvim.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors": {
|
||||||
|
"base00": "{{colors.surface.dark.hex}}",
|
||||||
|
"base01": "{{colors.surface_container.dark.hex}}",
|
||||||
|
"base02": "{{colors.surface_container_high.dark.hex}}",
|
||||||
|
"base03": "{{colors.outline.dark.hex}}",
|
||||||
|
"base04": "{{colors.on_surface_variant.dark.hex}}",
|
||||||
|
"base05": "{{colors.on_surface.dark.hex}}",
|
||||||
|
"base06": "{{colors.inverse_surface.dark.hex}}",
|
||||||
|
"base07": "{{colors.inverse_on_surface.dark.hex}}",
|
||||||
|
"base08": "{{colors.primary_fixed.dark.hex}}",
|
||||||
|
"base09": "{{colors.tertiary.dark.hex}}",
|
||||||
|
"base0A": "{{colors.secondary.dark.hex}}",
|
||||||
|
"base0B": "{{colors.primary.dark.hex}}",
|
||||||
|
"base0C": "{{colors.tertiary.light.hex}}",
|
||||||
|
"base0D": "{{colors.primary.light.hex}}",
|
||||||
|
"base0E": "{{colors.secondary.light.hex}}",
|
||||||
|
"base0F": "{{colors.error.light.hex}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
shell/wallpaper/templates/qtct-colors.conf
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[ColorScheme]
|
||||||
|
active_colors={{colors.on_background.default.hex}}, {{colors.surface.default.hex}}, {{colors.surface_container_high.default.hex}}, #cacaca, #9f9f9f, #b8b8b8, {{colors.on_background.default.hex}}, #ffffff, {{colors.on_surface.default.hex}}, {{colors.background.default.hex}}, {{colors.background.default.hex}}, {{colors.shadow.default.hex}}, {{colors.primary_container.default.hex}}, {{colors.on_primary_container.default.hex}}, {{colors.secondary.default.hex}}, {{colors.primary.default.hex}}, {{colors.surface.default.hex}}, {{colors.scrim.default.hex}}, {{colors.surface.default.hex}}, {{colors.on_surface.default.hex}}, {{colors.secondary.default.hex}}
|
||||||
|
disabled_colors={{colors.on_background.default.hex}}, {{colors.surface.default.hex}}, {{colors.surface_container_high.default.hex}}, #cacaca, #9f9f9f, #b8b8b8, {{colors.on_background.default.hex}}, #ffffff, {{colors.on_surface.default.hex}}, {{colors.background.default.hex}}, {{colors.background.default.hex}}, {{colors.shadow.default.hex}}, {{colors.primary_container.default.hex}}, {{colors.on_primary_container.default.hex}}, {{colors.secondary.default.hex}}, {{colors.primary.default.hex}}, {{colors.surface.default.hex}}, {{colors.scrim.default.hex}}, {{colors.surface.default.hex}}, {{colors.on_surface.default.hex}}, {{colors.secondary.default.hex}}
|
||||||
|
inactive_colors={{colors.on_background.default.hex}}, {{colors.surface.default.hex}}, {{colors.surface_container_high.default.hex}}, #cacaca, #9f9f9f, #b8b8b8, {{colors.on_background.default.hex}}, #ffffff, {{colors.on_surface.default.hex}}, {{colors.background.default.hex}}, {{colors.background.default.hex}}, {{colors.shadow.default.hex}}, {{colors.primary_container.default.hex}}, {{colors.on_primary_container.default.hex}}, {{colors.secondary.default.hex}}, {{colors.primary.default.hex}}, {{colors.surface.default.hex}}, {{colors.scrim.default.hex}}, {{colors.surface.default.hex}}, {{colors.on_surface.default.hex}}, {{colors.secondary.default.hex}}
|
||||||
32
shell/widgets/ColoredIcon.qml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property var source
|
||||||
|
property var implicitSize: 0
|
||||||
|
property var color: "white"
|
||||||
|
readonly property real actualSize: Math.min(root.width, root.height)
|
||||||
|
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: root.source
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
colorization: 1
|
||||||
|
colorizationColor: root.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: root.color
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
34
shell/widgets/FontIcon.qml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: textIcon
|
||||||
|
|
||||||
|
property real fill: 0
|
||||||
|
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
font {
|
||||||
|
family: "Material Symbols Outlined"
|
||||||
|
pointSize: Math.max(parent.height * 0.50, 11)
|
||||||
|
|
||||||
|
variableAxes: {
|
||||||
|
"FILL": fill
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on fill {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
shell/widgets/FontIconButton.qml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
MaterialButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real implicitSize
|
||||||
|
property string iconName: ""
|
||||||
|
property string activeIconColor: ShellSettings.colors["inverse_primary"]
|
||||||
|
property string inactiveIconColor: ShellSettings.colors["inverse_surface"]
|
||||||
|
|
||||||
|
implicitWidth: this.implicitSize
|
||||||
|
implicitHeight: this.implicitSize
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: textIcon
|
||||||
|
text: root.iconName
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
color: root.containsMouse || root.checked ? root.activeIconColor : root.inactiveIconColor
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
font {
|
||||||
|
family: "Material Symbols Outlined"
|
||||||
|
pointSize: Math.max(parent.height * 0.60, 11)
|
||||||
|
|
||||||
|
variableAxes: {
|
||||||
|
"FILL": fill
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property real fill: !root.containsMouse && !root.checked ? 0 : 1
|
||||||
|
|
||||||
|
Behavior on fill {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
shell/widgets/IconButton.qml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property string source
|
||||||
|
property var implicitSize: 24
|
||||||
|
property var padding: 0
|
||||||
|
property var radius: 20
|
||||||
|
property var activeRectangle: true
|
||||||
|
property var color: ShellSettings.colors.inactive_translucent
|
||||||
|
property var activeColor: ShellSettings.colors.active_translucent
|
||||||
|
signal clicked
|
||||||
|
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: iconBackground
|
||||||
|
color: ShellSettings.colors.active_translucent
|
||||||
|
radius: root.radius
|
||||||
|
visible: iconButton.containsMouse && root.activeRectangle
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out a way to color images better
|
||||||
|
IconImage {
|
||||||
|
id: iconImage
|
||||||
|
source: root.source
|
||||||
|
visible: true
|
||||||
|
// color: {
|
||||||
|
// if (!activeRectangle)
|
||||||
|
// return root.color;
|
||||||
|
//
|
||||||
|
// return iconButton.containsMouse ? root.activeColor : root.color;
|
||||||
|
// }
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: root.padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: iconButton
|
||||||
|
hoverEnabled: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
105
shell/widgets/MaterialSlider.qml
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
value: 0.5
|
||||||
|
from: 0.0
|
||||||
|
to: 1.0
|
||||||
|
|
||||||
|
property string text
|
||||||
|
property Component icon
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: background
|
||||||
|
implicitWidth: parent.width
|
||||||
|
implicitHeight: parent.height
|
||||||
|
width: root.availableWidth
|
||||||
|
height: implicitHeight
|
||||||
|
x: root.leftPadding
|
||||||
|
y: root.topPadding + root.availableHeight / 2 - height / 2
|
||||||
|
z: 0
|
||||||
|
color: ShellSettings.colors["surface_container_highest"]
|
||||||
|
radius: height / 2
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: background.implicitWidth
|
||||||
|
height: background.implicitHeight
|
||||||
|
radius: background.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: visualPos
|
||||||
|
width: root.visualPosition * (root.availableWidth - root.height) + (root.height / 2)
|
||||||
|
height: parent.height
|
||||||
|
color: ShellSettings.colors["primary"]
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: sliderText
|
||||||
|
text: root.text
|
||||||
|
visible: text !== ""
|
||||||
|
color: ShellSettings.colors["inverse_primary"]
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
|
||||||
|
font {
|
||||||
|
pointSize: Math.max(handle.implicitHeight * 0.35, 11)
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: {
|
||||||
|
let visualWidth = (root.visualPosition * root.availableWidth);
|
||||||
|
if ((visualWidth / root.availableWidth) < 0.5)
|
||||||
|
return visualPos.right;
|
||||||
|
else
|
||||||
|
return parent.left;
|
||||||
|
}
|
||||||
|
right: {
|
||||||
|
let visualWidth = (root.visualPosition * root.availableWidth);
|
||||||
|
if ((visualWidth / root.availableWidth) > 0.5)
|
||||||
|
return visualPos.right;
|
||||||
|
else
|
||||||
|
return parent.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
leftMargin: 20
|
||||||
|
rightMargin: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
id: handle
|
||||||
|
color: ShellSettings.colors["primary"]
|
||||||
|
implicitWidth: root.height
|
||||||
|
implicitHeight: root.height
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
x: root.leftPadding + root.visualPosition * (root.availableWidth - width)
|
||||||
|
y: root.topPadding + root.availableHeight / 2 - height / 2
|
||||||
|
// icon maybe
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.icon !== undefined
|
||||||
|
sourceComponent: root.icon
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
shell/widgets/RoundSlider.qml
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider
|
||||||
|
implicitHeight: 8
|
||||||
|
property var accentColor: ShellSettings.colors["primary"]
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
id: sliderContainer
|
||||||
|
width: slider.availableWidth
|
||||||
|
height: slider.implicitHeight
|
||||||
|
color: ShellSettings.colors["inverse_surface"]
|
||||||
|
radius: 4
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
source: Rectangle {
|
||||||
|
width: sliderContainer.width
|
||||||
|
height: sliderContainer.height
|
||||||
|
radius: sliderContainer.radius
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: sliderContainer.width
|
||||||
|
height: sliderContainer.height
|
||||||
|
radius: sliderContainer.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: fill
|
||||||
|
width: slider.handle.width / 2 + slider.visualPosition * (sliderContainer.width - slider.handle.width)
|
||||||
|
height: sliderContainer.height
|
||||||
|
color: Qt.color(slider.accentColor ?? "purple").darker(1.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
id: handleRect
|
||||||
|
x: slider.visualPosition * (slider.availableWidth - width)
|
||||||
|
y: slider.topPadding + slider.availableHeight / 2 - height / 2
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
radius: width / 2
|
||||||
|
color: slider.pressed ? Qt.color(slider.accentColor ?? "purple").darker(1.5) : slider.accentColor ?? "purple"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
shell/widgets/Separator.qml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: ShellSettings.colors["active"]
|
||||||
|
radius: 5
|
||||||
|
width: 3.5
|
||||||
|
height: 15
|
||||||
|
}
|
||||||
24
shell/widgets/StyledMouseArea.qml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import QtQuick
|
||||||
|
import qs
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: root
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
property real radius: width / 2
|
||||||
|
property bool checked: false
|
||||||
|
property var activeColor: ShellSettings.colors.active_translucent
|
||||||
|
property var inactiveColor: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: root.containsMouse || root.checked ? root.activeColor : root.inactiveColor
|
||||||
|
radius: root.radius
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
shell/widgets/StyledPopup.qml
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
id: root
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: container.width
|
||||||
|
implicitHeight: container.height
|
||||||
|
|
||||||
|
default property alias contentItem: container.children
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
// root.anchor.rect.y = -root.implicitHeight;
|
||||||
|
root.visible = true;
|
||||||
|
grab.active = true;
|
||||||
|
// slideAnimation.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
root.visible = false;
|
||||||
|
grab.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertyAnimation {
|
||||||
|
// id: slideAnimation
|
||||||
|
// target: root.anchor.rect
|
||||||
|
// property: "y"
|
||||||
|
// from: -root.implicitHeight // Off-screen position
|
||||||
|
// to: 0 // On-screen position
|
||||||
|
// duration: 300 // Animation duration in milliseconds
|
||||||
|
// }
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
id: grab
|
||||||
|
windows: [root]
|
||||||
|
onCleared: root.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapperRectangle {
|
||||||
|
id: container
|
||||||
|
margin: 5
|
||||||
|
radius: 12
|
||||||
|
color: ShellSettings.colors.surface_translucent
|
||||||
|
|
||||||
|
border {
|
||||||
|
width: 1
|
||||||
|
color: ShellSettings.colors.active_translucent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||