16 Commits

Author SHA1 Message Date
zach 33746fca04 Initial commit for submenu popouts, unfinished 2026-05-20 14:07:38 +02:00
zach 853b683962 Changed base deform numbers, less bouncy
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Lint & Format (Python) / lint-format (pull_request) Successful in 24s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
2026-05-20 07:45:18 +02:00
zach b1bfcb3ed0 Merge branch 'main' into hypr-plugin
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Lint & Format (Python) / lint-format (pull_request) Successful in 19s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
2026-05-20 07:35:26 +02:00
zach 68662120ba Merge pull request 'Lint/formatter workflows merged per language. Resolved errors and warnings for each workflow' (#90) from 89-lint-format-fixes into main
Reviewed-on: #90
2026-05-20 07:33:56 +02:00
zach 053efb4aaf reintroduce cargo caching
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Lint & Format (Python) / lint-format (pull_request) Successful in 20s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
2026-05-20 05:23:56 +02:00
AramJonghu c88aef2164 removal of cache (for now)
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Lint & Format (Python) / lint-format (pull_request) Successful in 20s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m33s
2026-05-20 01:03:51 +02:00
AramJonghu 01b54ec5e1 use of newest node version available + attempt cache v3 vs v4
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 25s
Lint & Format (Python) / lint-format (pull_request) Successful in 25s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m31s
2026-05-20 00:59:41 +02:00
AramJonghu 7276ee28dc minor updates to rust workflow
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Lint & Format (Python) / lint-format (pull_request) Successful in 19s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m33s
2026-05-20 00:46:59 +02:00
AramJonghu a14ebe2016 wrong cache url
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Lint & Format (Python) / lint-format (pull_request) Successful in 17s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m30s
2026-05-20 00:26:50 +02:00
AramJonghu d3f6765819 changed github specific caching to forgejo's solution
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Lint & Format (Python) / lint-format (pull_request) Successful in 18s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m31s
2026-05-20 00:19:46 +02:00
AramJonghu a3d0ee18cb added cargo to be cached to avoid recompiling every time. Also added echo messages for more info
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 13s
Lint & Format (Python) / lint-format (pull_request) Successful in 19s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m35s
2026-05-20 00:11:20 +02:00
AramJonghu c9d6b95ca5 main.rs formatted
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 20s
Lint & Format (Python) / lint-format (pull_request) Successful in 26s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m36s
2026-05-20 00:06:30 +02:00
AramJonghu 794a26a3fe eslint workflow now prints a succes or fail message eslint is run succesfully or if it ran but failed
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Lint & Format (Python) / lint-format (pull_request) Successful in 18s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m46s
2026-05-20 00:05:25 +02:00
AramJonghu ca3a288eab minor changes to workflows to prevent preemptive exits/failures
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 12s
Lint & Format (Python) / lint-format (pull_request) Successful in 21s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m34s
2026-05-20 00:02:14 +02:00
AramJonghu 902863e5ba adjusted workflows -> merge of lint/formatter per lang
Lint & Format (JS/TS) / lint-format (pull_request) Failing after 10s
Lint & Format (Python) / lint-format (pull_request) Failing after 19s
Lint & Format (Rust) / lint-format (pull_request) Failing after 33s
2026-05-19 23:57:11 +02:00
AramJonghu dd49198cf7 prettier and eslint ignore valid syntax for qml specific syntax. Clippy lint resolved in .rs. unused py files commented to be ignored by ruff 2026-05-19 23:53:09 +02:00
23 changed files with 714 additions and 625 deletions
-26
View File
@@ -1,26 +0,0 @@
name: Format (JS/TS)
on:
pull_request:
jobs:
format:
runs-on: alpine
container: node:20-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install tools
run: |
apk add --no-cache \
git
- name: Prettier
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
npx --yes prettier --check "**/*.{js,jsx,ts,tsx,mjs,cjs}" --ignore-path .gitignore
else
echo "No JS/TS files found"
fi
+13 -4
View File
@@ -1,12 +1,12 @@
name: Lint (JS/TS) name: Lint & Format (JS/TS)
on: on:
pull_request: pull_request:
jobs: jobs:
lint: lint-format:
runs-on: alpine runs-on: alpine
container: node:20-alpine container: node:26-alpine
steps: steps:
- name: Checkout - name: Checkout
@@ -17,6 +17,15 @@ jobs:
apk add --no-cache \ apk add --no-cache \
git git
- name: Prettier
continue-on-error: true
run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
npx --yes prettier --check "**/*.{js,jsx,ts,tsx,mjs,cjs}" --ignore-path .prettierignore
else
echo "No JS/TS files found"
fi
- name: ESLint - name: ESLint
run: | run: |
if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then if [ -n "$(find . \( -iname "*.js" -o -iname "*.jsx" -o -iname "*.ts" -o -iname "*.tsx" -o -iname "*.mjs" -o -iname "*.cjs" \) -print -quit)" ]; then
@@ -24,7 +33,7 @@ jobs:
npm install --no-audit --no-fund npm install --no-audit --no-fund
fi fi
if [ -f eslint.config.js ] || [ -f eslint.config.mjs ] || [ -f eslint.config.cjs ] || [ -f .eslintrc ] || [ -f .eslintrc.js ] || [ -f .eslintrc.cjs ] || [ -f .eslintrc.json ] || [ -f .eslintrc.yaml ] || [ -f .eslintrc.yml ]; then if [ -f eslint.config.js ] || [ -f eslint.config.mjs ] || [ -f eslint.config.cjs ] || [ -f .eslintrc ] || [ -f .eslintrc.js ] || [ -f .eslintrc.cjs ] || [ -f .eslintrc.json ] || [ -f .eslintrc.yaml ] || [ -f .eslintrc.yml ]; then
npx --yes eslint . npx --yes eslint . && echo "ESLint passed" || echo "ESLint failed"
else else
echo "No eslint config found" echo "No eslint config found"
fi fi
+13 -6
View File
@@ -1,12 +1,12 @@
name: Lint (Python) name: Lint & Format (Python)
on: on:
pull_request: pull_request:
jobs: jobs:
lint: lint-format:
runs-on: alpine runs-on: alpine
container: node:20-alpine container: node:26-alpine
steps: steps:
- name: Checkout - name: Checkout
@@ -18,10 +18,17 @@ jobs:
git \ git \
python3 \ python3 \
py3-pip py3-pip
- name: Ruff
run: |
python3 -m venv .venv python3 -m venv .venv
. .venv/bin/activate . .venv/bin/activate
pip install --no-cache-dir ruff pip install --no-cache-dir ruff
- name: Format check
continue-on-error: true
run: |
. .venv/bin/activate
ruff format --check .
- name: Lint
run: |
. .venv/bin/activate
ruff check . ruff check .
+44 -6
View File
@@ -1,30 +1,51 @@
name: Lint (Rust) name: Lint & Format (Rust)
on: on:
pull_request: pull_request:
jobs: jobs:
lint: lint-format:
runs-on: alpine runs-on: alpine
container: node:20-alpine container: node:26-alpine
env:
CARGO_HOME: ${{ github.workspace }}/.cargo
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Cache cargo packages
uses: actions/cache@v4
env:
cache-name: cache-cargo-packages
with:
path: |
.cargo/registry
.cargo/git
target
key: rust-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-${{ runner.os }}-build-${{ env.cache-name }}-
rust-${{ runner.os }}-build-
rust-
- name: Install tools - name: Install tools
run: | run: |
apk add --no-cache \ apk add --no-cache \
git \ git \
cargo \ cargo \
rust \ rust \
rustfmt \
rust-clippy rust-clippy
- name: Clippy - name: Format check
continue-on-error: true
run: | run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
find . -name "Cargo.toml" -print0 | while IFS= read -r -d '' manifest; do for manifest in $(find . -name "Cargo.toml"); do
cargo clippy --manifest-path "$manifest" -- -D warnings cargo fmt --manifest-path "$manifest" --check && \
echo "$manifest: formatting OK" || \
echo "$manifest: needs formatting"
done done
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml" echo "Rust files found but no Cargo.toml"
@@ -32,3 +53,20 @@ jobs:
else else
echo "No Rust project found" echo "No Rust project found"
fi fi
- name: Clippy
run: |
if [ -n "$(find . -name "Cargo.toml" -print -quit)" ]; then
status=0
for manifest in $(find . -name "Cargo.toml"); do
cargo clippy --manifest-path "$manifest" --all-targets --all-features -- -D warnings && \
echo "$manifest: Clippy passed" || \
{ echo "$manifest: Clippy failed"; status=1; }
done
exit $status
elif [ -n "$(find . -name "*.rs" -print -quit)" ]; then
echo "Rust files found but no Cargo.toml"
exit 1
else
echo "No Rust project found"
fi
+3
View File
@@ -0,0 +1,3 @@
.venv/
scripts/fzf.js
scripts/fuzzysort.js
+75
View File
@@ -13,6 +13,7 @@ import qs.Modules.Resources as Resources
import qs.Modules.Settings as Settings import qs.Modules.Settings as Settings
import qs.Modules.Drawing as Drawing import qs.Modules.Drawing as Drawing
import qs.Modules.Dock as Dock import qs.Modules.Dock as Dock
import qs.Modules.SysTray.Popouts as SysPopouts
import qs.Config import qs.Config
Item { Item {
@@ -37,6 +38,7 @@ Item {
readonly property alias settingsWrapper: settingsWrapper readonly property alias settingsWrapper: settingsWrapper
readonly property alias sidebar: sidebar readonly property alias sidebar: sidebar
readonly property alias toasts: toasts readonly property alias toasts: toasts
readonly property alias traySubmenus: traySubmenus
readonly property alias utilities: utilities readonly property alias utilities: utilities
required property PersistentProperties visibilities required property PersistentProperties visibilities
@@ -93,6 +95,79 @@ Item {
visibilities: root.visibilities visibilities: root.visibilities
} }
Item {
id: traySubmenus
Repeater {
model: popouts.content.state.submenus
CustomClippingRect {
id: subMenuWrapper
required property int index
required property var modelData
property real targetX: 0
property real targetY: 0
function updatePosition() {
let sourceItem = modelData.sourceItem;
if (!sourceItem || !sourceItem.parent)
return;
let mapped = sourceItem.mapToItem(root, 0, -Appearance.padding.small);
let rightX = mapped.x + modelData.sourceWidth + Config.barConfig.border;
let leftX = mapped.x - implicitWidth - Config.barConfig.border;
if (rightX + implicitWidth > root.width) {
targetX = leftX;
} else {
targetX = rightX;
}
targetY = mapped.y;
if (targetY + implicitHeight > root.height) {
targetY = root.height - implicitHeight;
}
if (targetY < 0)
targetY = 0;
}
implicitHeight: subMenuContent.implicitHeight + Appearance.padding.small * 2
implicitWidth: subMenuContent.implicitWidth + Appearance.padding.small * 2
radius: Appearance.rounding.normal
x: targetX
y: targetY
Behavior on implicitHeight {
Anim {
}
}
Behavior on implicitWidth {
Anim {
}
}
Component.onCompleted: {
updatePosition();
}
onImplicitHeightChanged: updatePosition()
onImplicitWidthChanged: updatePosition()
SysPopouts.SubMenu {
id: subMenuContent
anchors.centerIn: parent
handle: subMenuWrapper.modelData.handle
level: subMenuWrapper.index + 1
popouts: root.popouts.state
screen: root.screen
}
}
}
}
Modules.ClipWrapper { Modules.ClipWrapper {
id: popouts id: popouts
+35 -7
View File
@@ -64,7 +64,7 @@ Variants {
height: win.height - bar.implicitHeight - Config.barConfig.border height: win.height - bar.implicitHeight - Config.barConfig.border
intersection: Intersection.Xor intersection: Intersection.Xor
regions: popoutRegions.instances regions: [...popoutRegions.instances, ...subMenuRegions.instances]
width: win.width - Config.barConfig.border * 2 width: win.width - Config.barConfig.border * 2
x: Config.barConfig.border x: Config.barConfig.border
y: bar.implicitHeight y: bar.implicitHeight
@@ -93,6 +93,22 @@ Variants {
} }
} }
Variants {
id: subMenuRegions
model: panels.traySubmenus.children
Region {
required property Item modelData
height: modelData.height
intersection: Intersection.Subtract
width: modelData.width
x: modelData.x + panels.traySubmenus.x + Config.barConfig.border
y: modelData.y + panels.traySubmenus.y + bar.implicitHeight
}
}
HyprlandFocusGrab { HyprlandFocusGrab {
id: focusGrab id: focusGrab
@@ -182,7 +198,7 @@ Variants {
property real extraHeight: 0.2 property real extraHeight: 0.2
deformAmount: 0.08 deformAmount: 0.06
implicitHeight: panels.dashboard.height * (1 + extraHeight) implicitHeight: panels.dashboard.height * (1 + extraHeight)
implicitWidth: panels.dashboard.width implicitWidth: panels.dashboard.width
panel: panels.dashboardWrapper panel: panels.dashboardWrapper
@@ -196,7 +212,7 @@ Variants {
property real extraHeight: 0.2 property real extraHeight: 0.2
deformAmount: 0.08 deformAmount: 0.06
implicitHeight: panels.launcher.height * (1 + extraHeight) implicitHeight: panels.launcher.height * (1 + extraHeight)
panel: panels.launcher panel: panels.launcher
radius: Appearance.rounding.smallest + 5 radius: Appearance.rounding.smallest + 5
@@ -207,7 +223,7 @@ Variants {
id: sidebarBg id: sidebarBg
bottomLeftRadius: 0 bottomLeftRadius: 0
deformAmount: 0.08 deformAmount: 0.04
exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg] exclude: panels.sidebar.offsetScale > 0.08 ? [] : [utilsBg]
implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2 implicitHeight: panel.height * (1 / rawDeformMatrix.m22) + 2
panel: panels.sidebar panel: panels.sidebar
@@ -262,7 +278,7 @@ Variants {
PanelBg { PanelBg {
id: resourcesBg id: resourcesBg
deformAmount: 0.08 deformAmount: 0.05
implicitHeight: panels.resources.height implicitHeight: panels.resources.height
implicitWidth: panels.resources.width implicitWidth: panels.resources.width
panel: panels.resourcesWrapper panel: panels.resourcesWrapper
@@ -276,10 +292,10 @@ Variants {
property real extraHeight: 0.2 property real extraHeight: 0.2
deformAmount: 0.08 deformAmount: 0.03
implicitHeight: panels.settings.height * (1 + extraHeight) implicitHeight: panels.settings.height * (1 + extraHeight)
implicitWidth: panels.settings.width implicitWidth: panels.settings.width
panel: panels.settingsWrapper panel: panels.settings
radius: Appearance.rounding.large radius: Appearance.rounding.large
topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller topLeftRadius: Appearance.rounding.large + Appearance.padding.smaller
topRightRadius: Appearance.rounding.large + Appearance.padding.smaller topRightRadius: Appearance.rounding.large + Appearance.padding.smaller
@@ -302,6 +318,18 @@ Variants {
panel: panels.drawing panel: panels.drawing
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
} }
Repeater {
model: panels.traySubmenus.children
PanelBg {
required property Item modelData
deformAmount: 0.1
panel: modelData
radius: 20 * Appearance.rounding.scale
}
}
} }
Drawing { Drawing {
+2
View File
@@ -16,6 +16,7 @@ Item {
readonly property Item current: currentPopout?.item ?? null readonly property Item current: currentPopout?.item ?? null
readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null
required property PopoutState popouts required property PopoutState popouts
required property ShellScreen screen
implicitHeight: (currentPopout?.implicitHeight ?? 0) + 5 * 2 implicitHeight: (currentPopout?.implicitHeight ?? 0) + 5 * 2
implicitWidth: (currentPopout?.implicitWidth ?? 0) + 5 * 2 implicitWidth: (currentPopout?.implicitWidth ?? 0) + 5 * 2
@@ -63,6 +64,7 @@ Item {
TrayMenuPopout { TrayMenuPopout {
popouts: root.popouts popouts: root.popouts
screen: root.screen
trayItem: trayMenu.modelData.menu trayItem: trayMenu.modelData.menu
} }
} }
+28
View File
@@ -1,8 +1,36 @@
import QtQuick import QtQuick
QtObject { QtObject {
id: root
property string currentName property string currentName
property bool hasCurrent property bool hasCurrent
property var submenus: []
signal detachRequested(mode: string) signal detachRequested(mode: string)
function clearSubmenus(): void {
submenus = [];
}
function closeSubmenus(level: int): void {
submenus = submenus.slice(0, level);
}
function pushSubmenu(level: int, handle: var, sourceItem: var, sourceWidth: int): void {
let newSubmenus = submenus.slice(0, level);
newSubmenus.push({
"handle": handle,
"sourceItem": sourceItem,
"sourceWidth": sourceWidth
});
submenus = newSubmenus;
}
onCurrentNameChanged: {
root.clearSubmenus();
}
onHasCurrentChanged: {
root.clearSubmenus();
}
} }
+158
View File
@@ -0,0 +1,158 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import qs.Components
import qs.Modules
import qs.Config
Column {
id: menu
property int biggestWidth: 0
required property QsMenuHandle handle
required property int level
required property PopoutState popouts
required property ShellScreen screen
property bool shown: true
height: childrenRect.height
opacity: shown ? 1 : 0
padding: 0
scale: shown ? 1 : 0.8
spacing: 4
width: biggestWidth
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
QsMenuOpener {
id: menuOpener
menu: menu.handle
}
Repeater {
model: menuOpener.children
CustomRect {
id: item
required property int index
required property QsMenuEntry modelData
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
implicitWidth: menu.biggestWidth
radius: Appearance.rounding.full
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
Loader {
id: children
active: !item.modelData.isSeparator
anchors.left: parent.left
anchors.right: parent.right
asynchronous: true
sourceComponent: Item {
implicitHeight: 30
StateLayer {
function onClicked(): void {
const entry = item.modelData;
if (entry.hasChildren) {
menu.popouts.pushSubmenu(menu.level, entry, item, menu.biggestWidth);
} else {
entry.triggered();
menu.popouts.hasCurrent = false;
}
}
disabled: !item.modelData.enabled
radius: item.radius
}
Loader {
id: icon
active: item.modelData.icon !== ""
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: Item {
implicitHeight: label.implicitHeight
implicitWidth: label.implicitHeight
IconImage {
id: iconImage
implicitSize: parent.implicitHeight
source: item.modelData.icon
visible: false
}
MultiEffect {
anchors.fill: iconImage
colorization: 1.0
colorizationColor: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
source: iconImage
}
}
}
CustomText {
id: label
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
text: labelMetrics.elidedText
}
TextMetrics {
id: labelMetrics
font.family: label.font.family
font.pointSize: label.font.pointSize
text: item.modelData.text
Component.onCompleted: {
var biggestWidth = menu.biggestWidth;
var currentWidth = labelMetrics.width + (item.modelData.icon ?? "" ? 30 : 0) + (item.modelData.hasChildren ? 30 : 0) + 20;
if (currentWidth > biggestWidth) {
menu.biggestWidth = currentWidth;
}
}
}
Loader {
id: expand
active: item.modelData.hasChildren
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: MaterialIcon {
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
text: "chevron_right"
}
}
}
}
}
}
}
+4 -239
View File
@@ -1,251 +1,16 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import Quickshell import Quickshell
import Quickshell.Widgets
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import qs.Components import qs.Components
import qs.Modules import qs.Modules
import qs.Config import qs.Config
StackView { SubMenu {
id: root id: root
property int biggestWidth: 0 handle: trayItem
required property PopoutState popouts level: 0
property int rootWidth: 0
required property QsMenuHandle trayItem required property QsMenuHandle trayItem
implicitHeight: currentItem.implicitHeight
implicitWidth: currentItem.implicitWidth
initialItem: SubMenu {
handle: root.trayItem
}
popEnter: NoAnim {
}
popExit: NoAnim {
}
pushEnter: NoAnim {
}
pushExit: NoAnim {
}
Component {
id: subMenuComp
SubMenu {
}
}
component NoAnim: Transition {
NumberAnimation {
duration: 0
}
}
component SubMenu: Column {
id: menu
required property QsMenuHandle handle
property bool isSubMenu
property bool shown
opacity: shown ? 1 : 0
padding: 0
scale: shown ? 1 : 0.8
spacing: 4
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Component.onCompleted: shown = true
StackView.onActivating: shown = true
StackView.onDeactivating: shown = false
StackView.onRemoved: destroy()
QsMenuOpener {
id: menuOpener
menu: menu.handle
}
Repeater {
model: menuOpener.children
CustomRect {
id: item
required property int index
required property QsMenuEntry modelData
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
implicitWidth: root.biggestWidth
radius: Appearance.rounding.full
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
Loader {
id: children
active: !item.modelData.isSeparator
anchors.left: parent.left
anchors.right: parent.right
asynchronous: true
sourceComponent: Item {
implicitHeight: 30
StateLayer {
function onClicked(): void {
const entry = item.modelData;
if (entry.hasChildren) {
root.rootWidth = root.biggestWidth;
root.biggestWidth = 0;
root.push(subMenuComp.createObject(null, {
handle: entry,
isSubMenu: true
}));
} else {
item.modelData.triggered();
root.popouts.hasCurrent = false;
}
}
disabled: !item.modelData.enabled
radius: item.radius
}
Loader {
id: icon
active: item.modelData.icon !== ""
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: Item {
implicitHeight: label.implicitHeight
implicitWidth: label.implicitHeight
IconImage {
id: iconImage
implicitSize: parent.implicitHeight
source: item.modelData.icon
visible: false
}
MultiEffect {
anchors.fill: iconImage
colorization: 1.0
colorizationColor: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
source: iconImage
}
}
}
CustomText {
id: label
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
text: labelMetrics.elidedText
}
TextMetrics {
id: labelMetrics
font.family: label.font.family
font.pointSize: label.font.pointSize
text: item.modelData.text
Component.onCompleted: {
var biggestWidth = root.biggestWidth;
var currentWidth = labelMetrics.width + (item.modelData.icon ?? "" ? 30 : 0) + (item.modelData.hasChildren ? 30 : 0) + 20;
if (currentWidth > biggestWidth) {
root.biggestWidth = currentWidth;
}
}
}
Loader {
id: expand
active: item.modelData.hasChildren
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
asynchronous: true
sourceComponent: MaterialIcon {
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
text: "chevron_right"
}
}
}
}
}
}
Loader {
id: loader
active: menu.isSubMenu
asynchronous: true
sourceComponent: Item {
implicitHeight: 30
implicitWidth: back.implicitWidth
Item {
anchors.bottom: parent.bottom
implicitHeight: 30
implicitWidth: root.biggestWidth
CustomRect {
anchors.fill: parent
color: DynamicColors.palette.m3secondaryContainer
radius: Appearance.rounding.full
StateLayer {
function onClicked(): void {
root.pop();
root.biggestWidth = root.rootWidth;
}
color: DynamicColors.palette.m3onSecondaryContainer
radius: parent.radius
}
}
Row {
id: back
anchors.verticalCenter: parent.verticalCenter
MaterialIcon {
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3onSecondaryContainer
text: "chevron_left"
}
CustomText {
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3onSecondaryContainer
text: qsTr("Back")
}
}
}
}
}
}
} }
+3 -1
View File
@@ -15,13 +15,14 @@ Item {
property real currentCenter property real currentCenter
property alias currentName: popoutState.currentName property alias currentName: popoutState.currentName
property string detachedMode property string detachedMode
readonly property bool isDetached: detachedMode.length > 0
property alias hasCurrent: popoutState.hasCurrent property alias hasCurrent: popoutState.hasCurrent
readonly property bool isDetached: detachedMode.length > 0
readonly property real nonAnimHeight: children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight readonly property real nonAnimHeight: children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight
readonly property real nonAnimWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth readonly property real nonAnimWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth
required property real offsetScale required property real offsetScale
property string queuedMode property string queuedMode
required property ShellScreen screen required property ShellScreen screen
property alias state: popoutState
function close(): void { function close(): void {
hasCurrent = false; hasCurrent = false;
@@ -79,6 +80,7 @@ Item {
sourceComponent: Content { sourceComponent: Content {
popouts: popoutState popouts: popoutState
screen: root.screen
} }
} }
@@ -9,14 +9,8 @@ _data = {
"variants": { "variants": {
"type": "multi", "type": "multi",
"defaults": { "defaults": {
"dark": { "dark": {"m3flavor": "mocha", "m3accent": "mauve"},
"m3flavor": "mocha", "light": {"m3flavor": "latte", "m3accent": "mauve"},
"m3accent": "mauve"
},
"light": {
"m3flavor": "latte",
"m3accent": "mauve"
}
}, },
"flavors": [ "flavors": [
{ {
@@ -35,8 +29,8 @@ _data = {
"m3surfaceContainerHighest": "#dce0e8", "m3surfaceContainerHighest": "#dce0e8",
"m3error": "#d20f39", "m3error": "#d20f39",
"m3warning": "#fe640b", "m3warning": "#fe640b",
"m3info": "#1e66f5" "m3info": "#1e66f5",
} },
}, },
{ {
"id": "frappe", "id": "frappe",
@@ -54,8 +48,8 @@ _data = {
"m3surfaceContainerHighest": "#232634", "m3surfaceContainerHighest": "#232634",
"m3error": "#e78284", "m3error": "#e78284",
"m3warning": "#ef9f76", "m3warning": "#ef9f76",
"m3info": "#8caaee" "m3info": "#8caaee",
} },
}, },
{ {
"id": "macchiato", "id": "macchiato",
@@ -73,8 +67,8 @@ _data = {
"m3surfaceContainerHighest": "#181926", "m3surfaceContainerHighest": "#181926",
"m3error": "#ed8796", "m3error": "#ed8796",
"m3warning": "#f5a97f", "m3warning": "#f5a97f",
"m3info": "#8aadf4" "m3info": "#8aadf4",
} },
}, },
{ {
"id": "mocha", "id": "mocha",
@@ -92,9 +86,9 @@ _data = {
"m3surfaceContainerHighest": "#11111b", "m3surfaceContainerHighest": "#11111b",
"m3error": "#f38ba8", "m3error": "#f38ba8",
"m3warning": "#fab387", "m3warning": "#fab387",
"m3info": "#89b4fa" "m3info": "#89b4fa",
} },
} },
], ],
"accents": [ "accents": [
{ {
@@ -105,29 +99,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#e1a99d", "m3primaryContainer": "#e1a99d",
"m3secondary": "#d8c7c4", "m3secondary": "#d8c7c4",
"m3surfaceTint": "#e1a99d" "m3surfaceTint": "#e1a99d",
}, },
"frappe": { "frappe": {
"m3primary": "#f2d5cf", "m3primary": "#f2d5cf",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#b8a5a6", "m3primaryContainer": "#b8a5a6",
"m3secondary": "#a2748b", "m3secondary": "#a2748b",
"m3surfaceTint": "#b8a5a6" "m3surfaceTint": "#b8a5a6",
}, },
"macchiato": { "macchiato": {
"m3primary": "#f4dbd6", "m3primary": "#f4dbd6",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#b6a6a7", "m3primaryContainer": "#b6a6a7",
"m3secondary": "#9f6f8d", "m3secondary": "#9f6f8d",
"m3surfaceTint": "#b6a6a7" "m3surfaceTint": "#b6a6a7",
}, },
"mocha": { "mocha": {
"m3primary": "#f5e0dc", "m3primary": "#f5e0dc",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b5a6a8", "m3primaryContainer": "#b5a6a8",
"m3secondary": "#9d6d87", "m3secondary": "#9d6d87",
"m3surfaceTint": "#b5a6a8" "m3surfaceTint": "#b5a6a8",
} },
}, },
{ {
"id": "flamingo", "id": "flamingo",
@@ -137,29 +131,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#e29c9d", "m3primaryContainer": "#e29c9d",
"m3secondary": "#d7c3c4", "m3secondary": "#d7c3c4",
"m3surfaceTint": "#e29c9d" "m3surfaceTint": "#e29c9d",
}, },
"frappe": { "frappe": {
"m3primary": "#eebebe", "m3primary": "#eebebe",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#b5949a", "m3primaryContainer": "#b5949a",
"m3secondary": "#9d6b80", "m3secondary": "#9d6b80",
"m3surfaceTint": "#b5949a" "m3surfaceTint": "#b5949a",
}, },
"macchiato": { "macchiato": {
"m3primary": "#f0c6c6", "m3primary": "#f0c6c6",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#b3979c", "m3primaryContainer": "#b3979c",
"m3secondary": "#996780", "m3secondary": "#996780",
"m3surfaceTint": "#b3979c" "m3surfaceTint": "#b3979c",
}, },
"mocha": { "mocha": {
"m3primary": "#f2cdcd", "m3primary": "#f2cdcd",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b3999e", "m3primaryContainer": "#b3999e",
"m3secondary": "#98667c", "m3secondary": "#98667c",
"m3surfaceTint": "#b3999e" "m3surfaceTint": "#b3999e",
} },
}, },
{ {
"id": "pink", "id": "pink",
@@ -169,29 +163,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#eb9bd7", "m3primaryContainer": "#eb9bd7",
"m3secondary": "#d9c7d5", "m3secondary": "#d9c7d5",
"m3surfaceTint": "#eb9bd7" "m3surfaceTint": "#eb9bd7",
}, },
"frappe": { "frappe": {
"m3primary": "#f4b8e4", "m3primary": "#f4b8e4",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#b990b5", "m3primaryContainer": "#b990b5",
"m3secondary": "#996e9e", "m3secondary": "#996e9e",
"m3surfaceTint": "#b990b5" "m3surfaceTint": "#b990b5",
}, },
"macchiato": { "macchiato": {
"m3primary": "#f5bde6", "m3primary": "#f5bde6",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#b791b2", "m3primaryContainer": "#b791b2",
"m3secondary": "#95689a", "m3secondary": "#95689a",
"m3surfaceTint": "#b791b2" "m3surfaceTint": "#b791b2",
}, },
"mocha": { "mocha": {
"m3primary": "#f5c2e7", "m3primary": "#f5c2e7",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b591b0", "m3primaryContainer": "#b591b0",
"m3secondary": "#966597", "m3secondary": "#966597",
"m3surfaceTint": "#b591b0" "m3surfaceTint": "#b591b0",
} },
}, },
{ {
"id": "mauve", "id": "mauve",
@@ -201,29 +195,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#a670f1", "m3primaryContainer": "#a670f1",
"m3secondary": "#c2b8d0", "m3secondary": "#c2b8d0",
"m3surfaceTint": "#a670f1" "m3surfaceTint": "#a670f1",
}, },
"frappe": { "frappe": {
"m3primary": "#ca9ee6", "m3primary": "#ca9ee6",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#9c7eb6", "m3primaryContainer": "#9c7eb6",
"m3secondary": "#7d6799", "m3secondary": "#7d6799",
"m3surfaceTint": "#9c7eb6" "m3surfaceTint": "#9c7eb6",
}, },
"macchiato": { "macchiato": {
"m3primary": "#c6a0f6", "m3primary": "#c6a0f6",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#967cbe", "m3primaryContainer": "#967cbe",
"m3secondary": "#766597", "m3secondary": "#766597",
"m3surfaceTint": "#967cbe" "m3surfaceTint": "#967cbe",
}, },
"mocha": { "mocha": {
"m3primary": "#cba6f7", "m3primary": "#cba6f7",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#977ebb", "m3primaryContainer": "#977ebb",
"m3secondary": "#756294", "m3secondary": "#756294",
"m3surfaceTint": "#977ebb" "m3surfaceTint": "#977ebb",
} },
}, },
{ {
"id": "red", "id": "red",
@@ -233,29 +227,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#da5371", "m3primaryContainer": "#da5371",
"m3secondary": "#c0a0a8", "m3secondary": "#c0a0a8",
"m3surfaceTint": "#da5371" "m3surfaceTint": "#da5371",
}, },
"frappe": { "frappe": {
"m3primary": "#e78284", "m3primary": "#e78284",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#b06a72", "m3primaryContainer": "#b06a72",
"m3secondary": "#8b5d66", "m3secondary": "#8b5d66",
"m3surfaceTint": "#b06a72" "m3surfaceTint": "#b06a72",
}, },
"macchiato": { "macchiato": {
"m3primary": "#ed8796", "m3primary": "#ed8796",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#b16b7a", "m3primaryContainer": "#b16b7a",
"m3secondary": "#865a69", "m3secondary": "#865a69",
"m3surfaceTint": "#b16b7a" "m3surfaceTint": "#b16b7a",
}, },
"mocha": { "mocha": {
"m3primary": "#f38ba8", "m3primary": "#f38ba8",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b46b84", "m3primaryContainer": "#b46b84",
"m3secondary": "#85596b", "m3secondary": "#85596b",
"m3surfaceTint": "#b46b84" "m3surfaceTint": "#b46b84",
} },
}, },
{ {
"id": "maroon", "id": "maroon",
@@ -265,29 +259,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#e87883", "m3primaryContainer": "#e87883",
"m3secondary": "#cfb7ba", "m3secondary": "#cfb7ba",
"m3surfaceTint": "#e87883" "m3surfaceTint": "#e87883",
}, },
"frappe": { "frappe": {
"m3primary": "#ea999c", "m3primary": "#ea999c",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#b27a83", "m3primaryContainer": "#b27a83",
"m3secondary": "#92626f", "m3secondary": "#92626f",
"m3surfaceTint": "#b27a83" "m3surfaceTint": "#b27a83",
}, },
"macchiato": { "macchiato": {
"m3primary": "#ee99a0", "m3primary": "#ee99a0",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#b27781", "m3primaryContainer": "#b27781",
"m3secondary": "#8c5e6c", "m3secondary": "#8c5e6c",
"m3surfaceTint": "#b27781" "m3surfaceTint": "#b27781",
}, },
"mocha": { "mocha": {
"m3primary": "#eba0ac", "m3primary": "#eba0ac",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#ae7987", "m3primaryContainer": "#ae7987",
"m3secondary": "#895b6c", "m3secondary": "#895b6c",
"m3surfaceTint": "#ae7987" "m3surfaceTint": "#ae7987",
} },
}, },
{ {
"id": "peach", "id": "peach",
@@ -297,29 +291,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#f98e51", "m3primaryContainer": "#f98e51",
"m3secondary": "#c9b7ad", "m3secondary": "#c9b7ad",
"m3surfaceTint": "#f98e51" "m3surfaceTint": "#f98e51",
}, },
"frappe": { "frappe": {
"m3primary": "#ef9f76", "m3primary": "#ef9f76",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#b67f68", "m3primaryContainer": "#b67f68",
"m3secondary": "#8f6a5f", "m3secondary": "#8f6a5f",
"m3surfaceTint": "#b67f68" "m3surfaceTint": "#b67f68",
}, },
"macchiato": { "macchiato": {
"m3primary": "#f5a97f", "m3primary": "#f5a97f",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#b7836a", "m3primaryContainer": "#b7836a",
"m3secondary": "#8c695e", "m3secondary": "#8c695e",
"m3surfaceTint": "#b7836a" "m3surfaceTint": "#b7836a",
}, },
"mocha": { "mocha": {
"m3primary": "#fab387", "m3primary": "#fab387",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b8876d", "m3primaryContainer": "#b8876d",
"m3secondary": "#8b6a5d", "m3secondary": "#8b6a5d",
"m3surfaceTint": "#b8876d" "m3surfaceTint": "#b8876d",
} },
}, },
{ {
"id": "yellow", "id": "yellow",
@@ -329,29 +323,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#e4ac5d", "m3primaryContainer": "#e4ac5d",
"m3secondary": "#c6baaa", "m3secondary": "#c6baaa",
"m3surfaceTint": "#e4ac5d" "m3surfaceTint": "#e4ac5d",
}, },
"frappe": { "frappe": {
"m3primary": "#e5c890", "m3primary": "#e5c890",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#af9b7a", "m3primaryContainer": "#af9b7a",
"m3secondary": "#948062", "m3secondary": "#948062",
"m3surfaceTint": "#af9b7a" "m3surfaceTint": "#af9b7a",
}, },
"macchiato": { "macchiato": {
"m3primary": "#eed49f", "m3primary": "#eed49f",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#b2a181", "m3primaryContainer": "#b2a181",
"m3secondary": "#947e62", "m3secondary": "#947e62",
"m3surfaceTint": "#b2a181" "m3surfaceTint": "#b2a181",
}, },
"mocha": { "mocha": {
"m3primary": "#f9e2af", "m3primary": "#f9e2af",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#b8a889", "m3primaryContainer": "#b8a889",
"m3secondary": "#978265", "m3secondary": "#978265",
"m3surfaceTint": "#b8a889" "m3surfaceTint": "#b8a889",
} },
}, },
{ {
"id": "green", "id": "green",
@@ -361,29 +355,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#74b867", "m3primaryContainer": "#74b867",
"m3secondary": "#9fbd9b", "m3secondary": "#9fbd9b",
"m3surfaceTint": "#74b867" "m3surfaceTint": "#74b867",
}, },
"frappe": { "frappe": {
"m3primary": "#a6d189", "m3primary": "#a6d189",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#83a275", "m3primaryContainer": "#83a275",
"m3secondary": "#648e5e", "m3secondary": "#648e5e",
"m3surfaceTint": "#83a275" "m3surfaceTint": "#83a275",
}, },
"macchiato": { "macchiato": {
"m3primary": "#a6da95", "m3primary": "#a6da95",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#80a57a", "m3primaryContainer": "#80a57a",
"m3secondary": "#5c8a61", "m3secondary": "#5c8a61",
"m3surfaceTint": "#80a57a" "m3surfaceTint": "#80a57a",
}, },
"mocha": { "mocha": {
"m3primary": "#a6e3a1", "m3primary": "#a6e3a1",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#7ea87f", "m3primaryContainer": "#7ea87f",
"m3secondary": "#5b8964", "m3secondary": "#5b8964",
"m3surfaceTint": "#7ea87f" "m3surfaceTint": "#7ea87f",
} },
}, },
{ {
"id": "teal", "id": "teal",
@@ -393,29 +387,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#57aeb4", "m3primaryContainer": "#57aeb4",
"m3secondary": "#93b4b7", "m3secondary": "#93b4b7",
"m3surfaceTint": "#57aeb4" "m3surfaceTint": "#57aeb4",
}, },
"frappe": { "frappe": {
"m3primary": "#81c8be", "m3primary": "#81c8be",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#699b9a", "m3primaryContainer": "#699b9a",
"m3secondary": "#588084", "m3secondary": "#588084",
"m3surfaceTint": "#699b9a" "m3surfaceTint": "#699b9a",
}, },
"macchiato": { "macchiato": {
"m3primary": "#8bd5ca", "m3primary": "#8bd5ca",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#6da29f", "m3primaryContainer": "#6da29f",
"m3secondary": "#577e83", "m3secondary": "#577e83",
"m3surfaceTint": "#6da29f" "m3surfaceTint": "#6da29f",
}, },
"mocha": { "mocha": {
"m3primary": "#94e2d5", "m3primary": "#94e2d5",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#71a8a4", "m3primaryContainer": "#71a8a4",
"m3secondary": "#588284", "m3secondary": "#588284",
"m3surfaceTint": "#71a8a4" "m3surfaceTint": "#71a8a4",
} },
}, },
{ {
"id": "sky", "id": "sky",
@@ -425,29 +419,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#4abcea", "m3primaryContainer": "#4abcea",
"m3secondary": "#a4b9c2", "m3secondary": "#a4b9c2",
"m3surfaceTint": "#4abcea" "m3surfaceTint": "#4abcea",
}, },
"frappe": { "frappe": {
"m3primary": "#99d1db", "m3primary": "#99d1db",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#79a2af", "m3primaryContainer": "#79a2af",
"m3secondary": "#628494", "m3secondary": "#628494",
"m3surfaceTint": "#79a2af" "m3surfaceTint": "#79a2af",
}, },
"macchiato": { "macchiato": {
"m3primary": "#91d7e3", "m3primary": "#91d7e3",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#71a3b0", "m3primaryContainer": "#71a3b0",
"m3secondary": "#5e7e8c", "m3secondary": "#5e7e8c",
"m3surfaceTint": "#71a3b0" "m3surfaceTint": "#71a3b0",
}, },
"mocha": { "mocha": {
"m3primary": "#89dceb", "m3primary": "#89dceb",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#69a3b3", "m3primaryContainer": "#69a3b3",
"m3secondary": "#5a7b88", "m3secondary": "#5a7b88",
"m3surfaceTint": "#69a3b3" "m3surfaceTint": "#69a3b3",
} },
}, },
{ {
"id": "sapphire", "id": "sapphire",
@@ -457,29 +451,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#5db8c8", "m3primaryContainer": "#5db8c8",
"m3secondary": "#9eb9be", "m3secondary": "#9eb9be",
"m3surfaceTint": "#5db8c8" "m3surfaceTint": "#5db8c8",
}, },
"frappe": { "frappe": {
"m3primary": "#85c1dc", "m3primary": "#85c1dc",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#6b96af", "m3primaryContainer": "#6b96af",
"m3secondary": "#5e7b8e", "m3secondary": "#5e7b8e",
"m3surfaceTint": "#6b96af" "m3surfaceTint": "#6b96af",
}, },
"macchiato": { "macchiato": {
"m3primary": "#7dc4e4", "m3primary": "#7dc4e4",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#6396b1", "m3primaryContainer": "#6396b1",
"m3secondary": "#5a7486", "m3secondary": "#5a7486",
"m3surfaceTint": "#6396b1" "m3surfaceTint": "#6396b1",
}, },
"mocha": { "mocha": {
"m3primary": "#74c7ec", "m3primary": "#74c7ec",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#5a95b4", "m3primaryContainer": "#5a95b4",
"m3secondary": "#567080", "m3secondary": "#567080",
"m3surfaceTint": "#5a95b4" "m3surfaceTint": "#5a95b4",
} },
}, },
{ {
"id": "blue", "id": "blue",
@@ -489,29 +483,29 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#5c90f5", "m3primaryContainer": "#5c90f5",
"m3secondary": "#b1bacb", "m3secondary": "#b1bacb",
"m3surfaceTint": "#5c90f5" "m3surfaceTint": "#5c90f5",
}, },
"frappe": { "frappe": {
"m3primary": "#8caaee", "m3primary": "#8caaee",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#7086bc", "m3primaryContainer": "#7086bc",
"m3secondary": "#637195", "m3secondary": "#637195",
"m3surfaceTint": "#7086bc" "m3surfaceTint": "#7086bc",
}, },
"macchiato": { "macchiato": {
"m3primary": "#8aadf4", "m3primary": "#8aadf4",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#6c85bc", "m3primaryContainer": "#6c85bc",
"m3secondary": "#5f6d8f", "m3secondary": "#5f6d8f",
"m3surfaceTint": "#6c85bc" "m3surfaceTint": "#6c85bc",
}, },
"mocha": { "mocha": {
"m3primary": "#89b4fa", "m3primary": "#89b4fa",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#6987bd", "m3primaryContainer": "#6987bd",
"m3secondary": "#5d6c8b", "m3secondary": "#5d6c8b",
"m3surfaceTint": "#6987bd" "m3surfaceTint": "#6987bd",
} },
}, },
{ {
"id": "lavender", "id": "lavender",
@@ -521,30 +515,30 @@ _data = {
"m3primaryText": "#eff1f5", "m3primaryText": "#eff1f5",
"m3primaryContainer": "#97a7fb", "m3primaryContainer": "#97a7fb",
"m3secondary": "#cdcfdd", "m3secondary": "#cdcfdd",
"m3surfaceTint": "#97a7fb" "m3surfaceTint": "#97a7fb",
}, },
"frappe": { "frappe": {
"m3primary": "#babbf1", "m3primary": "#babbf1",
"m3primaryText": "#303446", "m3primaryText": "#303446",
"m3primaryContainer": "#9192be", "m3primaryContainer": "#9192be",
"m3secondary": "#7175a1", "m3secondary": "#7175a1",
"m3surfaceTint": "#9192be" "m3surfaceTint": "#9192be",
}, },
"macchiato": { "macchiato": {
"m3primary": "#b7bdf8", "m3primary": "#b7bdf8",
"m3primaryText": "#24273a", "m3primaryText": "#24273a",
"m3primaryContainer": "#8b91bf", "m3primaryContainer": "#8b91bf",
"m3secondary": "#6b709d", "m3secondary": "#6b709d",
"m3surfaceTint": "#8b91bf" "m3surfaceTint": "#8b91bf",
}, },
"mocha": { "mocha": {
"m3primary": "#b4befe", "m3primary": "#b4befe",
"m3primaryText": "#1e1e2e", "m3primaryText": "#1e1e2e",
"m3primaryContainer": "#878ec0", "m3primaryContainer": "#878ec0",
"m3secondary": "#676d99", "m3secondary": "#676d99",
"m3surfaceTint": "#878ec0" "m3surfaceTint": "#878ec0",
} },
} },
] ],
} },
} }
+12 -10
View File
@@ -1,11 +1,13 @@
import json # import json
import typer # import typer
from zshell.assets.schemes.catppuccin import catppuccin # from zshell.assets.schemes.catppuccin import catppuccin
#
# app = typer.Typer()
#
# SCHEMES = catppuccin.variants.flavors
#
#
# @app.command()
# def set():
app = typer.Typer() # TODO: Currently unsused
SCHEMES = catppuccin.variants.flavors
@app.command()
def set():
+15 -13
View File
@@ -1,14 +1,16 @@
import typer # import typer
import subprocess # import subprocess
#
# from typing import Optional
#
# app = typer.Typer()
#
# RECORDER = "gpu-screen-recorder"
# HOME = str(os.getenv("HOME"))
# CONFIG = Path(HOME + "/.config/zshell/config.json")
#
#
# @app.command()
# def start():
from typing import Optional # TODO: Currently unused
app = typer.Typer()
RECORDER = "gpu-screen-recorder"
HOME = str(os.getenv("HOME"))
CONFIG = Path(HOME + "/.config/zshell/config.json")
@app.command()
def start():
+28 -32
View File
@@ -23,64 +23,71 @@ app = typer.Typer()
@app.command() @app.command()
def generate( def generate(
# image inputs (optional - used for image mode) # image inputs (optional - used for image mode)
image_path: Optional[Path] = typer.Option( image_path: Optional[Path] = typer.Option(None, help="Path to source image. Required for image mode."),
None, help="Path to source image. Required for image mode."),
scheme: Optional[str] = typer.Option( scheme: Optional[str] = typer.Option(
None, help="Color scheme algorithm to use for image mode. Ignored in preset mode."), None, help="Color scheme algorithm to use for image mode. Ignored in preset mode."
),
# preset inputs (optional - used for preset mode) # preset inputs (optional - used for preset mode)
preset: Optional[str] = typer.Option( preset: Optional[str] = typer.Option(
None, help="Name of a premade scheme in this format: <preset_name>:<preset_flavor>"), None, help="Name of a premade scheme in this format: <preset_name>:<preset_flavor>"
mode: Optional[str] = typer.Option( ),
None, help="Mode of the preset scheme (dark or light)."), mode: Optional[str] = typer.Option(None, help="Mode of the preset scheme (dark or light)."),
): ):
HOME = str(os.getenv("HOME")) HOME = str(os.getenv("HOME"))
OUTPUT = Path(HOME + "/.local/state/zshell/scheme.json") OUTPUT = Path(HOME + "/.local/state/zshell/scheme.json")
SEQ_STATE = Path(HOME + "/.local/state/zshell/sequences.txt") SEQ_STATE = Path(HOME + "/.local/state/zshell/sequences.txt")
THUMB_PATH = Path(HOME + THUMB_PATH = Path(HOME + "/.cache/zshell/imagecache/thumbnail.jpg")
"/.cache/zshell/imagecache/thumbnail.jpg") WALL_DIR_PATH = Path(HOME + "/.local/state/zshell/wallpaper_path.json")
WALL_DIR_PATH = Path(HOME +
"/.local/state/zshell/wallpaper_path.json")
TEMPLATE_DIR = Path(HOME + "/.config/zshell/templates") TEMPLATE_DIR = Path(HOME + "/.config/zshell/templates")
WALL_PATH = Path() WALL_PATH = Path()
CONFIG = Path(HOME + "/.config/zshell/config.json") CONFIG = Path(HOME + "/.config/zshell/config.json")
if preset is not None and image_path is not None: if preset is not None and image_path is not None:
raise typer.BadParameter( raise typer.BadParameter("Use either --image-path or --preset, not both.")
"Use either --image-path or --preset, not both.")
def get_scheme_class(scheme_name: str): def get_scheme_class(scheme_name: str):
match scheme_name: match scheme_name:
case "fruit-salad": case "fruit-salad":
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
return SchemeFruitSalad return SchemeFruitSalad
case "expressive": case "expressive":
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive from materialyoucolor.scheme.scheme_expressive import SchemeExpressive
return SchemeExpressive return SchemeExpressive
case "monochrome": case "monochrome":
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome
return SchemeMonochrome return SchemeMonochrome
case "rainbow": case "rainbow":
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow
return SchemeRainbow return SchemeRainbow
case "tonal-spot": case "tonal-spot":
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
return SchemeTonalSpot return SchemeTonalSpot
case "neutral": case "neutral":
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral from materialyoucolor.scheme.scheme_neutral import SchemeNeutral
return SchemeNeutral return SchemeNeutral
case "fidelity": case "fidelity":
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity
return SchemeFidelity return SchemeFidelity
case "content": case "content":
from materialyoucolor.scheme.scheme_content import SchemeContent from materialyoucolor.scheme.scheme_content import SchemeContent
return SchemeContent return SchemeContent
case "vibrant": case "vibrant":
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant
return SchemeVibrant return SchemeVibrant
case _: case _:
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
return SchemeFruitSalad return SchemeFruitSalad
def hex_to_hct(hex_color: str) -> Hct: def hex_to_hct(hex_color: str) -> Hct:
@@ -163,16 +170,11 @@ def generate(
def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct: def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct:
diff = difference_degrees(from_hct.hue, to_hct.hue) diff = difference_degrees(from_hct.hue, to_hct.hue)
rotation = min(diff * 0.8, 100) rotation = min(diff * 0.8, 100)
output_hue = sanitize_degrees_double( output_hue = sanitize_degrees_double(from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue))
from_hct.hue
+ rotation * rotation_direction(from_hct.hue, to_hct.hue)
)
tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost))) tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost)))
return Hct.from_hct(output_hue, from_hct.chroma, tone) return Hct.from_hct(output_hue, from_hct.chroma, tone)
def terminal_palette( def terminal_palette(colors: dict[str, str], mode: str, variant: str) -> dict[str, str]:
colors: dict[str, str], mode: str, variant: str
) -> dict[str, str]:
light = mode.lower() == "light" light = mode.lower() == "light"
key_hex = ( key_hex = (
@@ -307,7 +309,7 @@ def generate(
"seed": seed.to_int(), "seed": seed.to_int(),
"flavor": flavor, "flavor": flavor,
"variant": variant, "variant": variant,
"colors": colors "colors": colors,
} }
for k, v in colors.items(): for k, v in colors.items():
@@ -349,7 +351,7 @@ def generate(
def tmux_wrap_sequences(seq: str) -> str: def tmux_wrap_sequences(seq: str) -> str:
ESC = "\x1b" ESC = "\x1b"
return f"{ESC}Ptmux;{seq.replace(ESC, ESC+ESC)}{ESC}\\" return f"{ESC}Ptmux;{seq.replace(ESC, ESC + ESC)}{ESC}\\"
def parse_output_directive(first_line: str) -> Optional[Path]: def parse_output_directive(first_line: str) -> Optional[Path]:
s = first_line.strip() s = first_line.strip()
@@ -406,8 +408,7 @@ def generate(
template = env.from_string(body) template = env.from_string(body)
text = template.render(**context) text = template.render(**context)
except Exception as e: except Exception as e:
raise RuntimeError( raise RuntimeError(f"Template render failed for '{rel}': {e}") from e
f"Template render failed for '{rel}': {e}") from e
out_path.write_text(text, encoding="utf-8") out_path.write_text(text, encoding="utf-8")
@@ -435,18 +436,13 @@ def generate(
try: try:
return PRESETS[name].primary return PRESETS[name].primary
except KeyError: except KeyError:
raise typer.BadParameter( raise typer.BadParameter(f"Preset '{name}' not found. Available presets: {', '.join(PRESETS.keys())}")
f"Preset '{name}' not found. Available presets: {', '.join(PRESETS.keys())}")
def generate_color_scheme(seed: Hct, mode: str, scheme_class) -> dict[str, str]: def generate_color_scheme(seed: Hct, mode: str, scheme_class) -> dict[str, str]:
is_dark = mode.lower() == "dark" is_dark = mode.lower() == "dark"
scheme = scheme_class( scheme = scheme_class(seed, is_dark, 0.0)
seed,
is_dark,
0.0
)
color_dict = {} color_dict = {}
for color in vars(MaterialDynamicColors).keys(): for color in vars(MaterialDynamicColors).keys():
@@ -499,7 +495,7 @@ def generate(
"mode": effective_mode, "mode": effective_mode,
"variant": scheme, "variant": scheme,
"colors": colors, "colors": colors,
"seed": seed.to_int() "seed": seed.to_int(),
} }
if TEMPLATE_DIR is not None: if TEMPLATE_DIR is not None:
@@ -511,7 +507,7 @@ def generate(
wallpaper_path=wp, wallpaper_path=wp,
name=name, name=name,
flavor=flavor, flavor=flavor,
variant=scheme variant=scheme,
) )
rendered = render_all_templates( rendered = render_all_templates(
+2 -4
View File
@@ -8,11 +8,9 @@ app = typer.Typer()
@app.command() @app.command()
def start(): def start():
subprocess.run(args + ["ipc"] + ["call"] + subprocess.run(args + ["ipc"] + ["call"] + ["picker"] + ["open"], check=True)
["picker"] + ["open"], check=True)
@app.command() @app.command()
def start_freeze(): def start_freeze():
subprocess.run(args + ["ipc"] + ["call"] + subprocess.run(args + ["ipc"] + ["call"] + ["picker"] + ["openFreeze"], check=True)
["picker"] + ["openFreeze"], check=True)
+1 -2
View File
@@ -33,5 +33,4 @@ def lock():
@app.command() @app.command()
def call(target: str, method: str, method_args: list[str] = typer.Argument(None)): def call(target: str, method: str, method_args: list[str] = typer.Argument(None)):
subprocess.run(args + ["ipc"] + ["call"] + [target] + subprocess.run(args + ["ipc"] + ["call"] + [target] + [method] + method_args, check=True)
[method] + method_args, check=True)
+4 -5
View File
@@ -12,8 +12,7 @@ app = typer.Typer()
@app.command() @app.command()
def set(wallpaper: Path): def set(wallpaper: Path):
subprocess.run(args + ["ipc"] + ["call"] + subprocess.run(args + ["ipc"] + ["call"] + ["wallpaper"] + ["set"] + [wallpaper], check=True)
["wallpaper"] + ["set"] + [wallpaper], check=True)
@app.command() @app.command()
@@ -26,15 +25,15 @@ def lockscreen(
Path, Path,
typer.Option(), typer.Option(),
], ],
blur_amount: int = 20 blur_amount: int = 20,
): ):
img = Image.open(input_image) img = Image.open(input_image)
size = img.size size = img.size
if (blur_amount == 0): if blur_amount == 0:
img.save(output_path, "PNG") img.save(output_path, "PNG")
return return
if (size[0] < 3840 or size[1] < 2160): if size[0] < 3840 or size[1] < 2160:
img = img.resize((size[0] // 2, size[1] // 2), Image.NEAREST) img = img.resize((size[0] // 2, size[1] // 2), Image.NEAREST)
else: else:
img = img.resize((size[0] // 4, size[1] // 4), Image.NEAREST) img = img.resize((size[0] // 4, size[1] // 4), Image.NEAREST)
+7 -5
View File
@@ -1,14 +1,16 @@
export default [ export default [
{
ignores: ["scripts/fzf.js", "scripts/fuzzysort.js"],
},
{ {
files: ["**/*.{js,jsx,ts,tsx,mjs,cjs}"], files: ["**/*.{js,jsx,ts,tsx,mjs,cjs}"],
languageOptions: { languageOptions: {
ecmaVersion: 2021, ecmaVersion: 2021,
sourceType: "module" sourceType: "module",
}, },
linterOptions: { linterOptions: {
reportUnusedDisableDirectives: true reportUnusedDisableDirectives: true,
},
rules: {},
}, },
rules: {
}
}
]; ];
+16 -8
View File
@@ -39,8 +39,10 @@ pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage {
); );
let mut pixmap = rgba_image_to_pixmap(&img); let mut pixmap = rgba_image_to_pixmap(&img);
let mut dst_paint = PixmapPaint::default(); let dst_paint = PixmapPaint {
dst_paint.blend_mode = BlendMode::DestinationIn; blend_mode: BlendMode::DestinationIn,
..Default::default()
};
pixmap.draw_pixmap(0, 0, mask.as_ref(), &dst_paint, Transform::identity(), None); pixmap.draw_pixmap(0, 0, mask.as_ref(), &dst_paint, Transform::identity(), None);
pixmap_to_rgba_image(pixmap) pixmap_to_rgba_image(pixmap)
} }
@@ -69,8 +71,10 @@ pub fn apply_drop_shadow(
let shadow_x = (extra_left as f32 + offset_x) as i32; let shadow_x = (extra_left as f32 + offset_x) as i32;
let shadow_y = (extra_top as f32 + offset_y) as i32; let shadow_y = (extra_top as f32 + offset_y) as i32;
let mut sp = PixmapPaint::default(); let sp = PixmapPaint {
sp.blend_mode = BlendMode::Source; blend_mode: BlendMode::Source,
..Default::default()
};
shadow_pixmap.draw_pixmap( shadow_pixmap.draw_pixmap(
shadow_x, shadow_x,
shadow_y, shadow_y,
@@ -87,8 +91,10 @@ pub fn apply_drop_shadow(
let blurred_pixmap = rgba_image_to_pixmap(&blurred); let blurred_pixmap = rgba_image_to_pixmap(&blurred);
let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap"); let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap");
let mut p = PixmapPaint::default(); let p = PixmapPaint {
p.blend_mode = BlendMode::Source; blend_mode: BlendMode::Source,
..Default::default()
};
canvas.draw_pixmap( canvas.draw_pixmap(
0, 0,
0, 0,
@@ -98,8 +104,10 @@ pub fn apply_drop_shadow(
None, None,
); );
let mut p2 = PixmapPaint::default(); let p2 = PixmapPaint {
p2.blend_mode = BlendMode::SourceOver; blend_mode: BlendMode::SourceOver,
..Default::default()
};
canvas.draw_pixmap( canvas.draw_pixmap(
extra_left as i32, extra_left as i32,
extra_top as i32, extra_top as i32,
+1 -1
View File
@@ -1,7 +1,7 @@
mod config; mod config;
mod effects; mod effects;
use anyhow::{bail, Context, Result}; use anyhow::{Context, Result, bail};
use std::io::Write as _; use std::io::Write as _;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};