refactor rice into config

This commit is contained in:
atagen 2025-07-21 12:26:28 +10:00
parent 08c13ea2bc
commit 9a1217044c
64 changed files with 225 additions and 230 deletions

View file

@ -0,0 +1,133 @@
pragma ComponentBehavior: Bound
import QtQuick
Rectangle {
id: base
anchors {
fill: parent
margins: 2
}
color: colours[0]
required property string format
required property var colours
property var clock: genClock(format)
property var date: new Date()
property string time: date.toLocaleString(Qt.locale())
property int cols: getColSum(clock)
property real colWidth: (base.width - topgrid.spacing - base.anchors.margins) / cols
property var keys: {
"a": [(date.getHours() > 11) | 0, -1],
"H": [-1].concat(binarise(date.getHours(), 5)),
"h": binarise(date.getHours() % 12, 4),
"m": binarise(date.getMinutes(), 6),
"s": binarise(date.getSeconds(), 6)
}
function genClock(format) {
return format.split('').map(k => keys[k]);
}
function getColSum(clock) {
return clock.map(x => Math.ceil(x.length / 2)).reduce((acc, el) => acc + el);
}
function binarise(n, p) {
return n.toString(2) // to base-2 string
.padStart(p, 0) // zero pad
.split('') // split to array
.slice(-p) // take only desired bits, lsb first
.map(x => parseInt(x)); // map to int
}
Grid {
id: topgrid
rows: 1
columns: base.clock.length
spacing: 2
anchors.fill: parent
Repeater {
model: base.clock.length
Grid {
id: inner
required property int index
property var bits: base.clock[index]
property int cols: bits.length / 2
width: base.colWidth * cols
height: base.height
rows: 2
columns: cols
spacing: 1
function calcBitSize() {
let cell = inner.width - inner.spacing * 2;
let def = (cell / inner.cols);
return (def > inner.height / 2) ? inner.height / 2 : def;
}
Repeater {
model: inner.bits.length
Rectangle {
required property int index
height: inner.calcBitSize()
width: height
color: "transparent"
Rectangle {
property int bit: inner.bits[parent.index]
property string on: base.colours[1]
property string off: base.colours[0]
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
height: parent.height
width: parent.width
border.width: 1
border.color: bit == -1 ? off : on
color: (bit == -1 || !bit) ? off : on
Behavior on color {
ColorAnimation {
duration: 150
}
}
radius: height
//
// height: bit ? parent.height / 3 * 2 : parent.height
// Behavior on height {
// NumberAnimation {
// duration: 50
// }
// }
// width: bit ? 1 : height
// Behavior on width {
// NumberAnimation {
// duration: 50
// }
// }
// radius: bit ? 0 : height
// Behavior on radius {
// NumberAnimation {
// duration: 50
// }
// }
}
}
}
}
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
base.date = new Date();
}
}
}
}

View file

@ -0,0 +1,222 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import Quickshell.Io
Singleton {
id: topLevel
required property real width
Timer {
id: closeTimer
interval: 400
running: false
repeat: false
onTriggered: launcherData.active = false
}
PersistentProperties {
id: launcherData
property bool open: false
property bool active: false
property real curWidth: 0
onOpenChanged: {
if (open) {
curWidth = topLevel.width;
launcherData.active = true;
} else {
curWidth = 0;
closeTimer.start();
}
}
Behavior on curWidth {
NumberAnimation {
duration: 400
easing.type: Easing.InOutQuad
}
}
}
IpcHandler {
target: "launch"
function open(): void {
launcherData.open = true;
}
function close(): void {
launcherData.open = false;
}
function toggle(): void {
launcherData.open = !launcherData.open;
}
}
LazyLoader {
id: loader
activeAsync: launcherData.active
PanelWindow {
id: launcherBase
anchors {
top: true
bottom: true
right: true
}
color: "transparent"
implicitWidth: topLevel.width
WlrLayershell.layer: WlrLayer.Overlay
focusable: true
exclusionMode: ExclusionMode.Ignore
WlrLayershell.namespace: "shell:launcher"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
// launcherData.curWidth
Rectangle {
color: "#ffab5b"
anchors {
fill: parent
topMargin: parent.height / 3
bottomMargin: anchors.topMargin
leftMargin: topLevel.width - launcherData.curWidth
}
bottomLeftRadius: 10
// topLeftRadius: 10
Rectangle {
color: "#272a2a"
anchors {
fill: parent
topMargin: 3
bottomMargin: 3
leftMargin: 3
}
bottomLeftRadius: 10
// topLeftRadius: 10
// implicitWidth: topLevel.width
TextField {
id: searchField
anchors {
top: parent.top
left: parent.left
right: parent.right
topMargin: 10
leftMargin: 10
}
font {
family: "Inria Sans"
pointSize: 12
}
color: "#202e2f"
height: 24
background: Rectangle {
color: "#caccce"
radius: 5
}
focus: true
Keys.forwardTo: [list]
Keys.onEscapePressed: launcherData.open = false
onAccepted: {
if (list.currentItem) {
list.currentItem.clicked(null);
}
}
}
Rectangle {
id: emptyScrollbar
anchors {
left: parent.left
top: searchField.bottom
bottom: list.bottom
topMargin: 6
bottomMargin: 4
leftMargin: 4
}
color: "#3a5299ff"
width: 3
radius: 10
}
ListView {
id: list
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
top: searchField.bottom
topMargin: 4
leftMargin: 12
bottomMargin: 12
}
clip: true
cacheBuffer: 0
model: ScriptModel {
values: DesktopEntries.applications.values.map(x => x).filter(entry => {
const search = searchField.text.toLowerCase();
const name = entry.name.toLowerCase();
return search.length ? name.indexOf(search) > -1 : true;
})
}
onModelChanged: list.currentIndex = 0
highlight: Rectangle {
anchors {
left: parent.left
right: parent.right
}
color: "#7f5299ff"
border.color: "#928cc9"
border.width: 2
}
spacing: 0
delegate: MouseArea {
id: clickableEntry
required property DesktopEntry modelData
onClicked: {
modelData.execute();
launcherData.open = false;
}
implicitHeight: 24
implicitWidth: ListView.view.width
RowLayout {
id: rowEntry
anchors {
left: parent.left
leftMargin: 4
verticalCenter: parent.verticalCenter
}
IconImage {
asynchronous: true
implicitSize: 18
source: Quickshell.iconPath(clickableEntry.modelData.icon)
}
Text {
font {
family: "Inria Sans"
pointSize: 12
}
color: "#ffab5b"
text: clickableEntry.modelData.name
Layout.alignment: Qt.AlignBottom
}
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,18 @@
pragma Singleton
import Quickshell
import Quickshell.Services.Notifications
Singleton {
NotificationServer {
id: notifications
actionsSupported: true
bodyHyperlinksSupported: true
// bodyImagesSupported: true
bodyMarkupSupported: true
imageSupported: true
onNotification: noti => {
}
}
}

View file

@ -0,0 +1,84 @@
import Quickshell
import QtQuick
import QtQuick.Particles
PanelWindow {
id: particleRoot
anchors {
top: true
bottom: true
left: true
right: true
}
color: "transparent"
exclusionMode: ExclusionMode.Ignore
screen: Quickshell.screens.find(s => s.name == "DP-1")
property var mousePos: [width/2, height/2]
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
particleRoot.mousePos = [mouse.x, mouse.y];
}
}
ParticleSystem {
id: sys
running: true
anchors.fill: parent
ItemParticle {
system: sys
delegate: Component {
Rectangle {
color: "black"
width: 7
height: width
radius: width
Rectangle {
color: "white"
width: 5
height: width
radius: width
}
}
}
}
Emitter {
system: sys
// x: particleRoot.mousePos[0]-10
// y: particleRoot.mousePos[1]-10
x: particleRoot.width/2
y: particleRoot.height/2
height: 60
width: 60
emitRate: 200
lifeSpan: 3000
startTime: 0
velocity: CumulativeDirection {
AngleDirection {
angle: 0
angleVariation: 360
magnitude: 10
magnitudeVariation: 0.2
}
}
}
Attractor {
pointX: particleRoot.mousePos[0]
pointY: particleRoot.mousePos[1]
strength: 200
affectedParameter: Attractor.Position
proportionalToDistance: Attractor.InverseLinear
}
}
}

View file

@ -0,0 +1,34 @@
pragma Singleton
import Quickshell
Singleton {
property var c: {
"bg": "#1b2021",
"fg": "#cecbca",
"black": "#272a2a",
"black_b": "#202e2f",
"red": "#c43325",
"red_b": "#c46056",
"green": "#8cc992",
"green_b": "#c2dab0",
"yellow": "#ffb852",
"yellow_b": "#ffab5b",
"blue": "#5299ff",
"blue_b": "#92beff",
"magenta": "#645ac9",
"magenta_b": "#928cc9",
"cyan": "#5abfc9",
"cyan_b": "#8cc4c9",
"white": "#b0c2da",
"white_b": "#caccce",
}
}

View file

@ -0,0 +1,212 @@
// components
import "bink" as Bink
import "launcher" as Launcher
// singletons
import "title"
import "tags"
import "rice"
import Quickshell
import Quickshell.Wayland
import QtQuick
import QtQuick.Controls
ShellRoot {
// rhs main
Variants {
model: Quickshell.screens.filter(s => s.name == "DP-2")
delegate: PanelWindow {
id: bink
property var modelData
anchors {
top: true
right: true
}
implicitHeight: 22
implicitWidth: 115
color: "transparent"
Rectangle {
anchors.fill: parent
bottomLeftRadius: 10
color: Colours.c.black
}
Rectangle {
anchors {
leftMargin: 8
bottomMargin: 3
fill: parent
}
color: Colours.c.black
bottomLeftRadius: 10
Bink.Bink {
format: "ahms"
colours: ["transparent", Colours.c.yellow_b]
}
}
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Top
screen: modelData
}
}
// centre main
Variants {
model: Quickshell.screens.filter(s => s.name == "DP-2")
delegate: PanelWindow {
id: windowTitle
property var modelData
anchors {
top: true
}
TextMetrics {
id: textInfo
font {
family: "Inria Sans"
pointSize: 12
}
elideWidth: 550
elide: Qt.ElideMiddle
text: Title.currentWindow
}
implicitHeight: textInfo.height + 6
implicitWidth: (textInfo.width > textInfo.elideWidth ? textInfo.elideWidth : textInfo.width) + 24
Behavior on implicitWidth {
NumberAnimation {
duration: 150
easing.type: Easing.InOutQuad
}
}
color: "transparent"
Rectangle {
anchors {
fill: parent
}
color: Colours.c.black
bottomLeftRadius: 10
bottomRightRadius: 10
Rectangle {
anchors {
fill: parent
leftMargin: 12
rightMargin: 12
topMargin: 4
}
color: "transparent"
Text {
font {
family: "Inria Sans"
pointSize: 12
}
color: Colours.c.yellow_b
text: textInfo.elidedText
}
}
}
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Top
screen: modelData
}
}
// bottom middle main
Variants {
model: Quickshell.screens.filter(s => s.name == "DP-2")
delegate: PanelWindow {
id: tags
property var modelData
anchors {
bottom: true
}
implicitHeight: 22
implicitWidth: Tags.keys.length * 13 + 24
// implicitWidth:.width + 24
color: "transparent"
Rectangle {
anchors {
fill: parent
}
color: Colours.c.black
topLeftRadius: 10
topRightRadius: 10
Rectangle {
anchors {
fill: parent
leftMargin: 12
rightMargin: 12
topMargin: 3
}
Row {
anchors {
fill: parent
}
spacing: 1
Repeater {
model: Tags.keys
delegate: Column {
id: baseCol
required property var modelData
spacing: 3
Rectangle {
property var tag: Tags.tags[baseCol.modelData]
width: 12
height: width
radius: width
color: tag.urgent ? Colours.c.red_b : (tag.enabled) ? Colours.c.yellow_b : Colours.c.black
Behavior on color {
ColorAnimation {
duration: 300
}
}
border {
width: 1
color: tag.urgent ? Colours.c.red_b : Colours.c.yellow_b
Behavior on color {
ColorAnimation {
duration: 300
}
}
}
}
Rectangle {
property var tag: Tags.tags[baseCol.modelData]
anchors.horizontalCenter: parent.horizontalCenter
width: 1
height: 1
color: tag.urgent ? Colours.c.red_b : (tag.occupied) ? Colours.c.yellow_b : Colours.c.black
Behavior on color {
ColorAnimation {
duration: 300
}
}
}
}
}
}
color: "transparent"
} // inner container
}// outer visible rect
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Top
screen: modelData
}//invisible rect
}
// pops up on current monitor
Launcher.Launcher {
width: 190
}
}

View file

@ -0,0 +1,80 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: data
property var tags: {}
property var keys: []
Timer {
id: reconnectTimer
running: false
repeat: true
onTriggered: () => sock.connected = true
interval: 5000
}
Socket {
id: sock
connected: true
onConnectionStateChanged: () => {
reconnectTimer.running = sock.connected ? false : true
if (!sock.connected) {
data.tags = {}
data.keys = []
}
}
path: Quickshell.env("XDG_RUNTIME_DIR") + "/niri-tag-events.sock"
parser: SplitParser {
onRead: tag_data => {
let event = JSON.parse(tag_data);
let ensure = (d, t) => {
if (!d[t]) {
d[t] = {
occupied: false,
urgent: false,
enabled: false,
id: t
};
}
return d;
};
if (event.TagEmpty) {
data.tags = ensure(data.tags, event.TagEmpty);
data.tags[event.TagEmpty].occupied = false;
} else if (event.TagOccupied) {
data.tags = ensure(data.tags, event.TagOccupied);
data.tags[event.TagOccupied].occupied = true;
} else if (event.TagUrgent) {
data.tags = ensure(data.tags, event.TagUrgent);
data.tags[event.TagUrgent].urgent = true;
} else if (event.TagEnabled) {
data.tags = ensure(data.tags, event.TagEnabled);
data.tags[event.TagEnabled].enabled = true;
} else if (event.TagDisabled) {
data.tags = ensure(data.tags, event.TagDisabled);
data.tags[event.TagDisabled].enabled = false;
} else if (event.TagExclusive) {
data.tags = ensure(data.tags, event.TagExclusive);
for (const [k] of Object.keys(data.tags)) {
data.tags[k].enabled = false;
}
data.tags[event.TagExclusive].enabled = true;
} else if (event.TagFullState) {
data["tags"] = event.TagFullState;
for (const [k, v] of Object.entries(data.tags)) {
data.tags[k].id = k;
}
}
data.keys = Object.keys(data.tags);
data.tagsChanged();
data.keysChanged();
}
}
}
}

View file

@ -0,0 +1,52 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: data
property var currentWindow: " "
property var currentId: 0
property var windows: {}
Process {
id: proc
command: ["niri", "msg", "-j", "event-stream"]
running: true
stdout: SplitParser {
onRead: niri_data => {
var event = JSON.parse(niri_data);
if (event.WindowsChanged) {
let new_data = event.WindowsChanged.windows.reduce((acc, el) => {
acc[el.id] = el.title == "" ? el.app_id : el.title;
return acc;
}, {});
data.windows = new_data;
let focused = event.WindowsChanged.windows.find(w => w.is_focused);
if (focused === undefined) {
data.currentId = 0;
}
} else if (event.WindowOpenedOrChanged) {
let target = event.WindowOpenedOrChanged.window;
data.windows[target.id] = target.title == "" ? target.app_id : target.title;
if (target.is_focused) {
data.currentId = target.id;
}
} else if (event.WindowClosed) {
delete data[event.WindowClosed.id];
if (Object.keys(data).length == 0) {
data.currentId = 0;
}
} else if (event.WindowFocusChanged) {
if (event.WindowFocusChanged.id === null) {
data.currentId = 0;
} else {
data.currentId = event.WindowFocusChanged.id;
}
}
data.currentWindow = data.currentId == 0 ? " " : data.windows[data.currentId];
}
}
}
}