Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02606e5ab3 | ||
|
|
c7b0ed957a | ||
|
|
c011c76900 |
@@ -1,5 +1 @@
|
||||
*.svg
|
||||
Colors.qml
|
||||
modules/wlogout/scripts/helper.sh
|
||||
config/path/ConfigPath.qml
|
||||
config/path/WallpaperPath.qml
|
||||
@@ -1,38 +0,0 @@
|
||||
# HyprlandQuickshellGUI: a replacement for Waybar
|
||||
|
||||
Run install.sh to make the quickshell runable
|
||||
|
||||
## Colors
|
||||
### [Matugen](https://github.com/InioX/matugen)
|
||||
We use [matugen](https://github.com/InioX/matugen) for the colors. With these Templates for Some Applications we use: [maguten-config](https://git.honney333.work/honney/matugen-config)
|
||||
|
||||
## Wallpaper
|
||||
We use a custom directory by deafault to save all your Wallpapers:
|
||||
```
|
||||
$HOME/Pictures/Wallpapers/
|
||||
```
|
||||
### Compatible Wallpaper Software:
|
||||
- [hyprpaper](https://github.com/hyprwm/hyprpaper)
|
||||
- [swww](https://github.com/LGFae/swww)
|
||||
- [awww (comming)](https://codeberg.org/LGFae/awww)
|
||||
|
||||
### Recommended Software for some stuff:
|
||||
- [ImageMagick (resizing & blurring)](https://github.com/ImageMagick/ImageMagick)
|
||||
|
||||
## Other Software:
|
||||
### Logout:
|
||||
- [wlogout](https://github.com/ArtsyMacaw/wlogout)
|
||||
- [nmcli (Recommended for Wifi)](https://github.com/ushiboy/nmcli)
|
||||
|
||||
## TO Enable Sub Programs:
|
||||
Add the Lines to your ```~/.config/hypr/hyprland.conf``` keep in mind that you might need to change the Keybind.
|
||||
### Wallpaper Picker
|
||||
```bind = $mainMod SHIFT, W, global, quickshell:Wallpaper```
|
||||
### JLearner
|
||||
```bind = $mainMod SHIFT, J, global, quickshell:JLearner```
|
||||
|
||||
# TODO:
|
||||
|
||||
- Volume Contol
|
||||
- Notes
|
||||
- wofi
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 MiB |
@@ -1,57 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
|
||||
// ===== PRIMARY =====
|
||||
readonly property color primary: "#cbcb77"
|
||||
readonly property color primary_fg: "#323200"
|
||||
readonly property color primaryContainer: "#494900"
|
||||
readonly property color primaryContainer_fg: "#e7e790"
|
||||
|
||||
// ===== SECONDARY =====
|
||||
readonly property color secondary: "#cac8a5"
|
||||
readonly property color secondary_fg: "#323218"
|
||||
readonly property color secondaryContainer: "#48482d"
|
||||
readonly property color secondaryContainer_fg: "#e6e4bf"
|
||||
|
||||
// ===== TERTIARY =====
|
||||
readonly property color tertiary: "#a4d0bd"
|
||||
readonly property color tertiary_fg: "#0a372a"
|
||||
readonly property color tertiaryContainer: "#254e40"
|
||||
readonly property color tertiaryContainer_fg: "#bfecd9"
|
||||
|
||||
// ===== ERROR =====
|
||||
readonly property color error: "#ffb4ab"
|
||||
readonly property color error_fg: "#690005"
|
||||
readonly property color errorContainer: "#93000a"
|
||||
readonly property color errorContainer_fg: "#ffdad6"
|
||||
|
||||
// ===== BACKGROUND / SURFACE =====
|
||||
readonly property color background: "#14140c"
|
||||
readonly property color background_fg: "#e6e3d5"
|
||||
|
||||
readonly property color surface: "#14140c"
|
||||
readonly property color surface_fg: "#e6e3d5"
|
||||
|
||||
readonly property color surfaceVariant: "#48473a"
|
||||
readonly property color surfaceVariant_fg: "#c9c7b6"
|
||||
|
||||
// ===== SURFACE CONTAINERS =====
|
||||
readonly property color surfaceContainerLowest: "#0f0f07"
|
||||
readonly property color surfaceContainerLow: "#1c1c14"
|
||||
readonly property color surfaceContainer: "#202018"
|
||||
readonly property color surfaceContainerHigh: "#2b2a22"
|
||||
readonly property color surfaceContainerHighest: "#36352c"
|
||||
|
||||
// ===== OUTLINE / UTILITY =====
|
||||
readonly property color outline: "#939182"
|
||||
readonly property color outlineVariant: "#48473a"
|
||||
readonly property color shadowColor: "#000000"
|
||||
readonly property color scrimColor: "#000000"
|
||||
|
||||
// ===== INVERSE =====
|
||||
readonly property color inverseSurface: "#e6e3d5"
|
||||
readonly property color inverseSurface_fg: "#313128"
|
||||
readonly property color inversePrimary: "#616219"
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
|
||||
// ===== PRIMARY =====
|
||||
readonly property color primary: "#cbcb77"
|
||||
readonly property color primary_fg: "#323200"
|
||||
readonly property color primaryContainer: "#494900"
|
||||
readonly property color primaryContainer_fg: "#e7e790"
|
||||
|
||||
// ===== SECONDARY =====
|
||||
readonly property color secondary: "#cac8a5"
|
||||
readonly property color secondary_fg: "#323218"
|
||||
readonly property color secondaryContainer: "#48482d"
|
||||
readonly property color secondaryContainer_fg: "#e6e4bf"
|
||||
|
||||
// ===== TERTIARY =====
|
||||
readonly property color tertiary: "#a4d0bd"
|
||||
readonly property color tertiary_fg: "#0a372a"
|
||||
readonly property color tertiaryContainer: "#254e40"
|
||||
readonly property color tertiaryContainer_fg: "#bfecd9"
|
||||
|
||||
// ===== ERROR =====
|
||||
readonly property color error: "#ffb4ab"
|
||||
readonly property color error_fg: "#690005"
|
||||
readonly property color errorContainer: "#93000a"
|
||||
readonly property color errorContainer_fg: "#ffdad6"
|
||||
|
||||
// ===== BACKGROUND / SURFACE =====
|
||||
readonly property color background: "#14140c"
|
||||
readonly property color background_fg: "#e6e3d5"
|
||||
|
||||
readonly property color surface: "#14140c"
|
||||
readonly property color surface_fg: "#e6e3d5"
|
||||
|
||||
readonly property color surfaceVariant: "#48473a"
|
||||
readonly property color surfaceVariant_fg: "#c9c7b6"
|
||||
|
||||
// ===== SURFACE CONTAINERS =====
|
||||
readonly property color surfaceContainerLowest: "#0f0f07"
|
||||
readonly property color surfaceContainerLow: "#1c1c14"
|
||||
readonly property color surfaceContainer: "#202018"
|
||||
readonly property color surfaceContainerHigh: "#2b2a22"
|
||||
readonly property color surfaceContainerHighest: "#36352c"
|
||||
|
||||
// ===== OUTLINE / UTILITY =====
|
||||
readonly property color outline: "#939182"
|
||||
readonly property color outlineVariant: "#48473a"
|
||||
readonly property color shadowColor: "#000000"
|
||||
readonly property color scrimColor: "#000000"
|
||||
|
||||
// ===== INVERSE =====
|
||||
readonly property color inverseSurface: "#e6e3d5"
|
||||
readonly property color inverseSurface_fg: "#313128"
|
||||
readonly property color inversePrimary: "#616219"
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick 2.15
|
||||
import "./path"
|
||||
|
||||
QtObject {
|
||||
readonly property string configDir: ConfigPath.path
|
||||
readonly property string wallpaperDir: WallpaperPath.wallpaperDir
|
||||
|
||||
readonly property int small_spacing: 4
|
||||
readonly property int spacing: 8
|
||||
function small_spacing_fun(screenHeight) { return Math.floor(barHeight(screenHeight) / 16) }
|
||||
function spacing_fun(barH) { return Math.floor(barH / 8) }
|
||||
function big_spacing_fun(barH) { return Math.floor(barH / 6) }
|
||||
|
||||
function font_size(spaceHeight) { return Math.floor(spaceHeight * 0.75) }
|
||||
function font_size_small(spaceHeight) { return Math.floor(spaceHeight * 0.625) }
|
||||
function font_size_tiny(spaceHeight) { return Math.floor(spaceHeight * 0.4) }
|
||||
|
||||
readonly property int small_radius: 8
|
||||
readonly property int radius: 16
|
||||
function small_radius_fun(barHeight) { return Math.floor(barHeight / 8) }
|
||||
function radius_fun(barHeight) { return Math.floor(barHeight / 4) }
|
||||
readonly property int big_radius: 10
|
||||
readonly property int border_width: 2
|
||||
|
||||
readonly property string font: "CodeNewRoman Nerd Font"
|
||||
|
||||
function big_font_size(value) {
|
||||
return value * 0.6
|
||||
}
|
||||
|
||||
function medium_scaling(value) {
|
||||
return value * 0.4
|
||||
}
|
||||
|
||||
// Scale font/icon size relative to bar height
|
||||
function bigIconSize(barHeight) { return Math.floor(barHeight * 0.7) }
|
||||
function halfBigIconSize(barHeight) { return Math.floor(barHeight * 0.6) }
|
||||
function iconSize(barHeight) { return Math.floor(barHeight * 0.5) }
|
||||
function labelSize(barHeight) { return Math.floor(barHeight * 0.35) }
|
||||
function normalSpacing(screenHeight) { return Math.floor(barHeight(screenHeight) / 4) }
|
||||
function widgetSpacing(screenHeight) { return Math.floor(barHeight(screenHeight) / 8) }
|
||||
|
||||
function barHeight(screenHeight) {
|
||||
return Math.floor(screenHeight * barHeightRatio(screenHeight))
|
||||
}
|
||||
|
||||
// Resolution-aware bar height ratio
|
||||
function barHeightRatio(screenHeight) {
|
||||
return screenHeight >= 2160 ? 0.03 :
|
||||
screenHeight >= 1440 ? 0.035 :
|
||||
0.04
|
||||
}
|
||||
|
||||
// Tray
|
||||
readonly property int reload_interval: 10000
|
||||
readonly property int fast_reload_interval: 1000
|
||||
|
||||
|
||||
|
||||
function maxTextWidth(maxNum, texts) {
|
||||
let maxWidth = 0;
|
||||
|
||||
for (let i = 0; i < Math.min(maxNum, texts.length); i++)
|
||||
maxWidth = Math.max(maxWidth, texts[i].width);
|
||||
|
||||
return maxWidth;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
readonly property string path: "__CONFIG_DIR__"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
QtObject {
|
||||
property string wallpaperDir: "__CONFIG_DIR__"
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
singleton ConfigPath ConfigPath.qml
|
||||
singleton WallpaperPath WallpaperPath.qml
|
||||
@@ -1,2 +0,0 @@
|
||||
singleton Config Config.qml
|
||||
singleton Colors Colors.qml
|
||||
-205
@@ -1,205 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
|
||||
cp "$SCRIPT_DIR/config/Colors.qml.template" "$SCRIPT_DIR/config/Colors.qml"
|
||||
|
||||
cp "$SCRIPT_DIR/config/path/ConfigPath.qml.template" "$SCRIPT_DIR/config/path/ConfigPath.qml"
|
||||
sed -i "s|__CONFIG_DIR__|$SCRIPT_DIR|g" "$SCRIPT_DIR/config/path/ConfigPath.qml"
|
||||
|
||||
# ----------------------------
|
||||
# UI helpers
|
||||
# ----------------------------
|
||||
info() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
||||
ok() { echo -e "\033[1;32m[ OK ]\033[0m $*"; }
|
||||
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
|
||||
fail() { echo -e "\033[1;31m[FAIL]\033[0m $*"; }
|
||||
|
||||
download() {
|
||||
local url="$1"
|
||||
local out="$2"
|
||||
local name="${3:-$(basename "$out")}"
|
||||
|
||||
info "Downloading $name"
|
||||
|
||||
if curl -fL --progress-bar "$url" -o "$out"; then
|
||||
ok "Saved $name"
|
||||
else
|
||||
fail "Failed $name"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
transform_svg_white() {
|
||||
local url="$1"
|
||||
local out="$2"
|
||||
local name="${3:-$(basename "$out")}"
|
||||
|
||||
info "Fetching + whitening $name"
|
||||
|
||||
if curl -fsSL "$url" \
|
||||
| sed -E 's/#[0-9a-fA-F]{3,6}/#ffffff/g' \
|
||||
> "$out"; then
|
||||
ok "Generated $name"
|
||||
else
|
||||
fail "Failed $name"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------
|
||||
# Start
|
||||
# ----------------------------
|
||||
echo
|
||||
info "Preparing wlogout icons..."
|
||||
mkdir -p "$SCRIPT_DIR/modules/wlogout/icons"
|
||||
|
||||
# ----------------------------
|
||||
# Icons (wlogout)
|
||||
# ----------------------------
|
||||
transform_svg_white \
|
||||
"https://raw.githubusercontent.com/vinceliuice/Colloid-icon-theme/main/src/actions/symbolic/system-hibernate-symbolic.svg" \
|
||||
"$SCRIPT_DIR/modules/wlogout/icons/hibernate.svg" \
|
||||
"hibernate.svg"
|
||||
|
||||
transform_svg_white \
|
||||
"https://raw.githubusercontent.com/vinceliuice/Colloid-icon-theme/main/bold/actions/symbolic/system-lock-screen-symbolic.svg" \
|
||||
"$SCRIPT_DIR/modules/wlogout/icons/lock.svg" \
|
||||
"lock.svg"
|
||||
|
||||
transform_svg_white \
|
||||
"https://raw.githubusercontent.com/vinceliuice/Colloid-icon-theme/main/src/status/symbolic/logout-symbolic.svg" \
|
||||
"$SCRIPT_DIR/modules/wlogout/icons/logout.svg" \
|
||||
"logout.svg"
|
||||
|
||||
transform_svg_white \
|
||||
"https://raw.githubusercontent.com/vinceliuice/Colloid-icon-theme/main/src/actions/symbolic/system-reboot-symbolic.svg" \
|
||||
"$SCRIPT_DIR/modules/wlogout/icons/reboot.svg" \
|
||||
"reboot.svg"
|
||||
|
||||
transform_svg_white \
|
||||
"https://raw.githubusercontent.com/vinceliuice/Colloid-icon-theme/main/src/actions/symbolic/system-shutdown-symbolic.svg" \
|
||||
"$SCRIPT_DIR/modules/wlogout/icons/shutdown.svg" \
|
||||
"shutdown.svg"
|
||||
|
||||
transform_svg_white \
|
||||
"https://raw.githubusercontent.com/vinceliuice/Colloid-icon-theme/main/src/actions/24/system-suspend.svg" \
|
||||
"$SCRIPT_DIR/modules/wlogout/icons/suspend.svg" \
|
||||
"suspend.svg"
|
||||
|
||||
# ----------------------------
|
||||
# UI icons
|
||||
# ----------------------------
|
||||
echo
|
||||
info "Preparing global icons..."
|
||||
mkdir -p "$SCRIPT_DIR/modules/jl/assets"
|
||||
|
||||
download \
|
||||
"https://raw.githubusercontent.com/Tekh-ops/Garuda-Linux-Icons/refs/heads/master/actions/scalable/window-close.svg" \
|
||||
"$SCRIPT_DIR/modules/jl/assets/wrong.svg" \
|
||||
"wrong.svg"
|
||||
|
||||
download \
|
||||
"https://raw.githubusercontent.com/Tekh-ops/Garuda-Linux-Icons/refs/heads/master/actions/scalable/dialog-ok-apply.svg" \
|
||||
"$SCRIPT_DIR/modules/jl/assets/correct.svg" \
|
||||
"correct.svg"
|
||||
|
||||
# ----------------------------
|
||||
# Notification sounds
|
||||
# ----------------------------
|
||||
echo
|
||||
info "Preparing notification sounds..."
|
||||
mkdir -p "$SCRIPT_DIR/assets/sounds"
|
||||
|
||||
download \
|
||||
"https://raw.githubusercontent.com/deepin-community/sound-theme-freedesktop/master/stereo/dialog-information.oga" \
|
||||
"$SCRIPT_DIR/assets/sounds/notification-low.oga" \
|
||||
"notification-low.oga"
|
||||
|
||||
download \
|
||||
"https://raw.githubusercontent.com/deepin-community/sound-theme-freedesktop/master/stereo/dialog-warning.oga" \
|
||||
"$SCRIPT_DIR/assets/sounds/notification-normal.oga" \
|
||||
"notification-normal.oga"
|
||||
|
||||
download \
|
||||
"https://raw.githubusercontent.com/deepin-community/sound-theme-freedesktop/master/stereo/bell.oga" \
|
||||
"$SCRIPT_DIR/assets/sounds/notification-critical.oga" \
|
||||
"notification-critical.oga"
|
||||
|
||||
# ----------------------------
|
||||
# Nerd Font
|
||||
# ----------------------------
|
||||
echo
|
||||
info "Installing CodeNewRoman Nerd Font..."
|
||||
FONT_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/fonts"
|
||||
mkdir -p "$FONT_DIR"
|
||||
|
||||
if [ ! -f "$FONT_DIR/CodeNewRomanNerdFont-Regular.ttf" ]; then
|
||||
download \
|
||||
"https://github.com/ryanoasis/nerd-fonts/releases/latest/download/CodeNewRoman.tar.xz" \
|
||||
"/tmp/CodeNewRoman.tar.xz" \
|
||||
"CodeNewRoman Nerd Font"
|
||||
|
||||
tar -xf /tmp/CodeNewRoman.tar.xz -C "$FONT_DIR" --wildcards '*.ttf' 2>/dev/null || \
|
||||
tar -xf /tmp/CodeNewRoman.tar.xz -C "$FONT_DIR"
|
||||
rm -f /tmp/CodeNewRoman.tar.xz
|
||||
|
||||
if command -v fc-cache &>/dev/null; then
|
||||
fc-cache -fv "$FONT_DIR" &>/dev/null
|
||||
fi
|
||||
ok "CodeNewRoman Nerd Font installed"
|
||||
else
|
||||
ok "CodeNewRoman Nerd Font already installed"
|
||||
fi
|
||||
|
||||
# ----------------------------
|
||||
# Japanese Font (for JLearner)
|
||||
# ----------------------------
|
||||
echo
|
||||
info "Installing Noto Sans JP font..."
|
||||
|
||||
if [ ! -f "$FONT_DIR/NotoSansJP.ttf" ]; then
|
||||
download \
|
||||
"https://github.com/google/fonts/raw/main/ofl/notosansjp/NotoSansJP%5Bwght%5D.ttf" \
|
||||
"$FONT_DIR/NotoSansJP.ttf" \
|
||||
"Noto Sans JP"
|
||||
|
||||
ok "Noto Sans JP font installed"
|
||||
else
|
||||
ok "Noto Sans JP font already installed"
|
||||
fi
|
||||
|
||||
# ----------------------------
|
||||
# Font cache reload
|
||||
# ----------------------------
|
||||
echo
|
||||
info "Reloading font cache..."
|
||||
if command -v fc-cache &>/dev/null; then
|
||||
fc-cache -fv &>/dev/null
|
||||
ok "Font cache reloaded"
|
||||
else
|
||||
warn "fc-cache not found — font cache not updated"
|
||||
fi
|
||||
|
||||
# ----------------------------
|
||||
# Example Wallpaper
|
||||
# ----------------------------
|
||||
echo
|
||||
info "Preparing wallpaper..."
|
||||
|
||||
WALLPAPER_DIR=$(scripts/wallpaper_dir.sh)
|
||||
|
||||
mkdir -p "$WALLPAPER_DIR"
|
||||
|
||||
info "Copying example Wallpaper..."
|
||||
|
||||
cp "$SCRIPT_DIR/assets/example_background.png" "$WALLPAPER_DIR/japan_old_emperor_stream_with_bridge.png"
|
||||
|
||||
info "Creating Wallpaper Folder Config"
|
||||
|
||||
cp "$SCRIPT_DIR/config/path/WallpaperPath.qml.template" "$SCRIPT_DIR/config/path/WallpaperPath.qml"
|
||||
sed -i "s|__CONFIG_DIR__|$WALLPAPER_DIR|g" "$SCRIPT_DIR/config/path/WallpaperPath.qml"
|
||||
|
||||
echo
|
||||
ok "All assets prepared successfully."
|
||||
@@ -1,44 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
|
||||
Variants {
|
||||
id: root
|
||||
|
||||
// Automatically generates a trap for every connected monitor
|
||||
model: Quickshell.screens
|
||||
|
||||
delegate: PanelWindow {
|
||||
screen: modelData
|
||||
|
||||
// Force the panel window to take up the full screen real estate
|
||||
anchors.top: true
|
||||
anchors.bottom: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
|
||||
// Push it to the background layer below normal windows
|
||||
WlrLayershell.layer: WlrLayer.Background
|
||||
WlrLayershell.namespace: "transparent-focus-trap"
|
||||
|
||||
// Keep it transparent so your current wallpaper shows right through
|
||||
color: "transparent"
|
||||
|
||||
// The core trap mechanism
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
// Having an active hover area on the background layer forces the
|
||||
// Wayland compositor to shift cursor target focus to this surface
|
||||
// the exact millisecond your mouse exits a tiled/floating window.
|
||||
onEntered: {
|
||||
console.log("DEBUG: Mouse entered background (Focus Trap Active)");
|
||||
}
|
||||
|
||||
onExited: {
|
||||
console.log("DEBUG: Mouse left background (Focusing on Window)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-16
@@ -2,21 +2,20 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import "./widgets"
|
||||
import "../../config"
|
||||
import "../notifications"
|
||||
import "../../colors"
|
||||
|
||||
// TODO: interact with PyWal
|
||||
|
||||
PanelWindow {
|
||||
id: panel
|
||||
|
||||
required property var manager
|
||||
|
||||
color: "transparent"
|
||||
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
|
||||
implicitHeight: Math.floor(screen.height * Config.barHeightRatio(screen.height))
|
||||
implicitHeight: 40
|
||||
|
||||
exclusiveZone: height // this is so context menus don't clip into the bar
|
||||
|
||||
@@ -32,35 +31,36 @@ PanelWindow {
|
||||
id: bar
|
||||
anchors.fill: parent
|
||||
color: Qt.alpha(Colors.background, 0.5)
|
||||
radius: 0
|
||||
|
||||
Row { // bar widgets left
|
||||
id: widgetsLeft
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Config.big_spacing_fun(height)
|
||||
spacing: Config.big_spacing_fun(height)
|
||||
anchors.leftMargin: 16
|
||||
spacing: 8
|
||||
|
||||
MenuButton {}
|
||||
// TODO: Add functionallity (Maybe just rofi or own way)
|
||||
|
||||
Workspaces {
|
||||
thisMonitor: barThisMonitor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
WindowInfo {}
|
||||
|
||||
NotificationIndicator {manager: panel.manager}
|
||||
// TODO: Maybe add current Apps in workspace
|
||||
|
||||
}
|
||||
|
||||
Row { // bar widgets center
|
||||
id: widgetsCenter
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Config.big_spacing_fun(height)
|
||||
|
||||
Clock {}
|
||||
|
||||
// TODO: Add a way to see Date (maybe hover or click)
|
||||
|
||||
}
|
||||
|
||||
Row { // bar widgets right
|
||||
@@ -68,16 +68,17 @@ PanelWindow {
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
spacing: Config.big_spacing_fun(height)
|
||||
anchors.rightMargin: 16
|
||||
spacing: 8
|
||||
|
||||
// TODO: Add bluetooth, network, audio, power, maybe HW monitor
|
||||
Tray{}
|
||||
Battery{}
|
||||
Network {}
|
||||
IdleButton {}
|
||||
PowerButton {}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Somewhere add Tray
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,12 @@ import Quickshell
|
||||
|
||||
|
||||
Item {
|
||||
id: bar_root
|
||||
|
||||
required property var manager
|
||||
|
||||
id: root
|
||||
Repeater { // Bars
|
||||
model: Quickshell.screens
|
||||
delegate: Loader {
|
||||
active: true // loads object
|
||||
sourceComponent: Bar {screen: modelData; manager: root.manager} // creates instance of bar component
|
||||
sourceComponent: Bar {screen: modelData} // creates instance of bar component
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
ID="$1"
|
||||
|
||||
if [[ -z "$ID" ]]; then
|
||||
echo "Usage: $0 Number"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# =========================
|
||||
# ICON MAP (ADD YOUR TABLE HERE)
|
||||
# =========================
|
||||
# Command Helper: hyprctl clients -j | jq '.[] | select(.workspace.id == 1)'
|
||||
# Icons: https://www.nerdfonts.com/cheat-sheet
|
||||
declare -A MAP=(
|
||||
# Youtube
|
||||
["Grayjay"]=""
|
||||
# Browser
|
||||
["Ablaze Floorp"]=""
|
||||
["floorp"]=""
|
||||
["LibreWolf"]=""
|
||||
["midori"]=""
|
||||
["brave-browser"]=""
|
||||
["chromium"]=""
|
||||
# E-Mail
|
||||
["org.mozilla.Thunderbird"]=""
|
||||
# Terminals
|
||||
["kitty"]=""
|
||||
["Alacritty"]=""
|
||||
# Videoplayer
|
||||
["VLC media player"]=""
|
||||
["mpv"]=""
|
||||
["ffplay"]=""
|
||||
# Video editing
|
||||
["org.kde.kdenlive"]=""
|
||||
# Chats
|
||||
["discord"]=""
|
||||
["vesktop"]=""
|
||||
["Signal"]=""
|
||||
["xyz.chatboxapp.app"]=""
|
||||
# Coding
|
||||
["Visual Studio Code"]=""
|
||||
["VSCodium"]=""
|
||||
["jetbrains-idea"]=""
|
||||
["jetbrains-studio"]=""
|
||||
# Text Editor
|
||||
["org.xfce.mousepad"]=""
|
||||
# Password
|
||||
["org.keepassxc.KeePassXC"]=""
|
||||
["Yubico Authenticator"]=""
|
||||
# Cloud
|
||||
["com.nextcloud.desktopclient.nextcloud"]=""
|
||||
# App Store
|
||||
["Octopi"]=""
|
||||
# Games
|
||||
["steam*"]=""
|
||||
["org.prismlauncher.PrismLauncher"]=""
|
||||
["Minecraft*"]=""
|
||||
["game"]=""
|
||||
# Digital Arts
|
||||
["Blender"]=""
|
||||
["Krita"]=""
|
||||
# VM
|
||||
["virt-manager"]=""
|
||||
["VirtualBox Manager"]=""
|
||||
["VirtualBox Machine"]=""
|
||||
["remote-viewer"]=""
|
||||
# Audio
|
||||
["qpwgraph"]=""
|
||||
["Carla"]=""
|
||||
# Video
|
||||
["com.obsproject.Studio"]=""
|
||||
["hyprland-share-picker"]=""
|
||||
# Document Editor/Viewer
|
||||
["libreoffice-startcenter"]=""
|
||||
["libreoffice-base"]=""
|
||||
["libreoffice-calc"]=""
|
||||
["libreoffice-draw"]=""
|
||||
["libreoffice-impress"]=""
|
||||
["libreoffice-math"]=""
|
||||
["libreoffice-writer"]=""
|
||||
["com.github.flxzt.rnote"]=""
|
||||
["Xreader"]=""
|
||||
["com.github.xournalpp.xournalpp"]=""
|
||||
["qpdfview.local.qpdfview"]=""
|
||||
# Hardware
|
||||
["Mission Center"]=""
|
||||
["QDirStat"]=""
|
||||
["Disks"]=""
|
||||
# File Manager
|
||||
["nemo"]=""
|
||||
["org.gnome.Nautilus"]=""
|
||||
["org.kde.dolphin"]=""
|
||||
["xdg-desktop-portal-gtk"]=""
|
||||
# Settings
|
||||
["nwg-look"]="" # gtk-settings
|
||||
["qt5ct"]=""
|
||||
["qt6ct"]=""
|
||||
["sddm-conf"]=""
|
||||
["blueman-manager"]=""
|
||||
["nm-connection-editor"]=""
|
||||
# Calculator
|
||||
["Calculator"]=""
|
||||
# VPN
|
||||
["Proton VPN"]=""
|
||||
["com.cisco.secureclient.gui"]=""
|
||||
# Torrent
|
||||
["org.qbittorrent.qBittorrent"]=""
|
||||
# Hardware Design
|
||||
["Xilinx Unified 2022.2 Installer"]=""
|
||||
["Xilinx Information Center"]=""
|
||||
["Xilinx Unified 2022.2 Installer"]=""
|
||||
["vsim"]=""
|
||||
# BoxBuddy
|
||||
["io.github.dvlv.boxbuddyrs"]=""
|
||||
# Stream
|
||||
["Fladder"]=""
|
||||
["crunchyroll"]=""
|
||||
["netflix"]=""
|
||||
["primevideo"]=""
|
||||
# Image Viewer
|
||||
["viewnior"]=""
|
||||
# Image Editor
|
||||
["gimp"]=""
|
||||
["org.inkscape.Inkscape"]=""
|
||||
# CNC
|
||||
["LightBurn"]=""
|
||||
)
|
||||
|
||||
# =========================
|
||||
# COLLECT HYPRLAND CLIENTS
|
||||
# =========================
|
||||
|
||||
to_lower() {
|
||||
echo "${1,,}"
|
||||
}
|
||||
|
||||
match_map() {
|
||||
local input
|
||||
input="$(to_lower "$1")"
|
||||
|
||||
for key in "${!MAP[@]}"; do
|
||||
local pattern
|
||||
pattern="$(to_lower "$key")"
|
||||
|
||||
# wildcard + glob match
|
||||
if [[ "$input" == $pattern ]]; then
|
||||
echo "${MAP[$key]}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# APPS=$(hyprctl clients -j | jq -r ".[] | select(.workspace.id == $ID) | .initialTitle")
|
||||
|
||||
# =========================
|
||||
# COLLECT HYPRLAND CLIENTS
|
||||
# =========================
|
||||
|
||||
mapfile -t APPS < <(
|
||||
hyprctl clients -j |
|
||||
jq -r ".[] | select(.workspace.id == $ID) | .initialTitle"
|
||||
)
|
||||
|
||||
mapfile -t APPS_CLASS < <(
|
||||
hyprctl clients -j |
|
||||
jq -r ".[] | select(.workspace.id == $ID) | .initialClass"
|
||||
)
|
||||
|
||||
# =========================
|
||||
# MAP ICONS
|
||||
# =========================
|
||||
|
||||
for i in "${!APPS[@]}"; do
|
||||
icon=""
|
||||
|
||||
icon="$(match_map "${APPS[$i]}")"
|
||||
|
||||
if [[ -z "$icon" ]]; then
|
||||
icon="$(match_map "${APPS_CLASS[$i]}")"
|
||||
fi
|
||||
|
||||
if [[ -n "$icon" ]]; then
|
||||
APPS[$i]="$icon"
|
||||
else
|
||||
APPS[$i]="${APPS_CLASS[$i]}|${APPS[$i]}"
|
||||
fi
|
||||
done
|
||||
|
||||
# =========================
|
||||
# OUTPUT
|
||||
# =========================
|
||||
|
||||
RESULT="$(printf '%s' "${APPS[@]}")"
|
||||
# RESULT=${RESULT% } # remove trailing space
|
||||
echo "$RESULT"
|
||||
@@ -1,14 +0,0 @@
|
||||
if [ "$1" = "--status" ]; then
|
||||
if pgrep -x "hypridle" >/dev/null; then
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if pgrep -x "hypridle" >/dev/null; then
|
||||
killall hypridle
|
||||
else
|
||||
hypridle &
|
||||
fi
|
||||
@@ -21,7 +21,7 @@ if printf "%s\n" "${ITYPES[@]}" | grep -qx "Ethernet"; then
|
||||
echo " "
|
||||
elif printf "%s\n" "${ITYPES[@]}" | grep -qx "Wi-Fi"; then
|
||||
if [ -x "/usr/bin/nmcli" ]; then
|
||||
CONNECTION="$(nmcli -f DEVICE,ACTIVE,SSID,SIGNAL dev wifi | grep yes | awk '{print $NF}')"
|
||||
CONNECTION="$(nmcli -f DEVICE,ACTIVE,SSID,SIGNAL dev wifi | grep yes | awk '{print $4}')"
|
||||
if [[ -z "$CONNECTION" ]]; then
|
||||
echo " "
|
||||
elif (( CONNECTION > 80 )); then
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// Per-workspace icon cache: maps workspaceId -> array of icon strings
|
||||
property var workspaceIcons: ({})
|
||||
|
||||
function iconsForWorkspace(workspaceId) {
|
||||
return root.workspaceIcons[workspaceId] || []
|
||||
}
|
||||
|
||||
function matchIcon(name) {
|
||||
if (!name) return ""
|
||||
var lower = name.toLowerCase()
|
||||
if (iconMap[lower]) return iconMap[lower]
|
||||
for (var key in iconMap) {
|
||||
if (key.endsWith("*") && key.startsWith("*")) {
|
||||
var middle = key.slice(1, -1)
|
||||
if (lower.indexOf(middle) !== -1) return iconMap[key]
|
||||
} else if (key.endsWith("*")) {
|
||||
var prefix = key.slice(0, -1)
|
||||
if (lower.startsWith(prefix)) return iconMap[key]
|
||||
} else if (key.startsWith("*")) {
|
||||
var suffix = key.slice(1)
|
||||
if (lower.endsWith(suffix)) return iconMap[key]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Process {
|
||||
id: clientsProc
|
||||
command: ["hyprctl", "clients", "-j"]
|
||||
running: true
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
var data = JSON.parse(this.text.trim())
|
||||
var newIcons = {}
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var client = data[i]
|
||||
var wid = client.workspace.id
|
||||
var icon = root.matchIcon(client.initialTitle) || root.matchIcon(client.initialClass) || "\"" + client.initialTitle + "\""
|
||||
if (icon) {
|
||||
if (!newIcons[wid]) newIcons[wid] = []
|
||||
newIcons[wid].push(icon)
|
||||
}
|
||||
}
|
||||
root.workspaceIcons = newIcons
|
||||
} catch (e) {
|
||||
// hyprctl output was not valid JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: clientsProc.running = true
|
||||
}
|
||||
|
||||
readonly property var iconMap: ({
|
||||
// Youtube
|
||||
"grayjay": "",
|
||||
// Browser
|
||||
"ablaze floorp": "",
|
||||
"floorp": "",
|
||||
"librewolf": "",
|
||||
"midori": "",
|
||||
"brave-browser": "",
|
||||
"chromium": "",
|
||||
// E-Mail
|
||||
"*thunderbird": "",
|
||||
// Terminals
|
||||
"kitty": "",
|
||||
"alacritty": "",
|
||||
// Videoplayer
|
||||
"vlc media player": "",
|
||||
"mpv": "",
|
||||
"ffplay": "",
|
||||
// Video editing
|
||||
"org.kde.kdenlive": "",
|
||||
// Video
|
||||
"*obs*": "",
|
||||
"hyprland-share-picker": "",
|
||||
// Chats
|
||||
"discord": "",
|
||||
"vesktop": "",
|
||||
"signal": "",
|
||||
"xyz.chatboxapp.app": "",
|
||||
// Coding
|
||||
"visual studio code": "",
|
||||
"vscodium": "",
|
||||
"jetbrains-idea": "",
|
||||
"jetbrains-studio": "",
|
||||
// Text Editor
|
||||
"org.xfce.mousepad": "",
|
||||
// Password
|
||||
"org.keepassxc.keepassxc": "",
|
||||
"yubico authenticator": "",
|
||||
// Cloud
|
||||
"com.nextcloud*": "",
|
||||
// App Store
|
||||
"octopi": "",
|
||||
// Games
|
||||
"steam*": "",
|
||||
"org.prismlauncher*": "",
|
||||
"minecraft*": "",
|
||||
"game": "",
|
||||
"eden*": "",
|
||||
// Digital Arts
|
||||
"blender": "",
|
||||
// Image Editor
|
||||
"gimp": "",
|
||||
"*inkscape*": "",
|
||||
"krita": "",
|
||||
// VM
|
||||
"virt*": "",
|
||||
"remote-viewer": "",
|
||||
// Audio
|
||||
"*qpwgraph": "",
|
||||
"carla": "",
|
||||
"volume*": "",
|
||||
"pavucontrol": "",
|
||||
// Document Editor/Viewer
|
||||
"libreoffice-startcenter": "",
|
||||
"libreoffice-base": "",
|
||||
"libreoffice-calc": "",
|
||||
"libreoffice-draw": "",
|
||||
"libreoffice-impress": "",
|
||||
"libreoffice-math": "",
|
||||
"libreoffice-writer": "",
|
||||
"*rnote": "",
|
||||
"xreader": "",
|
||||
"*xournalpp*": "",
|
||||
"qpdfview*": "",
|
||||
// Hardware
|
||||
"mission center": "",
|
||||
"qdirstat": "",
|
||||
"disks": "",
|
||||
// File Manager
|
||||
"nemo": "",
|
||||
"org.gnome.nautilus": "",
|
||||
"org.kde.dolphin": "",
|
||||
"xdg-desktop-portal-gtk": "",
|
||||
// Settings
|
||||
"nwg-look": "",
|
||||
"qt5ct": "",
|
||||
"qt6ct": "",
|
||||
"sddm-conf": "",
|
||||
"org.gnome.Settings": "",
|
||||
"blueman*": "",
|
||||
"nm-connection-editor": "",
|
||||
"nm-applet": "",
|
||||
// Calculator
|
||||
"calculator": "",
|
||||
// VPN
|
||||
"*vpn": "",
|
||||
"*cisco*": "",
|
||||
// Torrent
|
||||
"*qbittorrent*": "",
|
||||
// Hardware Design
|
||||
"xilinx*": "",
|
||||
"vsim": "",
|
||||
"*ivado*": "",
|
||||
// BoxBuddy
|
||||
"*boxbuddy*": "",
|
||||
// Stream
|
||||
"fladder": "",
|
||||
"crunchyroll": "",
|
||||
"netflix": "",
|
||||
"primevideo": "",
|
||||
// Image Viewer
|
||||
"viewnior": "",
|
||||
// CNC
|
||||
"lightburn": "",
|
||||
"wofi": "",
|
||||
// Screenshare
|
||||
"moonlight": "ßß"
|
||||
})
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
singleton AppsState AppsState.qml
|
||||
@@ -1,85 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Item {
|
||||
property bool available: false
|
||||
property real percentage: 0
|
||||
property bool charging: false
|
||||
property real energy: 0
|
||||
property real voltage: 0
|
||||
property int charge_cycle: 0
|
||||
property real time_to_empty: 0
|
||||
property real energy_rate: 0
|
||||
|
||||
function parseUpower(line) {
|
||||
line = line.trim()
|
||||
if (line.startsWith("percentage:")) {
|
||||
percentage = parseInt(line.split(":")[1])
|
||||
// console.log("percentage =", percentage)
|
||||
}
|
||||
else if (line.startsWith("state:")) {
|
||||
charging = !line.includes("discharging")
|
||||
// console.log("charging =", charging)
|
||||
}
|
||||
else if (line.startsWith("energy:")) {
|
||||
energy = parseFloat(line.split(":")[1])
|
||||
// console.log("energy =", energy)
|
||||
}
|
||||
else if (line.startsWith("voltage:")) {
|
||||
voltage = parseFloat(line.split(":")[1])
|
||||
// console.log("voltage =", voltage)
|
||||
}
|
||||
else if (line.startsWith("energy-rate:")) {
|
||||
energy_rate = parseFloat(line.split(":")[1])
|
||||
// console.log("energy_rate =", energy_rate)
|
||||
}
|
||||
else if (line.startsWith("charge-cycles:")) {
|
||||
charge_cycle = parseInt(line.split(":")[1])
|
||||
// console.log("charge_cycle =", charge_cycle)
|
||||
}
|
||||
else if (line.startsWith("time to empty:")) {
|
||||
time_to_empty = parseFloat(line.split(":")[1])
|
||||
// console.log("time_to_empty =", time_to_empty)
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: upowerProc
|
||||
command: ["bash", "-c", "upower -i $(upower -e | grep '^/org/freedesktop/UPower/devices/battery')"]
|
||||
stdout: SplitParser {
|
||||
onRead: function(data) {
|
||||
parseUpower(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: batteryAvailable
|
||||
command: ["sh", "-c", "upower -e | grep -q '^/org/freedesktop/UPower/devices/battery' && echo '1' || echo '0'"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const hasBattery = this.text.trim() === "1"
|
||||
available = hasBattery
|
||||
if (hasBattery) {
|
||||
pollTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: pollTimer
|
||||
interval: 1000
|
||||
running: false
|
||||
repeat: true
|
||||
triggeredOnStart: false
|
||||
onTriggered: upowerProc.running = true
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
batteryAvailable.running = true
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
singleton BatteryState 1.0 BatteryState.qml
|
||||
@@ -1,54 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property string shortDate: Qt.formatDate(new Date(), "dd.MM.yyyy - ")
|
||||
property string fullDate: Qt.formatDate(new Date(), "dddd, MMMM d, yyyy")
|
||||
property string currentTime: Qt.formatTime(new Date(), "hh:mm:ss")
|
||||
property string uptime: "Uptime: ..."
|
||||
property bool expanded: false
|
||||
|
||||
property Timer timeTimer: Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.currentTime = Qt.formatTime(new Date(), "hh:mm:ss")
|
||||
}
|
||||
|
||||
property Timer dateTimer: Timer {
|
||||
interval: 60000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
root.shortDate = Qt.formatDate(new Date(), "dd.MM.yyyy - ")
|
||||
root.fullDate = Qt.formatDate(new Date(), "dddd, MMMM d, yyyy")
|
||||
}
|
||||
}
|
||||
|
||||
property Process sysInfoProcess: Process {
|
||||
command: ["sh", "-c", "echo $(cat /proc/uptime)"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
var uptimeSecs = parseFloat(text.trim().split(" ")[0])
|
||||
var days = Math.floor(uptimeSecs / 86400)
|
||||
var hours = Math.floor((uptimeSecs % 86400) / 3600)
|
||||
var mins = Math.floor((uptimeSecs % 3600) / 60)
|
||||
root.uptime = "Uptime: " + days + "d " + hours + "h " + mins + "m"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Timer sysInfoTimer: Timer {
|
||||
interval: 10000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.sysInfoProcess.running = true
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
singleton ClockState ClockState.qml
|
||||
@@ -1,28 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../../../../config"
|
||||
|
||||
Item {
|
||||
property int idle: 0
|
||||
|
||||
Process {
|
||||
id: statusScript
|
||||
command: ["sh", "-c", Config.configDir + "/modules/bar/scripts/Idle.sh --status"]
|
||||
running: true
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
idle = this.text.trim() === "0" ? 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 5000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: statusScript.running = true
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
singleton IdleState 1.0 IdleState.qml
|
||||
@@ -1,230 +0,0 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../../../../config"
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property string status: "..."
|
||||
|
||||
property string interfaceName: ""
|
||||
property string interfaceType: ""
|
||||
property string connectionName: ""
|
||||
property string ipv4: ""
|
||||
property string wifiName: ""
|
||||
property string signalStrength: ""
|
||||
property string wifiChannel: ""
|
||||
property string wifiFrequency: ""
|
||||
property string linkSpeed: ""
|
||||
property string downloadSpeed: ""
|
||||
property string uploadSpeed: ""
|
||||
property string ping: ""
|
||||
property bool internetAvailable: false
|
||||
property bool vpnConnected: false
|
||||
property string vpnName: ""
|
||||
property string vpnIp: ""
|
||||
property bool tailscaleConnected: false
|
||||
property string tailscaleIp: ""
|
||||
|
||||
property bool running: false
|
||||
|
||||
property string _activeDevice: ""
|
||||
property real _prevRxBytes: 0
|
||||
property real _prevTxBytes: 0
|
||||
property var _prevUpdateTime: 0
|
||||
|
||||
property Timer networkTimer: Timer {
|
||||
interval: 10000
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: networkScript.running = true
|
||||
}
|
||||
|
||||
property Process networkScript: Process {
|
||||
command: ["sh", "-c",
|
||||
"echo '===SHOW==='; " +
|
||||
"ACTIVE=$(nmcli -t device status 2>/dev/null | awk -F: '$3==\"connected\"{print $1; exit}'); " +
|
||||
"echo \"ACTIVE_DEVICE=$ACTIVE\"; " +
|
||||
"if [ -n \"$ACTIVE\" ]; then " +
|
||||
" nmcli -t -f ALL device show \"$ACTIVE\" 2>/dev/null; " +
|
||||
"fi; " +
|
||||
"echo '===TRAFFIC==='; " +
|
||||
"if [ -n \"$ACTIVE\" ]; then " +
|
||||
" echo \"RX_BYTES=$(cat /sys/class/net/$ACTIVE/statistics/rx_bytes 2>/dev/null || echo 0)\"; " +
|
||||
" echo \"TX_BYTES=$(cat /sys/class/net/$ACTIVE/statistics/tx_bytes 2>/dev/null || echo 0)\"; " +
|
||||
"fi; " +
|
||||
"echo '===WIFI==='; " +
|
||||
"nmcli -t -f active,ssid,signal,chan,freq,rate dev wifi | awk -F: '$1==\"yes\" { $1=\"\"; sub(/^:/,\"\"); print }'; " +
|
||||
"echo '===PING==='; " +
|
||||
"ping -c 1 -W 2 1.1.1.1 2>/dev/null | awk -F/ '/rtt/{print $5}' || echo ''; " +
|
||||
"echo '===INTERNET==='; " +
|
||||
"ping -c 1 -W 1 1.1.1.1 2>/dev/null >/dev/null && echo 1 || echo 0; " +
|
||||
"echo '===VPN==='; " +
|
||||
"nmcli -t -f NAME,TYPE connection show --active 2>/dev/null | grep -i vpn || true; " +
|
||||
"echo '===TAILSCALE==='; " +
|
||||
"TAIL_IP=$(ip -4 addr show tailscale0 2>/dev/null | grep -oP 'inet \\K[\\d.]+'); " +
|
||||
"if [ -n \"$TAIL_IP\" ]; then echo \"TAILSCALE_IP=$TAIL_IP\"; else echo \"\"; fi"
|
||||
]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: root.parseNetworkOutput(text.trim())
|
||||
}
|
||||
}
|
||||
|
||||
function formatSpeed(bytesPerSec) {
|
||||
if (bytesPerSec >= 1073741824) return (bytesPerSec / 1073741824).toFixed(1) + " GB/s"
|
||||
if (bytesPerSec >= 1048576) return (bytesPerSec / 1048576).toFixed(1) + " MB/s"
|
||||
if (bytesPerSec >= 1024) return (bytesPerSec / 1024).toFixed(1) + " KB/s"
|
||||
return bytesPerSec.toFixed(0) + " B/s"
|
||||
}
|
||||
|
||||
function parseNetworkOutput(output) {
|
||||
var sections = output.split("===TAILSCALE===")
|
||||
var tailscaleSection = sections.length > 1 ? sections[1].trim() : ""
|
||||
|
||||
var beforeTailscale = sections[0]
|
||||
sections = beforeTailscale.split("===VPN===")
|
||||
var vpnSection = sections.length > 1 ? sections[1].trim() : ""
|
||||
|
||||
var beforeVpn = sections[0]
|
||||
sections = beforeVpn.split("===INTERNET===")
|
||||
root.internetAvailable = sections.length > 1 ? sections[1].trim() === "1" : false
|
||||
|
||||
var beforeInternet = sections[0]
|
||||
sections = beforeInternet.split("===PING===")
|
||||
root.ping = sections.length > 1 ? sections[1].trim() : ""
|
||||
|
||||
var beforePing = sections[0]
|
||||
sections = beforePing.split("===WIFI===")
|
||||
var wifiSection = sections.length > 1 ? sections[1].trim() : ""
|
||||
|
||||
var beforeWifi = sections[0]
|
||||
sections = beforeWifi.split("===TRAFFIC===")
|
||||
var trafficSection = sections.length > 1 ? sections[1].trim() : ""
|
||||
|
||||
var beforeTraffic = sections[0]
|
||||
sections = beforeTraffic.split("===SHOW===")
|
||||
var showSection = sections.length > 1 ? sections[1].trim() : ""
|
||||
|
||||
// Parse SHOW section
|
||||
var activeDevice = ""
|
||||
var lines = showSection.split("\n")
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim()
|
||||
if (line.indexOf("ACTIVE_DEVICE=") === 0) {
|
||||
activeDevice = line.substring("ACTIVE_DEVICE=".length)
|
||||
} else if (line.indexOf("GENERAL.DEVICE:") === 0) {
|
||||
root.interfaceName = line.substring("GENERAL.DEVICE:".length)
|
||||
} else if (line.indexOf("GENERAL.TYPE:") === 0) {
|
||||
root.interfaceType = line.substring("GENERAL.TYPE:".length)
|
||||
} else if (line.indexOf("GENERAL.CONNECTION:") === 0) {
|
||||
root.connectionName = line.substring("GENERAL.CONNECTION:".length)
|
||||
} else if (line.indexOf("IP4.ADDRESS") >= 0) {
|
||||
var ipPart = line.substring(line.indexOf(":") + 1)
|
||||
root.ipv4 = ipPart.split("/")[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Parse TRAFFIC section
|
||||
if (trafficSection) {
|
||||
var trafficLines = trafficSection.split("\n")
|
||||
var rxBytes = 0
|
||||
var txBytes = 0
|
||||
for (var t = 0; t < trafficLines.length; t++) {
|
||||
var tline = trafficLines[t].trim()
|
||||
if (tline.indexOf("RX_BYTES=") === 0) rxBytes = parseFloat(tline.substring("RX_BYTES=".length))
|
||||
else if (tline.indexOf("TX_BYTES=") === 0) txBytes = parseFloat(tline.substring("TX_BYTES=".length))
|
||||
}
|
||||
|
||||
// console.log("[Network] rxBytes:", rxBytes, "txBytes:", txBytes)
|
||||
// console.log("[Network] activeDevice:", activeDevice, "| prevActiveDevice:", root._activeDevice)
|
||||
// console.log("[Network] prevRxBytes:", root._prevRxBytes, "prevTxBytes:", root._prevTxBytes, "prevTime:", root._prevUpdateTime)
|
||||
|
||||
if (activeDevice === root._activeDevice && root._prevRxBytes > 0 && root._prevUpdateTime > 0) {
|
||||
var now = Date.now()
|
||||
var interval = (now - root._prevUpdateTime) / 1000
|
||||
// console.log("[Network] Same device, interval:", interval, "s")
|
||||
// console.log("[Network] rxDelta:", (rxBytes - root._prevRxBytes), "txDelta:", (txBytes - root._prevTxBytes))
|
||||
if (interval > 0) {
|
||||
var downBps = (rxBytes - root._prevRxBytes) / interval
|
||||
var upBps = (txBytes - root._prevTxBytes) / interval
|
||||
// console.log("[Network] downBps:", downBps, "upBps:", upBps)
|
||||
root.downloadSpeed = root.formatSpeed(downBps)
|
||||
root.uploadSpeed = root.formatSpeed(upBps)
|
||||
// console.log("[Network] downloadSpeed:", root.downloadSpeed, "uploadSpeed:", root.uploadSpeed)
|
||||
} else {
|
||||
console.log("[Network] Interval zero or negative, skipping")
|
||||
}
|
||||
} else {
|
||||
// console.log("[Network] First run or device changed, storing baseline")
|
||||
root.downloadSpeed = ""
|
||||
root.uploadSpeed = ""
|
||||
}
|
||||
|
||||
root._activeDevice = activeDevice
|
||||
root._prevRxBytes = rxBytes
|
||||
root._prevTxBytes = txBytes
|
||||
root._prevUpdateTime = Date.now()
|
||||
// console.log("[Network] Stored baseline: rxBytes:", root._prevRxBytes, "txBytes:", root._prevTxBytes)
|
||||
}
|
||||
|
||||
// Parse WIFI section
|
||||
if (wifiSection) {
|
||||
var wifiParts = wifiSection.split(" ")
|
||||
if (wifiParts.length >= 2) {
|
||||
root.wifiName = wifiParts[0] || ""
|
||||
root.signalStrength = wifiParts[1] || ""
|
||||
if (wifiParts.length >= 3) root.wifiChannel = wifiParts[2] || ""
|
||||
if (wifiParts.length >= 4) root.wifiFrequency = wifiParts[3] + " " + wifiParts[4] || ""
|
||||
if (wifiParts.length >= 5) root.linkSpeed = wifiParts[5] + " " + wifiParts[6] || ""
|
||||
}
|
||||
}
|
||||
|
||||
// Determine status icon
|
||||
if (root.interfaceType === "ethernet") {
|
||||
root.status = ""
|
||||
} else if (root.interfaceType === "wifi") {
|
||||
if (!root.connectionName) {
|
||||
root.status = ""
|
||||
} else {
|
||||
var sig = parseInt(root.signalStrength)
|
||||
if (sig > 80) root.status = ""
|
||||
else if (sig > 60) root.status = ""
|
||||
else if (sig > 40) root.status = ""
|
||||
else if (sig > 20) root.status = ""
|
||||
else root.status = ""
|
||||
}
|
||||
} else if (!activeDevice) {
|
||||
root.status = "/"
|
||||
}
|
||||
|
||||
// Parse VPN section
|
||||
if (vpnSection) {
|
||||
root.vpnConnected = true
|
||||
var vpnLines = vpnSection.split("\n")
|
||||
if (vpnLines.length > 0) {
|
||||
var vpnParts = vpnLines[0].split(":")
|
||||
root.vpnName = vpnParts[0] || ""
|
||||
}
|
||||
} else {
|
||||
root.vpnConnected = false
|
||||
root.vpnName = ""
|
||||
root.vpnIp = ""
|
||||
}
|
||||
|
||||
// Parse TAILSCALE section
|
||||
if (tailscaleSection && tailscaleSection.indexOf("TAILSCALE_IP=") === 0) {
|
||||
root.tailscaleConnected = true
|
||||
root.tailscaleIp = tailscaleSection.substring("TAILSCALE_IP=".length)
|
||||
} else {
|
||||
root.tailscaleConnected = false
|
||||
root.tailscaleIp = ""
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
networkScript.running = true
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
singleton NetworkState NetworkState.qml
|
||||
@@ -1,85 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "../../../config"
|
||||
|
||||
PopupWindow {
|
||||
id: popup
|
||||
|
||||
property Item anchorItem
|
||||
property bool anchorHovered: false
|
||||
property int minpopupWidth: 150
|
||||
property int minpopupHeight: 50
|
||||
property int popupVerticalOffset: 0
|
||||
property int spacingValue: 12
|
||||
property int radiusValue: 12
|
||||
|
||||
default property alias content: contentColumn.data
|
||||
|
||||
visible: false
|
||||
color: "transparent"
|
||||
implicitWidth: Math.max(popup.minpopupWidth, contentColumn.implicitWidth + 2 * popup.spacingValue)
|
||||
implicitHeight: Math.max(popup.minpopupHeight, contentColumn.implicitHeight + 2 * popup.spacingValue)
|
||||
|
||||
onAnchorHoveredChanged: popup.updateVisibility()
|
||||
|
||||
function updateVisibility() {
|
||||
if (anchorHovered || mouseArea.containsMouse) {
|
||||
closeTimer.stop()
|
||||
visible = true
|
||||
} else {
|
||||
closeTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: closeTimer
|
||||
interval: 1
|
||||
onTriggered: {
|
||||
if (!anchorHovered && !mouseArea.containsMouse) {
|
||||
visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchor {
|
||||
item: anchorItem
|
||||
edges: Edges.Top | Edges.Left
|
||||
gravity: Edges.Bottom | Edges.Right
|
||||
adjustment: PopupAdjustment.FlipY | PopupAdjustment.SlideX
|
||||
onAnchoring: {
|
||||
anchor.rect.x = (anchorItem.width - popup.width) / 2
|
||||
anchor.rect.y = anchorItem.height + popupVerticalOffset
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onContainsMouseChanged: popup.updateVisibility()
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
color: Qt.alpha(Colors.primaryContainer, 0.9)
|
||||
radius: popup.radiusValue
|
||||
border {
|
||||
color: Colors.primaryContainer_fg
|
||||
width: Config.border_width
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
width: implicitWidth
|
||||
height: implicitHeight
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: popup.spacingValue
|
||||
}
|
||||
spacing: popup.spacingValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../../config"
|
||||
import "../states/Battery"
|
||||
|
||||
MouseArea {
|
||||
id: batteryButton
|
||||
width: expanded ? Math.max(expandedRow.width + Config.spacing_fun(height)*3, bar.height) : Math.max(batteryText.width + Config.spacing_fun(height)*2, bar.height)
|
||||
height: bar.height
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
visible: BatteryState.available
|
||||
|
||||
property bool expanded: false
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: batteryButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.tertiaryContainer, 0.25)
|
||||
radius: Config.radius_fun(batteryButton.height)
|
||||
border.width: Config.border_width
|
||||
border.color: batteryButton.containsMouse ? Colors.tertiaryContainer_fg
|
||||
: Colors.secondaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: batteryText
|
||||
visible : !expanded
|
||||
anchors.centerIn: parent
|
||||
text:(
|
||||
BatteryState.percentage >= 90 ? "" :
|
||||
BatteryState.percentage >= 60 ? "" :
|
||||
BatteryState.percentage >= 30 ? "" :
|
||||
BatteryState.percentage >= 10 ? "" :
|
||||
""
|
||||
)
|
||||
font.pixelSize: Config.font_size(batteryButton.height)
|
||||
font.family: Config.font
|
||||
color: (
|
||||
BatteryState.charging ? (
|
||||
BatteryState.percentage == 100 ? Colors.secondary :
|
||||
Colors.tertiary
|
||||
) :
|
||||
BatteryState.percentage <= 5 ? Colors.errorContainer :
|
||||
BatteryState.percentage < 10 ? "yellow" :
|
||||
batteryButton.containsMouse ? Colors.tertiary_fg :
|
||||
Colors.primary
|
||||
)
|
||||
}
|
||||
|
||||
Text {
|
||||
id: chargeIcon
|
||||
visible : !expanded && BatteryState.charging
|
||||
anchors.centerIn: parent
|
||||
text: ""
|
||||
font.pixelSize: Config.iconSize(batteryButton.height)
|
||||
font.family: Config.font
|
||||
style: Text.Outline
|
||||
styleColor: Qt.alpha(Colors.tertiaryContainer, 0.5)
|
||||
color: Colors.tertiary
|
||||
}
|
||||
|
||||
Row {
|
||||
id: expandedRow
|
||||
visible: expanded
|
||||
anchors.centerIn: parent
|
||||
spacing: Config.spacing_fun(batteryButton.height)
|
||||
|
||||
Text {
|
||||
id: batteryPercentageSymbol
|
||||
text: (
|
||||
BatteryState.charging ? "" :
|
||||
BatteryState.percentage >= 90 ? "":
|
||||
BatteryState.percentage >= 80 ? "":
|
||||
BatteryState.percentage >= 70 ? "":
|
||||
BatteryState.percentage >= 60 ? "":
|
||||
BatteryState.percentage >= 50 ? "":
|
||||
BatteryState.percentage >= 40 ? "":
|
||||
BatteryState.percentage >= 30 ? "":
|
||||
BatteryState.percentage >= 20 ? "":
|
||||
BatteryState.percentage >= 10 ? "":
|
||||
BatteryState.percentage >= 5 ? "":
|
||||
""
|
||||
)
|
||||
|
||||
font.pixelSize: Config.labelSize(batteryButton.height)
|
||||
font.family: Config.font
|
||||
font.bold: true
|
||||
|
||||
color: (
|
||||
BatteryState.percentage <= 5 ? Colors.errorContainer :
|
||||
BatteryState.charging ? (
|
||||
BatteryState.percentage == 100 ? Colors.secondary :
|
||||
Colors.tertiary
|
||||
) :
|
||||
BatteryState.percentage < 10 ? "yellow" :
|
||||
batteryButton.containsMouse ? Colors.tertiary_fg :
|
||||
Colors.primary
|
||||
)
|
||||
}
|
||||
|
||||
Text {
|
||||
id: batteryPercentageText
|
||||
text: BatteryState.percentage + "%"
|
||||
|
||||
font.pixelSize: Config.labelSize(batteryButton.height)
|
||||
font.family: Config.font
|
||||
font.bold: true
|
||||
|
||||
color: batteryPercentageSymbol.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
expanded = !expanded
|
||||
console.log("[Battery] Expanded: " + expanded + ", percentage: " + BatteryState.percentage + "%, charging: " + BatteryState.charging)
|
||||
}
|
||||
|
||||
|
||||
BarPopup {
|
||||
id: batteryPopup
|
||||
anchorItem: batteryButton
|
||||
anchorHovered: batteryButton.containsMouse
|
||||
spacingValue: Config.spacing_fun(batteryButton.height)
|
||||
radiusValue: Config.radius_fun(batteryButton.height)
|
||||
|
||||
Column {
|
||||
spacing: parent.spacing
|
||||
Text {
|
||||
text: "percentage: " + BatteryState.percentage + "%"
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
Text {
|
||||
text: "charging: " + BatteryState.charging
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
Text {
|
||||
text: "energy: " + BatteryState.energy
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
Text {
|
||||
text: "voltage: " + BatteryState.voltage
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
Text {
|
||||
text: "charge_cycle: " + BatteryState.charge_cycle
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
Text {
|
||||
text: "time_to_empty: " + BatteryState.time_to_empty + "h"
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
Text {
|
||||
text: "energy_rate: " + BatteryState.energy_rate + "W"
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +1,36 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import "../../../config"
|
||||
import "../states/Clock"
|
||||
import "../../../colors"
|
||||
|
||||
Rectangle { //workspace
|
||||
id: clockRect
|
||||
width: ClockState.expanded ? Math.max(dateText.width + timeText.width + Config.spacing_fun(height)*3, bar.height) : Math.max(timeText.width + Config.spacing_fun(height)*2, bar.height)
|
||||
height: bar.height
|
||||
radius: Config.radius_fun(height)
|
||||
color: clockArea.containsMouse ? Colors.tertiaryContainer : Colors.primaryContainer
|
||||
border.color: (
|
||||
clockArea.containsMouse ? Colors.tertiaryContainer_fg :
|
||||
Colors.primaryContainer_fg
|
||||
)
|
||||
border.width: Config.border_width
|
||||
|
||||
MouseArea {
|
||||
id: clockArea
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
onClicked: {
|
||||
ClockState.expanded = !ClockState.expanded
|
||||
console.log("[Clock] Date visible: " + ClockState.expanded)
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
width: timeDisplay.width + 16
|
||||
height: timeDisplay.height + 16
|
||||
radius: 4
|
||||
color: Colors.primaryContainer
|
||||
border.color: Colors.primaryContainer_fg
|
||||
border.width: 2
|
||||
|
||||
Text {
|
||||
id: dateText
|
||||
text: ClockState.shortDate
|
||||
visible: ClockState.expanded
|
||||
color: (
|
||||
clockArea.containsMouse ? Colors.tertiary :
|
||||
Colors.primary
|
||||
)
|
||||
font.pixelSize: Config.font_size_small(clockRect.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
id: timeDisplay
|
||||
|
||||
Text {
|
||||
id: timeText
|
||||
property string currentTime: ""
|
||||
|
||||
text: ClockState.currentTime
|
||||
color: (
|
||||
clockArea.containsMouse ? Colors.tertiary :
|
||||
Colors.primary
|
||||
)
|
||||
font.pixelSize: Config.font_size_small(clockRect.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BarPopup {
|
||||
id: clockPopup
|
||||
anchorItem: clockRect
|
||||
anchorHovered: clockArea.containsMouse
|
||||
spacingValue: Config.spacing_fun(clockRect.height)
|
||||
radiusValue: Config.radius_fun(clockRect.height)
|
||||
|
||||
Column {
|
||||
spacing: parent.spacing
|
||||
Text {
|
||||
text: ClockState.shortDate + ClockState.currentTime
|
||||
wrapMode: Text.WordWrap
|
||||
text: currentTime
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
font.pixelSize: 18
|
||||
font.family: "Inter, sans-serif"
|
||||
|
||||
Text {
|
||||
text: ClockState.fullDate
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
|
||||
|
||||
Text {
|
||||
text: ClockState.uptime
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
//Update time every second
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
var now = new Date()
|
||||
timeDisplay.currentTime = Qt.formatTime(now, "hh:mm:ss")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../../config"
|
||||
import "../states/Idle"
|
||||
|
||||
MouseArea {
|
||||
id: idleButton
|
||||
width: Math.max(idleText.width + Config.spacing_fun(height)*2, height)
|
||||
height: bar.height
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
var newState = IdleState.idle ? 0 : 1
|
||||
console.log("[IdleButton] Toggling idle: " + (newState ? "enabled" : "disabled"))
|
||||
idleToggleProc.startDetached()
|
||||
IdleState.idle = newState
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: idleButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.tertiaryContainer, 0.25)
|
||||
radius: Config.radius_fun(idleButton.height)
|
||||
border.width: Config.border_width
|
||||
border.color: (
|
||||
idleButton.containsMouse ? Colors.tertiaryContainer_fg :
|
||||
Colors.secondaryContainer_fg
|
||||
)
|
||||
|
||||
Text {
|
||||
id: idleText
|
||||
anchors.centerIn: parent
|
||||
text: IdleState.idle ? "" : ""
|
||||
font.pixelSize: Config.iconSize(idleButton.height)
|
||||
font.family: Config.font
|
||||
color: (
|
||||
idleButton.containsMouse ? Colors.tertiary_fg :
|
||||
IdleState.idle ? Colors.tertiary
|
||||
: Colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: idleToggleProc
|
||||
command: ["sh", "-c", Config.configDir + "/modules/bar/scripts/Idle.sh"]
|
||||
running: false
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,26 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "../../../config"
|
||||
import Quickshell.Hyprland
|
||||
import "../../../colors"
|
||||
|
||||
MouseArea {
|
||||
id: menuButton
|
||||
width: Math.max(bar.height, menuText.width + Config.spacing)
|
||||
height: bar.height
|
||||
id: archButton
|
||||
width: 30
|
||||
height: 28
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
menuProc.running = true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
radius: Config.radius_fun(menuButton.height)
|
||||
color: archButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.primaryContainer, 0.25)
|
||||
radius: 6
|
||||
|
||||
Text {
|
||||
id: menuText
|
||||
anchors.centerIn: parent
|
||||
text: ""
|
||||
font.pixelSize: Config.bigIconSize(menuButton.height)
|
||||
font.family: Config.font
|
||||
color: Colors.secondaryContainer_fg
|
||||
font.pixelSize: 26
|
||||
font.family: "Nerd Font"
|
||||
color: archButton.containsMouse ? Colors.tertiary_fg : Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: menuProc
|
||||
command: ["sh", "-c", "wofi --show drun"]
|
||||
running: false
|
||||
}
|
||||
}
|
||||
+28
-186
@@ -1,210 +1,52 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "../../../config"
|
||||
import "../states/Network"
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../../colors"
|
||||
|
||||
MouseArea {
|
||||
id: networkButton
|
||||
|
||||
width: Math.max(networkText.width + Config.spacing_fun(height)*2, height)
|
||||
width: output.width + 16
|
||||
height: bar.height
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
console.log("[Network] Refreshing... current status: " + NetworkState.status)
|
||||
NetworkState.networkScript.running = false
|
||||
NetworkState.networkScript.running = true
|
||||
networkStatus.running = true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: networkButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.tertiaryContainer, 0.25)
|
||||
radius: Config.radius_fun(networkButton.height)
|
||||
border.width: Config.border_width
|
||||
border.color: networkButton.containsMouse ? Colors.tertiaryContainer_fg
|
||||
: Colors.secondaryContainer_fg
|
||||
radius: 6
|
||||
width: output.width + 16
|
||||
|
||||
Text {
|
||||
id: networkText
|
||||
text: NetworkState.status
|
||||
id: output
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Config.iconSize(bar.height)
|
||||
font.family: Config.font
|
||||
color: networkButton.containsMouse ? Colors.tertiary_fg : networkText.text === " /" ? Colors.error : Colors.primary
|
||||
font.pixelSize: 26
|
||||
font.family: "Nerd Font"
|
||||
color: networkButton.containsMouse ? Colors.tertiary_fg : output.text === " /" ? Colors.error : Colors.primary
|
||||
}
|
||||
|
||||
Process {
|
||||
id: networkStatus
|
||||
command: ["sh", "-c", Quickshell.env("HOME") + "/.config/quickshell/modules/bar/scripts/Network.sh"]
|
||||
running: true
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
output.text = this.text.trim()
|
||||
console.log("networkScript output:", this.text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BarPopup {
|
||||
id: clockPopup
|
||||
anchorItem: networkButton
|
||||
anchorHovered: networkButton.containsMouse
|
||||
spacingValue: Config.spacing_fun(networkButton.height)
|
||||
radiusValue: Config.radius_fun(networkButton.height)
|
||||
|
||||
Column {
|
||||
spacing: parent.spacing
|
||||
Text {
|
||||
text: "interfaceName: " + NetworkState.interfaceName
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "interfaceType: " + NetworkState.interfaceType
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "connectionName: " + NetworkState.connectionName
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "ipv4: " + NetworkState.ipv4
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "wifiName: " + NetworkState.wifiName
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "signalStrength: " + NetworkState.signalStrength
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "wifiChannel: " + NetworkState.wifiChannel
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "wifiFrequency: " + NetworkState.wifiFrequency
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "linkSpeed: " + NetworkState.linkSpeed
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "downloadSpeed: " + NetworkState.downloadSpeed
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "uploadSpeed: " + NetworkState.uploadSpeed
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "ping: " + NetworkState.ping
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "internetAvailable: " + NetworkState.internetAvailable
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "vpnConnected: " + NetworkState.vpnConnected
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "vpnName: " + NetworkState.vpnName
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "vpnIp: " + NetworkState.vpnIp
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "tailscaleConnected: " + NetworkState.tailscaleConnected
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "tailscaleIp: " + NetworkState.tailscaleIp
|
||||
wrapMode: Text.WordWrap
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: Config.font_size_tiny(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
Timer {
|
||||
interval: 10000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: networkStatus.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,48 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "../../../config"
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../../colors"
|
||||
|
||||
MouseArea {
|
||||
id: powerButton
|
||||
width: Math.max(powerText.width + Config.spacing_fun(height), height)
|
||||
width: powerText.width + 16
|
||||
height: bar.height
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
console.log("[PowerButton] Opening wlogout")
|
||||
root.openWlogout()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: powerButton.containsMouse ? Colors.secondaryContainer_fg : Qt.alpha(Colors.secondary, 0.75)
|
||||
radius: Config.radius_fun(powerButton.height)
|
||||
border.width: 0
|
||||
color: powerButton.containsMouse ? Colors.secondaryContainer_fg : Qt.alpha(Colors.secondary, 0.5)
|
||||
radius: 6
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
Process {
|
||||
id: powerOff
|
||||
command: ["wlogout", "-b", "2"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: console.log(`line read: ${this.text}`)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
console.log("Running PowerOff Menu")
|
||||
powerOff.startDetached()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: powerText
|
||||
anchors.centerIn: parent
|
||||
text: ""
|
||||
font.pixelSize: Config.bigIconSize(bar.height)
|
||||
font.family: Config.font
|
||||
font.pixelSize: 26
|
||||
font.family: "Nerd Font"
|
||||
color:powerButton.containsMouse ? Colors.secondaryContainer : Colors.secondary_fg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Io
|
||||
import "../../../config"
|
||||
|
||||
import "../../../colors"
|
||||
|
||||
Row { // Workspace Row
|
||||
id: trayRow
|
||||
spacing: Config.small_spacing_fun(height)
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: SystemTray.items
|
||||
Column{
|
||||
Item {
|
||||
width: 1
|
||||
height: Math.floor((bar.height - trayItem.height) / 2)
|
||||
}
|
||||
|
||||
Rectangle { //trayItem
|
||||
id: trayItem
|
||||
width: iconImage.width + Config.spacing_fun(bar.height)
|
||||
height: iconImage.height + Config.spacing_fun(bar.height)
|
||||
radius: Config.radius_fun(trayRow.height)
|
||||
color: trayArea.containsMouse ? Colors.tertiaryContainer
|
||||
: modelData.active ? Colors.primaryContainer
|
||||
: Colors.secondaryContainer
|
||||
border.width: Config.border_width
|
||||
border.color: trayArea.containsMouse ? Colors.tertiaryContainer_fg
|
||||
: Colors.secondaryContainer_fg
|
||||
width: bar.height
|
||||
height: bar.height
|
||||
radius: 4
|
||||
color: "transparent"
|
||||
border.width: 2
|
||||
|
||||
Process {
|
||||
id: steamRunner
|
||||
@@ -36,96 +27,66 @@ Row { // Workspace Row
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: trayArea
|
||||
id: workspaceButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
property int clickX: 0
|
||||
property int clickY: 0
|
||||
onClicked: (mouse) => {
|
||||
switch (mouse.button) {
|
||||
case Qt.LeftButton:
|
||||
console.log("[Tray] Activate: " + modelData.title)
|
||||
if (!modelData.onlyMenu) modelData.activate()
|
||||
if (!modelData.onlyMenu) {
|
||||
modelData.activate()
|
||||
console.log('Activcate function of ', modelData.title)
|
||||
}
|
||||
if (modelData.title.trim()==="Steam") {
|
||||
console.log("[Tray] Starting Steam")
|
||||
steamRunner.startDetached()
|
||||
}
|
||||
break
|
||||
case Qt.RightButton:
|
||||
console.log("[Tray] Context menu: " + modelData.title)
|
||||
clickX = mouse.x
|
||||
clickY = mouse.y
|
||||
trayMenu.menu = modelData.menu
|
||||
trayMenu.open()
|
||||
break
|
||||
case Qt.MiddleButton:
|
||||
console.log("[Tray] Secondary activate: " + modelData.title)
|
||||
modelData.secondaryActivate()
|
||||
break
|
||||
}
|
||||
// case Qt.RightButton:
|
||||
// if (modelData.hasMenu) {
|
||||
// console.log('Activcate menu of ', modelData.title)
|
||||
// menuAnchor.open()
|
||||
// }
|
||||
// break
|
||||
}
|
||||
}
|
||||
|
||||
QsMenuAnchor {
|
||||
id: trayMenu
|
||||
anchor {
|
||||
item: trayArea
|
||||
edges: Edges.Top | Edges.Left
|
||||
gravity: Edges.Bottom | Edges.Right
|
||||
adjustment: PopupAdjustment.FlipX | PopupAdjustment.SlideY
|
||||
onAnchoring: {
|
||||
rect.x = trayArea.clickX
|
||||
rect.y = trayArea.clickY
|
||||
rect.w = 1
|
||||
rect.h = 1
|
||||
}
|
||||
// QsMenuAnchor {
|
||||
// id: menuAnchor
|
||||
// menu: modelData.menu
|
||||
// anchor.window: menuHost
|
||||
// }
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: workspaceButton.containsMouse ? Colors.tertiaryContainer
|
||||
: modelData.active ? Colors.primaryContainer
|
||||
: Colors.secondaryContainer
|
||||
border.color: workspaceButton.containsMouse ? Colors.tertiaryContainer_fg
|
||||
: modelData.active ? Colors.primaryContainer_fg
|
||||
: Colors.secondaryContainer_fg
|
||||
radius: 6
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Image { // Image
|
||||
id: iconImage
|
||||
source: modelData.icon
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: Config.halfBigIconSize(bar.height)
|
||||
height: Config.halfBigIconSize(bar.height)
|
||||
|
||||
width: bar.height*0.75
|
||||
height: bar.height*0.75
|
||||
|
||||
asynchronous: true
|
||||
cache: true
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: Config.reload_interval
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
let src = modelData.icon;
|
||||
// console.log("########## icon: " + modelData.icon)
|
||||
// console.log("########## name: " + modelData.title)
|
||||
if (modelData.title.includes('GoXLR')) {
|
||||
// console.log("[Tray] Resolving GoXLR icon path")
|
||||
let pathMatch = src.match(/path=([^&]+)/);
|
||||
if (pathMatch && pathMatch[1]) {
|
||||
|
||||
let folderPath = pathMatch[1];
|
||||
let segments = folderPath.split('/');
|
||||
let fileName = segments[segments.length - 1] + "-icon.png";
|
||||
src = "file://" + pathMatch[1] + "/" + fileName;
|
||||
}
|
||||
}
|
||||
// if (modelData.title.includes('Nextcloud')) {
|
||||
// src = Quickshell.iconPath("Nextcloud")
|
||||
// }
|
||||
// console.log("########## icon: " + src)
|
||||
if (src.exists) {
|
||||
console.log("########## icon: " + src)
|
||||
}
|
||||
|
||||
// Set the resolved source
|
||||
iconImage.source = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import "../../../config"
|
||||
import "../states/Apps"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real pad: Config.spacing_fun(bar.height) * 1.5
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
|
||||
implicitWidth: contentRow.implicitWidth + pad * 2
|
||||
implicitHeight: contentRow.implicitHeight
|
||||
|
||||
readonly property string windowClass: Hyprland.activeToplevel?.wayland?.appId ?? ""
|
||||
|
||||
readonly property string windowTitle: {
|
||||
const title = Hyprland.activeToplevel?.title;
|
||||
if (!title)
|
||||
return qsTr("Desktop");
|
||||
return title.split(/\s+[\-\u2013\u2014\|]\s+/).pop();q
|
||||
}
|
||||
|
||||
readonly property string appIcon: {
|
||||
if (!root.windowClass) return AppsState.matchIcon(root.windowTitle);
|
||||
|
||||
let icon = AppsState.matchIcon(root.windowClass);
|
||||
|
||||
if (!icon) {
|
||||
icon = AppsState.matchIcon(root.windowTitle);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
Row {
|
||||
|
||||
id: contentRow
|
||||
spacing: Config.spacing_fun(bar.height) * 1.25
|
||||
anchors.centerIn: parent
|
||||
|
||||
Text {
|
||||
text: root.windowTitle
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
width: Math.min(implicitWidth, 250)
|
||||
|
||||
font.pixelSize: Config.iconSize(bar.height) * 0.8
|
||||
color: Colors.primary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.appIcon
|
||||
visible: text !== ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
font.pixelSize: Math.round(Config.iconSize(bar.height) * 0.85)
|
||||
color: Colors.primary
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,125 +1,62 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import "../../../config"
|
||||
import "../states/Apps"
|
||||
import "../../../colors"
|
||||
|
||||
Rectangle {
|
||||
id: workspacesContainer
|
||||
required property HyprlandMonitor thisMonitor
|
||||
|
||||
height: bar.height - (Config.spacing_fun(bar.height) * 1.5)
|
||||
width: workspacesRow.width + (Config.spacing_fun(bar.height) * 2)
|
||||
|
||||
radius: height / 2
|
||||
|
||||
color: Colors.surfaceVariant
|
||||
border.color: Colors.surfaceVariant_fg
|
||||
border.width: Config.border_width
|
||||
|
||||
Row {
|
||||
Row { // Workspace Row
|
||||
id: workspacesRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Config.spacing_fun(bar.height) / 2
|
||||
|
||||
Repeater {
|
||||
required property HyprlandMonitor thisMonitor
|
||||
spacing: 8
|
||||
|
||||
Repeater { // repeats workspaces
|
||||
model: Hyprland.workspaces
|
||||
Loader {
|
||||
active: modelData.monitor === thisMonitor
|
||||
sourceComponent: Item {
|
||||
id: wsItem
|
||||
property var workspaceData: modelData
|
||||
|
||||
width: Math.max(40, wsIconsRow.width==0 ? wsNum.width + 2*Config.spacing_fun(bar.height) : wsNum.width + wsIconsRow.width + 3*Config.spacing_fun(bar.height)+Config.spacing_fun(bar.height))
|
||||
// width: {
|
||||
// var spacing = Config.spacing_fun(bar.height)
|
||||
// var cw = wsNum.width
|
||||
// if (wsIconsRow.children.length > 0) {
|
||||
// cw += spacing + wsIconsRow.width + spacing
|
||||
// } else {
|
||||
// cw += spacing * 2
|
||||
// }
|
||||
// Math.max(cw, wsNum.height + spacing * 2)
|
||||
// }
|
||||
height: workspacesContainer.height
|
||||
Rectangle { //workspace
|
||||
visible: modelData.monitor === thisMonitor
|
||||
width: workspaceNum.width + 26
|
||||
height: workspaceNum.height + 8
|
||||
radius: 4
|
||||
color: workspaceButton.containsMouse ? Colors.tertiaryContainer
|
||||
: modelData.active ? Colors.primaryContainer
|
||||
: Colors.secondaryContainer
|
||||
border.color: workspaceButton.containsMouse ? Colors.tertiaryContainer_fg
|
||||
: modelData.active ? Colors.primaryContainer_fg
|
||||
: Colors.secondaryContainer_fg
|
||||
border.width: 2
|
||||
|
||||
// highlight
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height - (Config.spacing_fun(bar.height) * 1.5)
|
||||
|
||||
radius: height / 2
|
||||
|
||||
color: wsArea.containsMouse ? Colors.tertiaryContainer
|
||||
: workspaceData.active ? Colors.primaryContainer
|
||||
: "transparent"
|
||||
|
||||
border.color: wsArea.containsMouse ? Colors.tertiaryContainer_fg
|
||||
: workspaceData.active ? Colors.primaryContainer_fg
|
||||
: "transparent"
|
||||
|
||||
border.width: (wsArea.containsMouse || workspaceData.active) ? Config.border_width : 0
|
||||
|
||||
// hyprland workspace api + color
|
||||
MouseArea {
|
||||
id: wsArea
|
||||
id: workspaceButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
console.log("[Workspaces] Switch to workspace: " + workspaceData.id)
|
||||
Hyprland.dispatch("workspace " + workspaceData.id)
|
||||
Hyprland.dispatch("hl.dsp.focus({ workspace = "+workspaceData.id+" })")
|
||||
}
|
||||
onClicked: Hyprland.dispatch("workspace " + modelData.id)
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: wsIconsRow.width==0 ? (wsItem.width-wsNum.width)/2 : (wsItem.width-wsNum.width-wsIconsRow.width-Config.spacing_fun(bar.height))/2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Config.spacing_fun(bar.height)
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
radius: 6
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: wsNum
|
||||
text: workspaceData.id
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: wsArea.containsMouse ? Colors.tertiary
|
||||
: workspaceData.active ? Colors.primary
|
||||
id: workspaceNum
|
||||
text: modelData.id
|
||||
anchors.centerIn: parent
|
||||
color: workspaceButton.containsMouse ? Colors.tertiary
|
||||
: modelData.active ? Colors.primary
|
||||
: Colors.secondary
|
||||
font.pixelSize: Config.iconSize(bar.height)
|
||||
font.family: Config.font
|
||||
font.bold: true
|
||||
font.pixelSize: 18
|
||||
font.family: "Inter, sans-serif"
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: wsIconsRow
|
||||
spacing: Config.spacing_fun(workspacesContainer.height)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Repeater {
|
||||
model: AppsState.iconsForWorkspace(workspaceData.id)
|
||||
delegate: Text {
|
||||
text: modelData
|
||||
color: wsArea.containsMouse ? Colors.tertiary
|
||||
: Colors.secondary
|
||||
font.pixelSize: Config.labelSize(bar.height)
|
||||
font.family: Config.font
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: Hyprland.workspaces.length === 0
|
||||
text: "No Workspaces"
|
||||
color: Colors.error
|
||||
font.pixelSize: Config.iconSize(bar.height)
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: 18
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import QtQuick.Controls
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../config"
|
||||
import "./modules"
|
||||
|
||||
PanelWindow {
|
||||
id: learner
|
||||
implicitWidth: screen.width * 0.75
|
||||
implicitHeight: screen.height * 0.5
|
||||
color: "transparent"
|
||||
visible: true
|
||||
focusable: true
|
||||
|
||||
Component.onCompleted: {
|
||||
semiJLoader.all_invisible()
|
||||
semiJLoader.switchModeChooser()
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: jLearnerScope
|
||||
focus: true
|
||||
|
||||
Keys.onReleased: function(event) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
console.log("Pressed Escape, closing JLearner")
|
||||
jLearnerLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "lightblue"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 10
|
||||
color: Qt.alpha(Colors.surfaceContainerLowest, 0.5)
|
||||
|
||||
border.width: 2
|
||||
border.color: Colors.outlineVariant
|
||||
|
||||
clip: true
|
||||
|
||||
Rectangle{
|
||||
width: parent.width - 20
|
||||
height: parent.height - 20
|
||||
anchors.centerIn: parent
|
||||
color: "transparent"
|
||||
|
||||
Column {
|
||||
id: learnerWindow
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
Rectangle {
|
||||
id: head
|
||||
width: parent.width
|
||||
height: parent.height * 0.1
|
||||
color: Colors.tertiaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.tertiaryContainer_fg
|
||||
|
||||
Text {
|
||||
text: "Language Learner"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: parent.height * 0.8
|
||||
font.family: Config.font
|
||||
color: Colors.tertiary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
id: semiJLoader
|
||||
width: parent.width
|
||||
height: parent.height * 0.9 - parent.spacing
|
||||
|
||||
color: "transparent"
|
||||
|
||||
function all_invisible() {
|
||||
languageChooser.visible = false
|
||||
kanaVoicing.visible = false
|
||||
voicingKana.visible = false
|
||||
learnerResult.visible = false
|
||||
}
|
||||
|
||||
function switchModeChooser() {
|
||||
all_invisible()
|
||||
languageChooser.visible = true
|
||||
languageChooser.start()
|
||||
}
|
||||
|
||||
function switch_KanaVoicing() {
|
||||
all_invisible()
|
||||
kanaVoicing.visible = true
|
||||
kanaVoicing.start()
|
||||
}
|
||||
|
||||
function switch_VoicingKana() {
|
||||
all_invisible()
|
||||
voicingKana.visible = true
|
||||
voicingKana.start()
|
||||
}
|
||||
|
||||
function switchResult(result, mode, results) {
|
||||
all_invisible()
|
||||
console.log("Result: " + result)
|
||||
console.log("Mode: " + mode)
|
||||
learnerResult.visible = true
|
||||
learnerResult.result = result
|
||||
learnerResult.mode = mode
|
||||
learnerResult.results = results
|
||||
learnerResult.start()
|
||||
}
|
||||
|
||||
ModeChooser {
|
||||
id: languageChooser
|
||||
}
|
||||
|
||||
KanaVoicing {
|
||||
id: kanaVoicing
|
||||
}
|
||||
|
||||
VoicingKana {
|
||||
id: voicingKana
|
||||
}
|
||||
|
||||
JLearnerResult {
|
||||
id: learnerResult
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
|
||||
|
||||
Item { // container, invisible
|
||||
id: jLoader
|
||||
|
||||
Loader {
|
||||
id: jLearnerLoader
|
||||
active: false
|
||||
sourceComponent: JLearner { id: jLearner }
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "JLearner"
|
||||
description: "Open Japanese Learner"
|
||||
|
||||
onPressed: {
|
||||
console.log("Pressed Meta+Shift+J (Hyprland global shortcut)")
|
||||
jLearnerLoader.active = !jLearnerLoader.active
|
||||
}
|
||||
}
|
||||
|
||||
function closeJLearner() {
|
||||
jLearnerLoader.active = false
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import "../../../config"
|
||||
|
||||
Item { // Chooser
|
||||
id: learnerResult
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.margins: 15
|
||||
visible: false
|
||||
|
||||
function switch_KanaVoicing() {
|
||||
parent.switch_KanaVoicing()
|
||||
}
|
||||
|
||||
function start() {
|
||||
}
|
||||
|
||||
property real result: 0
|
||||
property string mode: ""
|
||||
property var results: createResults(10)
|
||||
|
||||
function createResults(n) {
|
||||
var arr = []
|
||||
for (var i = 0; i < n; i++) {
|
||||
arr.push({
|
||||
kana: "あ",
|
||||
correctness: false,
|
||||
answer: "a",
|
||||
correct: "a"
|
||||
})
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
Rectangle {
|
||||
id: learnerResultUpperText
|
||||
color: Colors.primaryContainer
|
||||
width: parent.width
|
||||
height: parent.height * 0.1
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
text: "Ergebnisse:"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: parent.height * 0.6
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
height: parent.height - learnerResultUpperText.height - parent.spacing
|
||||
spacing: 10
|
||||
Rectangle {
|
||||
width: parent.width * 0.25
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
Column{
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
spacing: 10
|
||||
|
||||
Repeater{
|
||||
model: results
|
||||
|
||||
Row{
|
||||
width: parent.width
|
||||
height: parent.height/10 - parent.spacing * 0.9
|
||||
spacing: 10
|
||||
|
||||
Rectangle{
|
||||
width: parent.height
|
||||
height: parent.height
|
||||
color: Colors.primaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text{
|
||||
text: modelData.kana
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -parent.height * 0.125
|
||||
anchors.horizontalCenterOffset: parent.width * 0.1
|
||||
font.pixelSize: parent.height * 0.8
|
||||
font.family: Config.jfont
|
||||
color: modelData.correctness ? "green" : "red"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
width: parent.height
|
||||
height: parent.height
|
||||
color: Colors.primaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Image {
|
||||
source: modelData.correctness ? Config.configDir+"/modules/jl/assets/correct.svg" : Config.configDir+"/modules/jl/assets/wrong.svg"
|
||||
anchors.centerIn: parent
|
||||
width: parent.height*0.8
|
||||
height: parent.height*0.8
|
||||
|
||||
smooth: true
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
width: parent.width - parent.height *2 - parent.spacing*2
|
||||
height: parent.height
|
||||
color: Colors.primaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text{
|
||||
text: "[" + modelData.correct + "]"
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: 10
|
||||
font.pixelSize: parent.height * 0.8
|
||||
font.family: Config.font
|
||||
color: modelData.correctness ? "green" : "red"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
width: parent.width * 0.75
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
Column{
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
spacing: 10
|
||||
Row {
|
||||
spacing: 10
|
||||
width: parent.width
|
||||
height: parent.height * 4 / 10 - parent.spacing * 2 / 3
|
||||
|
||||
Rectangle {
|
||||
color: "transparent"
|
||||
width: (parent.width - (learnerLastResultKanaBG.width + learnerLastResultTextBG.width))/2
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: learnerLastResultKanaBG
|
||||
color: results[results.length-1].correctness ? "green" : "red"
|
||||
width: parent.height - parent.spacing
|
||||
height: parent.height
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: learnerLastResultKana
|
||||
text: results[results.length-1].kana
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -parent.height * 0.1
|
||||
font.pixelSize: parent.height * 0.8
|
||||
font.family: Config.font
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: learnerLastResultTextBG
|
||||
color: results[results.length-1].correctness ? "green" : "red"
|
||||
width: learnerLastResultText.width + 2 * parent.spacing
|
||||
height: parent.height
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: learnerLastResultText
|
||||
text: "[" + results[results.length-1].correct + "]"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: learnerLastResultKana.font.pixelSize/2
|
||||
font.family: Config.font
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: learnerResultTextBG
|
||||
color: Colors.primaryContainer
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
height: parent.height * 3 / 10 - parent.spacing * 2 / 3
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: learnerResultText
|
||||
text: result*100 + "% Richtig"
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
|
||||
font.family: Config.font
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
height: parent.height * 3 / 10 - parent.spacing * 2 / 3
|
||||
spacing: 10
|
||||
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
width: parent.width / 2 - parent.spacing
|
||||
height: parent.height
|
||||
|
||||
onClicked: {
|
||||
console.log("Choosing White Window")
|
||||
mode === "KanaVoicing" ? learnerResult.parent.switch_KanaVoicing() : learnerResult.parent.switch_VoicingKana()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
|
||||
|
||||
Text {
|
||||
text: "Again"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
|
||||
font.family: Config.font
|
||||
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
width: parent.width / 2 - parent.spacing
|
||||
height: parent.height
|
||||
|
||||
onClicked: {
|
||||
console.log("Choosing Black Window")
|
||||
learnerResult.parent.switchModeChooser()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
|
||||
|
||||
Text {
|
||||
text: "Menu"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
|
||||
font.family: Config.font
|
||||
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
property var kana: [
|
||||
{ hira: "あ", voice: ["a"], known: true },
|
||||
{ hira: "い", voice: ["i"], known: true },
|
||||
{ hira: 'う', voice: ["u"], known: true },
|
||||
{ hira: 'え', voice: ["e"], known: true },
|
||||
{ hira: 'お', voice: ["o"], known: true },
|
||||
{ hira: 'か', voice: ["ka"], known: true },
|
||||
{ hira: 'が', voice: ["ga"], known: true },
|
||||
{ hira: 'き', voice: ["ki"], known: true },
|
||||
{ hira: 'ぎ', voice: ["gi"], known: true },
|
||||
{ hira: 'く', voice: ["ku"], known: true },
|
||||
{ hira: 'ぐ', voice: ["gu"], known: true },
|
||||
{ hira: 'け', voice: ["ke"], known: true },
|
||||
{ hira: 'げ', voice: ["ge"], known: true },
|
||||
{ hira: 'こ', voice: ["ko"], known: true },
|
||||
{ hira: 'ご', voice: ["go"], known: true },
|
||||
{ hira: 'さ', voice: ["sa"], known: true },
|
||||
{ hira: 'ざ', voice: ["za"], known: true },
|
||||
{ hira: 'し', voice: ["si", "shi"], known: true },
|
||||
{ hira: 'じ', voice: ["zi", "ji"], known: true },
|
||||
{ hira: 'す', voice: ["su"], known: true },
|
||||
{ hira: 'ず', voice: ["zu"], known: true },
|
||||
{ hira: 'せ', voice: ["se"], known: true },
|
||||
{ hira: 'ぜ', voice: ["ze"], known: true },
|
||||
{ hira: 'そ', voice: ["so"], known: true },
|
||||
{ hira: 'ぞ', voice: ["zo"], known: true },
|
||||
{ hira: 'た', voice: ["ta"], known: true },
|
||||
{ hira: 'だ', voice: ["da"], known: true },
|
||||
{ hira: 'ち', voice: ["ti", "shi"], known: true },
|
||||
{ hira: 'ぢ', voice: ["di", "ji"], known: true },
|
||||
{ hira: 'つ', voice: ["tu", "tsu"], known: true },
|
||||
{ hira: 'づ', voice: ["du", "zu"], known: true },
|
||||
{ hira: 'て', voice: ["te"], known: true },
|
||||
{ hira: 'で', voice: ["de"], known: true },
|
||||
{ hira: 'と', voice: ["to"], known: true },
|
||||
{ hira: 'ど', voice: ["do"], known: true },
|
||||
{ hira: 'な', voice: ["na"], known: true },
|
||||
{ hira: 'に', voice: ["ni"], known: true },
|
||||
{ hira: 'ぬ', voice: ["nu"], known: false },
|
||||
{ hira: 'ね', voice: ["ne"], known: false },
|
||||
{ hira: 'の', voice: ["no"], known: false },
|
||||
{ hira: 'は', voice: ["ha"], known: false },
|
||||
{ hira: 'ば', voice: ["ba"], known: false },
|
||||
{ hira: 'ぱ', voice: ["pa"], known: false },
|
||||
{ hira: 'ひ', voice: ["hi"], known: false },
|
||||
{ hira: 'び', voice: ["bi"], known: false },
|
||||
{ hira: 'ぴ', voice: ["pi"], known: false },
|
||||
{ hira: 'ふ', voice: ["hu"], known: false },
|
||||
{ hira: 'ぶ', voice: ["bu"], known: false },
|
||||
{ hira: 'ぷ', voice: ["pu"], known: false },
|
||||
{ hira: 'へ', voice: ["he"], known: false },
|
||||
{ hira: 'べ', voice: ["be"], known: false },
|
||||
{ hira: 'ぺ', voice: ["pe"], known: false },
|
||||
{ hira: 'ほ', voice: ["ho"], known: false },
|
||||
{ hira: 'ぼ', voice: ["bo"], known: false },
|
||||
{ hira: 'ぽ', voice: ["po"], known: false },
|
||||
{ hira: 'ま', voice: ["ma"], known: false },
|
||||
{ hira: 'み', voice: ["mi"], known: false },
|
||||
{ hira: 'む', voice: ["mu"], known: false },
|
||||
{ hira: 'め', voice: ["me"], known: false },
|
||||
{ hira: 'も', voice: ["mo"], known: false },
|
||||
{ hira: 'や', voice: ["ya"], known: false },
|
||||
{ hira: 'ゆ', voice: ["yu"], known: false },
|
||||
{ hira: 'よ', voice: ["yo"], known: false },
|
||||
{ hira: 'ら', voice: ["ra"], known: false },
|
||||
{ hira: 'り', voice: ["ri"], known: false },
|
||||
{ hira: 'る', voice: ["ru"], known: false },
|
||||
{ hira: 'れ', voice: ["re"], known: false },
|
||||
{ hira: 'ろ', voice: ["ro"], known: false },
|
||||
{ hira: 'わ', voice: ["wa"], known: false },
|
||||
{ hira: 'ゐ', voice: ["wi"], known: false },
|
||||
{ hira: 'ゑ', voice: ["we"], known: false },
|
||||
{ hira: 'を', voice: ["wo"], known: false },
|
||||
{ hira: 'ん', voice: ["n"], known: true },
|
||||
{ hira: 'ゔ', voice: ["vu"], known: false }
|
||||
]
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import "../../../config"
|
||||
|
||||
Item { // Kana_Try
|
||||
id: kanaVoicing
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.margins: 15
|
||||
visible: false
|
||||
|
||||
// Component.onCompleted: {
|
||||
// randomKnownKana()
|
||||
// }
|
||||
|
||||
function start() {
|
||||
kanaVoicingAnswer.focus = true
|
||||
roundNum = 0
|
||||
randomKnownKana()
|
||||
}
|
||||
|
||||
KanaData {
|
||||
id: kanaData
|
||||
}
|
||||
|
||||
property var currentKana: []
|
||||
|
||||
property int roundNum: 0
|
||||
property int correct: 0
|
||||
property int maxRoundNum: 10
|
||||
|
||||
property var pastKana: []
|
||||
property var pastCorrectness: []
|
||||
|
||||
function endRounds() {
|
||||
kanaVoicingAnswer.focus = false
|
||||
jLearnerScope.focus = true
|
||||
console.log("Switching back")
|
||||
parent.switchResult(correct/maxRoundNum, "KanaVoicing", pastCorrectness)
|
||||
pastKana = []
|
||||
pastCorrectness = []
|
||||
correct = 0
|
||||
kanaVoicingAnswer.placeholderText = "Type Voicing..."
|
||||
kanaVoicingAnswerBackground.color = kanaVoicingAnswer.activeFocus ? Colors.tertiary_fg : Colors.primary_fg
|
||||
}
|
||||
|
||||
function randomKana() {
|
||||
if (roundNum >= maxRoundNum) {
|
||||
endRounds()
|
||||
}
|
||||
var list = kanaData.kana
|
||||
currentKana = list[Math.floor(Math.random() * list.length)]
|
||||
if (knownList.length > maxRoundNum) {
|
||||
while (pastKana.includes(currentKana)) {
|
||||
currentKana = list[Math.floor(Math.random() * list.length)]
|
||||
}
|
||||
}
|
||||
pastKana.push(currentKana)
|
||||
console.log("Round Num: " + roundNum + " - Choosing Kana: " + currentKana.hira)
|
||||
roundNum = roundNum + 1
|
||||
}
|
||||
|
||||
function randomKnownKana() {
|
||||
if (roundNum >= maxRoundNum) {
|
||||
endRounds()
|
||||
}
|
||||
var knownList = kanaData.kana.filter(k => k.known)
|
||||
currentKana = knownList[Math.floor(Math.random() * knownList.length)]
|
||||
if (knownList.length > maxRoundNum) {
|
||||
while (pastKana.includes(currentKana)) {
|
||||
currentKana = knownList[Math.floor(Math.random() * knownList.length)]
|
||||
}
|
||||
}
|
||||
pastKana.push(currentKana)
|
||||
console.log("Round Num: " + roundNum + " - Choosing Kana: " + currentKana.hira)
|
||||
roundNum = roundNum + 1
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
Rectangle {
|
||||
id: kanaVoicingTextRectangale
|
||||
color: Colors.primaryContainer
|
||||
width: kanaVoicingText.width + 20
|
||||
height: parent.height * 0.1
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: kanaVoicingText
|
||||
text: "Im Spiel - Runde: " + kanaVoicing.roundNum
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: parent.height * 0.6
|
||||
font.family: Config.font
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: kanaVoicingKanaRectangle
|
||||
color: Colors.primaryContainer
|
||||
height: (parent.height - kanaVoicingTextRectangale.height)*0.75
|
||||
width: height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: kanaVoicingKana
|
||||
text: typeof kanaVoicing.currentKana.hira === "undefined" ? "" : kanaVoicing.currentKana.hira
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
|
||||
font.family: Config.jfont
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: kanaVoicingAnswer
|
||||
|
||||
placeholderText: "Type Voicing..."
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
width: parent.width/2
|
||||
height: (parent.height - kanaVoicingTextRectangale.height)*0.25 - parent.spacing*2
|
||||
|
||||
font.pixelSize: height * 0.8
|
||||
|
||||
color: Colors.secondary_fg
|
||||
placeholderTextColor: Colors.secondary
|
||||
|
||||
background: Rectangle {
|
||||
id: kanaVoicingAnswerBackground
|
||||
radius: 10
|
||||
color: kanaVoicingAnswer.activeFocus ? Colors.secondaryContainer : Colors.secondaryContainer_fg
|
||||
border.color: kanaVoicingAnswer.activeFocus ? Colors.secondaryContainer_fg : Colors.secondaryContainer
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
Keys.onReleased: function(event) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
console.log("Escape pressed inside TextField")
|
||||
console.log("Pressed Escape, closing JLearner")
|
||||
jLearnerLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: function(event) {
|
||||
console.log("Enter pressed inside TextField")
|
||||
if (kanaVoicing.currentKana.voice.includes(kanaVoicingAnswer.text.toLowerCase())){
|
||||
console.log("Correct")
|
||||
kanaVoicingAnswer.text = ""
|
||||
kanaVoicingAnswer.placeholderText = "[" + kanaVoicing.currentKana.voice + "]"
|
||||
kanaVoicingAnswerBackground.color = "green"
|
||||
kanaVoicing.correct = kanaVoicing.correct + 1
|
||||
pastCorrectness.push({
|
||||
kana: kanaVoicing.currentKana.hira,
|
||||
correctness: true,
|
||||
answer: kanaVoicingAnswer.text.toLowerCase(),
|
||||
correct: kanaVoicing.currentKana.voice.join("|")
|
||||
})
|
||||
} else {
|
||||
console.log("Wrong: [" + currentKana.voice + "]")
|
||||
kanaVoicingAnswer.text = ""
|
||||
kanaVoicingAnswerBackground.color = "red"
|
||||
kanaVoicingAnswer.placeholderText = "[" + kanaVoicing.currentKana.voice + "]"
|
||||
pastCorrectness.push({
|
||||
kana: kanaVoicing.currentKana.hira,
|
||||
correctness: false,
|
||||
answer: kanaVoicingAnswer.text.toLowerCase(),
|
||||
correct: kanaVoicing.currentKana.voice.join("|")
|
||||
})
|
||||
}
|
||||
randomKnownKana()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import "../../../config"
|
||||
|
||||
Item { // Chooser
|
||||
id: languageChooser
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.margins: 15
|
||||
visible: false
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
Rectangle {
|
||||
id: modeChooserUpperText
|
||||
color: Colors.primaryContainer
|
||||
width: mainText.width + 20
|
||||
height: parent.height * 0.1
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
text: "Wähle ein Modus:"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: parent.height * 0.6
|
||||
font.family: Config.font
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
height: parent.height - modeChooserUpperText.height - parent.spacing
|
||||
spacing: 10
|
||||
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
height: parent.height
|
||||
|
||||
onClicked: {
|
||||
console.log("Choosing White Window")
|
||||
languageChooser.parent.switch_KanaVoicing()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
|
||||
|
||||
Text {
|
||||
text: "Kana to Voicing"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
|
||||
font.family: Config.font
|
||||
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
height: parent.height
|
||||
|
||||
onClicked: {
|
||||
console.log("Choosing Black Window")
|
||||
languageChooser.parent.switch_VoicingKana()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
|
||||
|
||||
Text {
|
||||
text: "Voicing to Kana"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
|
||||
font.family: Config.font
|
||||
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import "../../../config"
|
||||
|
||||
Item { // Kana_Try
|
||||
id: voicingKana
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.margins: 15
|
||||
visible: false
|
||||
|
||||
// Component.onCompleted: {
|
||||
// randomKnownKana()
|
||||
// }
|
||||
|
||||
function start() {
|
||||
roundNum = 0
|
||||
new_round()
|
||||
}
|
||||
|
||||
KanaData {
|
||||
id: kanaData
|
||||
}
|
||||
|
||||
property int roundNum: 0
|
||||
property int correct: 0
|
||||
property int maxRoundNum: 10
|
||||
|
||||
property var pastKana: []
|
||||
property var pastCorrectness: []
|
||||
|
||||
property var kanaArray: createResults(6)
|
||||
property var targetKana: kanaArray[Math.floor(Math.random() * kanaArray.length)]
|
||||
|
||||
function createResults(n) {
|
||||
var arr = []
|
||||
for (var i = 0; i < n; i++) {
|
||||
arr.push({ hira: "あ", voice: ["a"], known: true })
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
function new_round() {
|
||||
roundNum = roundNum + 1
|
||||
kanaArray = gemerateKanaArray()
|
||||
targetKana = kanaArray[Math.floor(Math.random() * kanaArray.length)]
|
||||
console.log("Round Num: " + roundNum + " - Choosing Kana: " + targetKana.hira)
|
||||
pastKana.push(targetKana)
|
||||
}
|
||||
|
||||
function gemerateKanaArray() {
|
||||
var arr = []
|
||||
for (var i = 0; i < 6; i++) {
|
||||
arr.push(randomKnownKana(arr))
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
function randomKana(arr) {
|
||||
if (roundNum > maxRoundNum) {
|
||||
endRounds()
|
||||
}
|
||||
var list = kanaData.kana
|
||||
var newKana = list[Math.floor(Math.random() * list.length)]
|
||||
|
||||
var arrExists = Array.isArray(arr)
|
||||
|
||||
if (knownList.length > maxRoundNum + 6) {
|
||||
if (arrExists) {
|
||||
while (pastKana.includes(newKana) || arr.includes(newKana)) {
|
||||
newKana = list[Math.floor(Math.random() * list.length)]
|
||||
}
|
||||
} else{
|
||||
while (pastKana.includes(newKana)){
|
||||
newKana = list[Math.floor(Math.random() * list.length)]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (arrExists) {
|
||||
while (arr.includes(newKana)) {
|
||||
newKana = list[Math.floor(Math.random() * list.length)]
|
||||
}
|
||||
}
|
||||
}
|
||||
kanaArray.push(newKana)
|
||||
return newKana
|
||||
}
|
||||
|
||||
function randomKnownKana(arr) {
|
||||
if (roundNum > maxRoundNum) {
|
||||
endRounds()
|
||||
}
|
||||
var knownList = kanaData.kana.filter(k => k.known)
|
||||
var newKana = knownList[Math.floor(Math.random() * knownList.length)]
|
||||
|
||||
var arrExists = Array.isArray(arr)
|
||||
|
||||
if (knownList.length > maxRoundNum + 6) {
|
||||
if (arrExists) {
|
||||
while (pastKana.includes(newKana) || arr.includes(newKana)) {
|
||||
newKana = knownList[Math.floor(Math.random() * knownList.length)]
|
||||
}
|
||||
} else{
|
||||
while (pastKana.includes(newKana)){
|
||||
newKana = knownList[Math.floor(Math.random() * knownList.length)]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (arrExists) {
|
||||
while (arr.includes(newKana)) {
|
||||
newKana = knownList[Math.floor(Math.random() * knownList.length)]
|
||||
}
|
||||
}
|
||||
}
|
||||
return newKana
|
||||
}
|
||||
|
||||
function endRounds() {
|
||||
console.log("Switching back")
|
||||
roundNum = 0
|
||||
parent.switchResult(correct/maxRoundNum, "VoicingKana", pastCorrectness)
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
Rectangle {
|
||||
id: voicingKanaTextRectangale
|
||||
color: Colors.primaryContainer
|
||||
width: mainText.width + 20
|
||||
height: parent.height * 0.1
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
text: "Runde: " + roundNum + " - [" + targetKana.voice.join("|") + "]:"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: parent.height * 0.6
|
||||
font.family: Config.font
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
width: parent.width
|
||||
height: parent.height - voicingKanaTextRectangale.height - voicingKanaResultRectangale.height - parent.spacing * 2
|
||||
color: "transparent"
|
||||
Grid {
|
||||
id: grid
|
||||
anchors.fill: parent
|
||||
columns: 3
|
||||
spacing: 10
|
||||
|
||||
property int rows: 2
|
||||
|
||||
// calculate cell width and height based on parent size, spacing, and number of columns/rows
|
||||
property real cellWidth: (width - (columns - 1) * spacing) / columns
|
||||
property real cellHeight: (height - (rows - 1) * spacing) / rows
|
||||
|
||||
Repeater {
|
||||
model: kanaArray
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
width: grid.cellWidth
|
||||
height: grid.cellHeight
|
||||
|
||||
onClicked: {
|
||||
console.log("Choose a Kana: " + modelData.hira)
|
||||
if (modelData.hira === targetKana.hira) {
|
||||
console.log("Correct")
|
||||
correct = correct + 1
|
||||
voicingKanaResultRectangale.color = "green"
|
||||
} else {
|
||||
console.log("Wrong: [" + targetKana.hira + "]")
|
||||
voicingKanaResultRectangale.color = "red"
|
||||
}
|
||||
pastCorrectness.push({
|
||||
kana: targetKana.hira,
|
||||
correctness: modelData.hira === targetKana.hira,
|
||||
answer: modelData.hira,
|
||||
correct: targetKana.voice.join("|")
|
||||
})
|
||||
voicingKanaResult.text = "Letzte Runde: " + targetKana.hira + ": " + "[" + targetKana.voice.join("|") + "]"
|
||||
new_round()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
|
||||
|
||||
Text{
|
||||
text: modelData.hira
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: -parent.height * 0.1
|
||||
font.pixelSize: parent.height * 0.8
|
||||
font.family: Config.jfont
|
||||
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: voicingKanaResultRectangale
|
||||
color: Colors.primaryContainer
|
||||
width: voicingKanaResult.width + 20
|
||||
height: parent.height * 0.1
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
radius: 10
|
||||
border.width: 2
|
||||
border.color: Colors.primaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: voicingKanaResult
|
||||
text: "Wähle das richtige Zeichen"
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: parent.height * 0.6
|
||||
font.family: Config.font
|
||||
color: Colors.primary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Notifications
|
||||
import "../../config"
|
||||
|
||||
Rectangle {
|
||||
id: card
|
||||
|
||||
required property var notification
|
||||
required property var manager
|
||||
|
||||
width: parent.width
|
||||
height: Math.max(64, cardContent.height + 24)
|
||||
radius: Config.radius
|
||||
|
||||
property var urgency: notification.urgency
|
||||
property bool isCritical: urgency === NotificationUrgency.Critical
|
||||
|
||||
color: {
|
||||
if (isCritical) return Qt.alpha(Colors.errorContainer, 0.95)
|
||||
if (urgency === NotificationUrgency.Low) return Qt.alpha(Colors.surfaceContainerHigh, 0.95)
|
||||
return Qt.alpha(Colors.primaryContainer, 0.95)
|
||||
}
|
||||
|
||||
border {
|
||||
width: Config.border_width
|
||||
color: {
|
||||
if (isCritical) return Colors.error
|
||||
if (urgency === NotificationUrgency.Low) return Colors.outline
|
||||
return Colors.primaryContainer_fg
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation on opacity {
|
||||
id: fadeInAnim
|
||||
from: 0
|
||||
to: 1
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
NumberAnimation on x {
|
||||
id: slideInAnim
|
||||
from: parent ? parent.width : 100
|
||||
to: 0
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
fadeInAnim.start()
|
||||
slideInAnim.start()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
manager.dismissNotification(notification)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: cardContent
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 12
|
||||
}
|
||||
spacing: 10
|
||||
|
||||
Item {
|
||||
id: iconContainer
|
||||
width: 28
|
||||
height: 28
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Image {
|
||||
id: notifIcon
|
||||
anchors.fill: parent
|
||||
source: notification.appIcon ? "file://" + notification.appIcon : ""
|
||||
sourceSize.width: 28
|
||||
sourceSize.height: 28
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "\uF0F3"
|
||||
font.pixelSize: 16
|
||||
font.family: Config.font
|
||||
visible: !notifIcon.visible
|
||||
color: Colors.primary_fg
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: textColumn
|
||||
width: parent.width - iconContainer.width - parent.spacing - 20
|
||||
spacing: 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
id: summaryText
|
||||
width: parent.width
|
||||
text: notification.summary || notification.appName || "Notification"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
font.family: Config.font
|
||||
color: {
|
||||
if (isCritical) return Colors.error
|
||||
if (urgency === NotificationUrgency.Low) return Colors.secondaryContainer_fg
|
||||
return Colors.primary
|
||||
}
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
Text {
|
||||
id: bodyText
|
||||
width: parent.width
|
||||
text: notification.body || ""
|
||||
visible: text !== ""
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 11
|
||||
font.family: Config.font
|
||||
color: isCritical ? Colors.errorContainer_fg : Colors.surface_fg
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "\u00D7"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: isCritical ? Colors.error : Colors.secondary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: 0.6
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
manager.dismissNotification(notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Notifications
|
||||
import "../../config"
|
||||
|
||||
MouseArea {
|
||||
id: indicator
|
||||
|
||||
required property var manager
|
||||
|
||||
height: bar.height
|
||||
width: indicatorRow.width + Config.spacing_fun(height) * 2
|
||||
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
property bool popupOpen: false
|
||||
|
||||
onClicked: {
|
||||
popupOpen = !popupOpen
|
||||
if (popupOpen) {
|
||||
notifPopup.visible = true
|
||||
} else {
|
||||
notifPopup.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: indicator.containsMouse || popupOpen ? Colors.tertiaryContainer : "transparent"
|
||||
radius: Config.radius_fun(indicator.height)
|
||||
border.width: (indicator.containsMouse || popupOpen) ? Config.border_width : 0
|
||||
border.color: (indicator.containsMouse || popupOpen) ? Colors.tertiaryContainer_fg : "transparent"
|
||||
|
||||
Row {
|
||||
id: indicatorRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Config.spacing_fun(indicator.height) / 2
|
||||
|
||||
Item {
|
||||
id: iconWrap
|
||||
width: Config.iconSize(indicator.height)
|
||||
height: Config.iconSize(indicator.height)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Image {
|
||||
id: notifIcon
|
||||
anchors.fill: parent
|
||||
source: (manager.lastNotification && manager.lastNotification.appIcon)
|
||||
? "file://" + manager.lastNotification.appIcon : ""
|
||||
sourceSize.width: Config.iconSize(indicator.height)
|
||||
sourceSize.height: Config.iconSize(indicator.height)
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "\uF0F3"
|
||||
font.pixelSize: Config.iconSize(indicator.height)
|
||||
font.family: Config.font
|
||||
visible: !notifIcon.visible
|
||||
color: indicator.containsMouse ? Colors.tertiary : Colors.secondary
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: countBadge
|
||||
width: Math.max(badgeText.width + 6, 16)
|
||||
height: 16
|
||||
radius: 8
|
||||
visible: manager.notificationCount > 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: manager.hasCritical ? Colors.error : Colors.primary
|
||||
|
||||
Text {
|
||||
id: badgeText
|
||||
anchors.centerIn: parent
|
||||
text: manager.notificationCount > 99 ? "99+" : manager.notificationCount.toString()
|
||||
font.pixelSize: Config.font_size_tiny(indicator.height)
|
||||
font.family: Config.font
|
||||
font.bold: true
|
||||
color: manager.hasCritical ? Colors.error_fg : Colors.primary_fg
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: criticalDot
|
||||
width: 8
|
||||
height: 8
|
||||
radius: 4
|
||||
visible: manager.hasCritical
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: Colors.error
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
PropertyAnimation { from: 1.0; to: 0.4; duration: 800 }
|
||||
PropertyAnimation { from: 0.4; to: 1.0; duration: 800 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PopupWindow {
|
||||
id: notifPopup
|
||||
visible: indicator.popupOpen
|
||||
color: "transparent"
|
||||
|
||||
anchor {
|
||||
item: indicator
|
||||
edges: Edges.Top | Edges.Left
|
||||
gravity: Edges.Bottom | Edges.Right
|
||||
adjustment: PopupAdjustment.FlipY | PopupAdjustment.SlideX
|
||||
onAnchoring: {
|
||||
anchor.rect.x = (indicator.width - notifPopup.width) / 2
|
||||
anchor.rect.y = indicator.height + 4
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: 280
|
||||
implicitHeight: Math.max(60, popupContent.height + 24)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.alpha(Colors.primaryContainer, 0.95)
|
||||
radius: Config.radius
|
||||
border {
|
||||
width: Config.border_width
|
||||
color: Colors.primaryContainer_fg
|
||||
}
|
||||
|
||||
Column {
|
||||
id: popupContent
|
||||
width: parent.width
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 12
|
||||
}
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: "Notifications"
|
||||
font.pixelSize: Config.font_size_small(indicator.height)
|
||||
font.family: Config.font
|
||||
font.bold: true
|
||||
color: Colors.primary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: manager.notificationCount > 0
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: manager.notifications
|
||||
|
||||
delegate: Rectangle {
|
||||
width: parent.width
|
||||
height: delegateText.height + 10
|
||||
radius: Config.small_radius
|
||||
color: Qt.alpha(Colors.surfaceContainerHighest, 0.5)
|
||||
|
||||
property var notif: modelData
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 6
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
spacing: 6
|
||||
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: 24
|
||||
radius: 3
|
||||
color: {
|
||||
if (notif.urgency === NotificationUrgency.Critical) return Colors.error
|
||||
if (notif.urgency === NotificationUrgency.Low) return Colors.outline
|
||||
return Colors.primary
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width - 24
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
id: delegateText
|
||||
width: parent.width
|
||||
text: notif.summary || notif.appName || "Notification"
|
||||
font.pixelSize: Config.labelSize(indicator.height)
|
||||
font.family: Config.font
|
||||
font.bold: true
|
||||
color: Colors.surface_fg
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: notif.body || ""
|
||||
visible: text !== ""
|
||||
font.pixelSize: Config.font_size_tiny(indicator.height)
|
||||
font.family: Config.font
|
||||
color: Colors.surfaceVariant_fg
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "No notifications"
|
||||
font.pixelSize: Config.font_size_small(indicator.height)
|
||||
font.family: Config.font
|
||||
color: Colors.secondary
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: manager.notificationCount === 0
|
||||
height: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) indicator.popupOpen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Notifications
|
||||
import "../../config"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
NotificationServer {
|
||||
id: notificationServer
|
||||
|
||||
imageSupported: true
|
||||
actionsSupported: true
|
||||
bodyMarkupSupported: false
|
||||
|
||||
onNotification: (notification) => {
|
||||
notification.tracked = true
|
||||
root.onNotificationReceived(notification)
|
||||
}
|
||||
}
|
||||
|
||||
readonly property ObjectModel notifications: notificationServer.trackedNotifications
|
||||
|
||||
property int notificationCount: 0
|
||||
property var lastNotification: null
|
||||
property bool hasCritical: false
|
||||
|
||||
readonly property string soundDir: Config.configDir + "/assets/sounds"
|
||||
|
||||
property var _trackedIds: ({})
|
||||
property var _dismissTimes: ({})
|
||||
|
||||
function onNotificationReceived(notification) {
|
||||
_trackedIds[notification.id] = true
|
||||
notificationCount = Object.keys(_trackedIds).length
|
||||
lastNotification = notification
|
||||
if (notification.urgency === NotificationUrgency.Critical)
|
||||
hasCritical = true
|
||||
|
||||
playSound(notification.urgency)
|
||||
scheduleDismiss(notification)
|
||||
}
|
||||
|
||||
function playSound(urgency) {
|
||||
var path
|
||||
switch (urgency) {
|
||||
case NotificationUrgency.Low:
|
||||
path = soundDir + "/notification-low.oga"
|
||||
break
|
||||
case NotificationUrgency.Critical:
|
||||
path = soundDir + "/notification-critical.oga"
|
||||
break
|
||||
default:
|
||||
path = soundDir + "/notification-normal.oga"
|
||||
break
|
||||
}
|
||||
soundProc.command = [
|
||||
"sh", "-c",
|
||||
"pw-play \"" + path + "\" 2>/dev/null || paplay \"" + path + "\" 2>/dev/null || ffplay -nodisp -autoexit \"" + path + "\" 2>/dev/null"
|
||||
]
|
||||
soundProc.running = true
|
||||
}
|
||||
|
||||
function scheduleDismiss(notification) {
|
||||
var timeout = notification.expireTimeout
|
||||
if (timeout <= 0) {
|
||||
if (notification.urgency === NotificationUrgency.Critical)
|
||||
timeout = 60
|
||||
else
|
||||
timeout = 5
|
||||
}
|
||||
_dismissTimes[notification.id] = Date.now() / 1000 + timeout
|
||||
if (!refreshTimer.running)
|
||||
refreshTimer.running = true
|
||||
}
|
||||
|
||||
function dismissNotification(notification) {
|
||||
delete _trackedIds[notification.id]
|
||||
delete _dismissTimes[notification.id]
|
||||
notificationCount = Object.keys(_trackedIds).length
|
||||
notification.tracked = false
|
||||
refreshMetadata()
|
||||
}
|
||||
|
||||
function refreshMetadata() {
|
||||
hasCritical = false
|
||||
lastNotification = null
|
||||
for (var i = 0; i < notifications.count; i++) {
|
||||
var n = notifications.get(i)
|
||||
if (_trackedIds[n.id]) {
|
||||
lastNotification = n
|
||||
if (n.urgency === NotificationUrgency.Critical)
|
||||
hasCritical = true
|
||||
}
|
||||
}
|
||||
if (notificationCount === 0) {
|
||||
hasCritical = false
|
||||
lastNotification = null
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 1000
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
var now = Date.now() / 1000
|
||||
for (var id in _dismissTimes) {
|
||||
if (_dismissTimes[id] <= now) {
|
||||
for (var i = 0; i < notifications.count; i++) {
|
||||
var n = notifications.get(i)
|
||||
if (n.id.toString() === id) {
|
||||
dismissNotification(n)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(_trackedIds).length === 0)
|
||||
running = false
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: soundProc
|
||||
running: false
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.Notifications
|
||||
import "../../config"
|
||||
|
||||
Variants {
|
||||
id: root
|
||||
|
||||
required property var manager
|
||||
|
||||
model: Quickshell.screens
|
||||
|
||||
delegate: PanelWindow {
|
||||
id: popupWindow
|
||||
screen: modelData
|
||||
color: "transparent"
|
||||
|
||||
anchors.top: true
|
||||
anchors.right: true
|
||||
|
||||
width: 380
|
||||
height: Math.min(implicitHeight, screen.height * 0.6)
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "notification-popup"
|
||||
|
||||
margins {
|
||||
top: 8
|
||||
right: 8
|
||||
}
|
||||
|
||||
readonly property var hyprMonitor: Hyprland.monitorFor(screen)
|
||||
visible: hyprMonitor === Hyprland.focusedMonitor
|
||||
|
||||
implicitHeight: contentColumn.height
|
||||
|
||||
Connections {
|
||||
target: Hyprland
|
||||
function onFocusedMonitorChanged() {
|
||||
popupWindow.visible = popupWindow.hyprMonitor === Hyprland.focusedMonitor
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.manager.notifications
|
||||
function onCountChanged() {
|
||||
popupWindow.visible = popupWindow.hyprMonitor === Hyprland.focusedMonitor
|
||||
&& root.manager.notifications.count > 0
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
topPadding: 4
|
||||
|
||||
Repeater {
|
||||
model: root.manager.notifications
|
||||
|
||||
delegate: NotificationCard {
|
||||
width: contentColumn.width
|
||||
notification: modelData
|
||||
manager: root.manager
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
NotificationManager 1.0 NotificationManager.qml
|
||||
NotificationPopup 1.0 NotificationPopup.qml
|
||||
NotificationIndicator 1.0 NotificationIndicator.qml
|
||||
NotificationCard 1.0 NotificationCard.qml
|
||||
@@ -1,22 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
|
||||
QtObject {
|
||||
required property string command
|
||||
required property string text
|
||||
required property string icon
|
||||
required property string icon_text
|
||||
property var keybind: null
|
||||
|
||||
id: button
|
||||
|
||||
readonly property var process: Process {
|
||||
command: ["sh", "-c", button.command]
|
||||
}
|
||||
|
||||
function exec() {
|
||||
process.startDetached();
|
||||
console.log("Picked Logout Option, closing WLogout");
|
||||
wlogoutLoader.active = false;
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
|
||||
import "../../config"
|
||||
|
||||
Variants {
|
||||
id: root
|
||||
property color buttonColor: Colors.secondaryContainer
|
||||
property color buttonHoverColor: Colors.primary
|
||||
property color borderColor: Colors.secondaryContainer_fg
|
||||
property color borderHoverColor: Colors.primaryContainer_fg
|
||||
default property list<LogoutButton> buttons
|
||||
|
||||
model: Quickshell.screens
|
||||
PanelWindow {
|
||||
id: w
|
||||
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
|
||||
color: "transparent"
|
||||
|
||||
contentItem {
|
||||
focus: true
|
||||
Keys.onPressed: event => {
|
||||
if (event.key == Qt.Key_Escape) {
|
||||
console.log("Pressed Escape, closing Wallpaper Picker")
|
||||
console.log("Wallpaper Dir:", Config.wallpaperDir)
|
||||
wlogoutLoader.active = false
|
||||
} else {
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
let button = buttons[i];
|
||||
if (event.key == button.keybind) button.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
bottom: true
|
||||
right: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: Config.wallpaperDir + "/Static/blurred.jpg"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
console.log("clicked ???, closing Wallpaper Picker")
|
||||
wlogoutLoader.active = false
|
||||
}
|
||||
GridLayout {
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: parent.width * 0.75
|
||||
height: parent.height * 0.75
|
||||
|
||||
columns: 3
|
||||
columnSpacing: 20
|
||||
rowSpacing: 20
|
||||
|
||||
Repeater {
|
||||
model: buttons
|
||||
delegate: Rectangle {
|
||||
required property LogoutButton modelData;
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
color: ma.containsMouse ? buttonHoverColor : buttonColor
|
||||
radius: 10
|
||||
border.color: ma.containsMouse ? borderColor : borderHoverColor
|
||||
border.width: 2
|
||||
|
||||
MouseArea {
|
||||
id: ma
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: modelData.exec()
|
||||
}
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
anchors.centerIn: parent
|
||||
source: `icons/${modelData.icon}.svg`
|
||||
width: parent.width * 0.3
|
||||
height: parent.width * 0.3
|
||||
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
// Text {
|
||||
// id: icon
|
||||
// anchors.centerIn: parent
|
||||
|
||||
// text: modelData.icon_text ?? "?"
|
||||
// font.pointSize: parent.height * 0.4
|
||||
// color: "white"
|
||||
// }
|
||||
|
||||
Text {
|
||||
anchors {
|
||||
top: icon.bottom
|
||||
topMargin: 20
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
text: modelData.text
|
||||
font.pointSize: 20
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "../../config"
|
||||
|
||||
ShellRoot {
|
||||
WLogout {
|
||||
LogoutButton {
|
||||
command: Config.configDir + "/modules/wlogout/scripts/lock.sh"
|
||||
keybind: Qt.Key_L
|
||||
text: "Lock"
|
||||
icon: "lock"
|
||||
icon_text: ""
|
||||
}
|
||||
|
||||
LogoutButton {
|
||||
command: Config.configDir + "/modules/wlogout/scripts/logout.sh"
|
||||
keybind: Qt.Key_E
|
||||
text: "Logout"
|
||||
icon: "logout"
|
||||
icon_text: ""
|
||||
}
|
||||
|
||||
LogoutButton {
|
||||
command: Config.configDir + "/modules/wlogout/scripts/suspend.sh"
|
||||
keybind: Qt.Key_U
|
||||
text: "Suspend"
|
||||
icon: "suspend"
|
||||
icon_text: ""
|
||||
}
|
||||
|
||||
LogoutButton {
|
||||
command: Config.configDir + "/modules/wlogout/scripts/hibernate.sh"
|
||||
keybind: Qt.Key_H
|
||||
text: "Hibernate"
|
||||
icon: "hibernate"
|
||||
icon_text: ""
|
||||
}
|
||||
|
||||
LogoutButton {
|
||||
command: Config.configDir + "/modules/wlogout/scripts/shutdown.sh"
|
||||
keybind: Qt.Key_K
|
||||
text: "Shutdown"
|
||||
icon: "shutdown"
|
||||
icon_text: "⏻"
|
||||
}
|
||||
|
||||
LogoutButton {
|
||||
command: Config.configDir + "/modules/wlogout/scripts/reboot.sh"
|
||||
keybind: Qt.Key_R
|
||||
text: "Reboot"
|
||||
icon: "reboot"
|
||||
icon_text: " "
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../config"
|
||||
|
||||
|
||||
Item { // container, invisible
|
||||
id: wlLoader
|
||||
|
||||
Loader {
|
||||
id: wlogoutLoader
|
||||
active: false
|
||||
sourceComponent: Wlogout {
|
||||
id: wLogout
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "WLogout"
|
||||
description: "Open WLogout"
|
||||
|
||||
onPressed: {
|
||||
console.log("Pressed Meta+Shift+W (Hyprland global shortcut)")
|
||||
wlogoutLoader.active = !wlogoutLoader.active
|
||||
}
|
||||
}
|
||||
|
||||
function openWlogout() {
|
||||
wlogoutLoader.active = true
|
||||
}
|
||||
|
||||
function closeWlogout() {
|
||||
wlogoutLoader.active = false
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
systemctl hibernate
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
hyprlock
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 1. Run helper script if it exists
|
||||
if [[ -x "$SCRIPT_PATH" ]]; then
|
||||
echo "Running helper script..."
|
||||
bash "$SCRIPT_PATH"
|
||||
else
|
||||
echo "Helper script not found, skipping..."
|
||||
fi
|
||||
|
||||
# 2. Run hyprshutdown if available
|
||||
if command -v hyprshutdown >/dev/null 2>&1; then
|
||||
echo "Running hyprshutdown..."
|
||||
hyprshutdown
|
||||
else
|
||||
echo "Rebooting system..."
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl loginctl terminate-user $USER
|
||||
else
|
||||
pkill -KILL -u "$USER" -t tty2
|
||||
fi
|
||||
fi
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_PATH="$HOME/.config/quickshell/modules/wlogout/scripts/helper.sh"
|
||||
|
||||
# 1. Run helper script if it exists
|
||||
if [[ -x "$SCRIPT_PATH" ]]; then
|
||||
echo "Running helper script..."
|
||||
bash "$SCRIPT_PATH"
|
||||
else
|
||||
echo "Helper script not found, skipping..."
|
||||
fi
|
||||
|
||||
# 2. Run hyprshutdown if available
|
||||
if command -v hyprshutdown >/dev/null 2>&1; then
|
||||
echo "Running hyprshutdown..."
|
||||
hyprshutdown
|
||||
else
|
||||
echo "hyprshutdown not found, skipping..."
|
||||
fi
|
||||
|
||||
# 3. Power off if systemctl exists
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "Rebooting system..."
|
||||
systemctl reboot
|
||||
else
|
||||
reboot
|
||||
fi
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_PATH="$HOME/.config/quickshell/modules/wlogout/scripts/helper.sh"
|
||||
|
||||
# 1. Run helper script if it exists
|
||||
if [[ -x "$SCRIPT_PATH" ]]; then
|
||||
echo "Running helper script..."
|
||||
bash "$SCRIPT_PATH"
|
||||
else
|
||||
echo "Helper script not found, skipping..."
|
||||
fi
|
||||
|
||||
# 2. Run hyprshutdown if available
|
||||
if command -v hyprshutdown >/dev/null 2>&1; then
|
||||
echo "Running hyprshutdown..."
|
||||
hyprshutdown
|
||||
else
|
||||
echo "hyprshutdown not found, skipping..."
|
||||
fi
|
||||
|
||||
# 3. Power off if systemctl exists
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
echo "Powering off system..."
|
||||
systemctl poweroff
|
||||
else
|
||||
shutdown -h now
|
||||
fi
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
systemctl suspend
|
||||
@@ -4,7 +4,7 @@ import QtQuick.Controls
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import Qt.labs.folderlistmodel
|
||||
import "../../config"
|
||||
import "../../colors"
|
||||
|
||||
PanelWindow {
|
||||
id: picker
|
||||
@@ -14,23 +14,6 @@ PanelWindow {
|
||||
visible: true
|
||||
focusable: true
|
||||
|
||||
FocusScope {
|
||||
id: pickerScope
|
||||
focus: true
|
||||
|
||||
Keys.onReleased: function(event) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
console.log("Pressed Escape, closing Wallpaper Picker")
|
||||
wallpaperLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "lightblue"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 10
|
||||
@@ -59,22 +42,11 @@ PanelWindow {
|
||||
background: Rectangle {
|
||||
|
||||
radius: 12
|
||||
color: filterText.activeFocus ? Colors.tertiary_fg : Colors.primary_fg
|
||||
border.color: filterText.activeFocus ? Colors.tertiary : Colors.primary
|
||||
color: "#2b2b2b"
|
||||
border.color: filterText.activeFocus ? "#4da3ff" : "#555"
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
Keys.onReleased: {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
console.log("Escape pressed inside TextField")
|
||||
console.log("Pressed Escape, closing Wallpaper Picker")
|
||||
// wallpaperLoader.active = false
|
||||
filterText.text = ""
|
||||
filterText.focus = false
|
||||
event.accepted = true
|
||||
pickerScope.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +60,7 @@ PanelWindow {
|
||||
|
||||
width: parent.width - 35
|
||||
height: parent.height - 100
|
||||
color: Colors.surfaceContainer
|
||||
color: Qt.alpha(Colors.surfaceContainer, 0.5)
|
||||
radius: 10
|
||||
|
||||
clip: true
|
||||
@@ -143,30 +115,20 @@ PanelWindow {
|
||||
|
||||
|
||||
// pictures + text
|
||||
delegate: Rectangle {
|
||||
width: grid.cellWidth -10
|
||||
height: grid.cellHeight -25
|
||||
color: mouseArea.containsMouse ? Qt.alpha(Colors.surfaceContainerHover, 0.5) : "transparent" // select color
|
||||
radius: 10
|
||||
clip: true
|
||||
delegate: Item {
|
||||
width: grid.cellWidth
|
||||
height: grid.cellHeight
|
||||
|
||||
Column {
|
||||
spacing: 20
|
||||
|
||||
Rectangle {
|
||||
width: grid.cellWidth -10
|
||||
height: grid.cellHeight * 0.7
|
||||
|
||||
border.width: 2
|
||||
border.color: mouseArea.containsMouse ? Qt.alpha(Colors.surfaceContainerHover, 0.5) : "transparent"
|
||||
|
||||
radius: 10
|
||||
|
||||
Image { // Image
|
||||
source: filePath
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
width: grid.cellWidth -10
|
||||
height: grid.cellHeight * 0.7
|
||||
|
||||
sourceSize.width: grid.cellWidth -10
|
||||
sourceSize.height: grid.cellHeight * 0.7
|
||||
|
||||
@@ -174,14 +136,6 @@ PanelWindow {
|
||||
cache: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.width: 2
|
||||
border.color: mouseArea.containsMouse ? Qt.alpha(Colors.surfaceContainerHover, 0.5) : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // highlight of text
|
||||
id: textHighlight
|
||||
width: parent.width - 10
|
||||
@@ -205,7 +159,7 @@ PanelWindow {
|
||||
anchors.fill: parent
|
||||
Process {
|
||||
id: wallpaperProcess
|
||||
command: ["sh", "-c", Config.configDir + "/modules/wp/set-wallpaper.sh" + " " + model.filePath + " &"]
|
||||
command: ["sh", "-c", Quickshell.env("HOME") + "/.config/quickshell/modules/wp/set-wallpaper.sh" + " " + model.filePath + " &"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: console.log('line read:', this.text)
|
||||
}
|
||||
@@ -237,7 +191,7 @@ PanelWindow {
|
||||
anchors.fill: parent
|
||||
radius: 10
|
||||
color: "transparent"
|
||||
border.color: Colors.primary
|
||||
border.color: Colors.outline
|
||||
border.width: 1
|
||||
z: 999
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../config"
|
||||
|
||||
|
||||
Item {
|
||||
Item { // container, invisible
|
||||
id: wLoader
|
||||
|
||||
Process {
|
||||
id: generateThumbs
|
||||
command: ["sh", "-c", Config.configDir + "/modules/wp/generate-wallpaper-thumbs.sh"]
|
||||
command: ["sh", "-c", Quickshell.env("HOME") + "/.config/quickshell/modules/wp/generate-wallpaper-thumbs.sh"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: console.log(this.text)
|
||||
}
|
||||
@@ -29,8 +28,8 @@ Item {
|
||||
onPressed: {
|
||||
// generateThumbs.startDetached()
|
||||
generateThumbs.running = true
|
||||
console.log("Pressed Meta+Shift+W (Hyprland global shortcut)")
|
||||
wallpaperLoader.active = !wallpaperLoader.active
|
||||
console.log("Pressed Meta+Shift+W (Hyprland global shortcut)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
|
||||
# Folder with full-size wallpapers
|
||||
WALLPAPER_DIR=$("$SCRIPT_DIR/../../scripts/wallpaper_dir.sh")
|
||||
|
||||
echo "$WALLPAPER_DIR"
|
||||
WALLPAPER_DIR="$HOME/Pictures/Wallpapers"
|
||||
|
||||
# Folder to store thumbnails
|
||||
THUMB_DIR="$HOME/.cache/wallpaper-thumbs"
|
||||
echo "$THUMB_DIR"
|
||||
|
||||
if [ ! -d $THUMB_DIR ]; then
|
||||
mkdir -p $THUMB_DIR;
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
files=( "$THUMB_DIR"/*.jpg "$THUMB_DIR"/*.jpeg "$THUMB_DIR"/*.png )
|
||||
# printf ' %s\n' "${files[@]}"
|
||||
|
||||
# Make sure the thumb directory exists
|
||||
mkdir -p "$THUMB_DIR"
|
||||
|
||||
@@ -26,28 +19,6 @@ MAX_DIM=300
|
||||
|
||||
num=0
|
||||
|
||||
remove_from_array() {
|
||||
local -n arr=$1
|
||||
local elem=$2
|
||||
local result=()
|
||||
|
||||
# echo "=== Removing element: '$elem' ==="
|
||||
# echo "Original array has ${#arr[@]} elements"
|
||||
|
||||
for item in "${arr[@]}"; do
|
||||
if [[ "$item" != "$elem" ]]; then
|
||||
result+=( "$item" )
|
||||
# echo "Keeping: '$item'"
|
||||
# else
|
||||
# echo "Removing: '$item'"
|
||||
fi
|
||||
done
|
||||
|
||||
arr=( "${result[@]}" )
|
||||
# echo "New array has ${#arr[@]} elements"
|
||||
# echo "-----------------------------------"
|
||||
}
|
||||
|
||||
# Process each image
|
||||
for img in "$WALLPAPER_DIR"/*.{jpg,jpeg,png}; do
|
||||
# Skip if no files match
|
||||
@@ -55,46 +26,19 @@ for img in "$WALLPAPER_DIR"/*.{jpg,jpeg,png}; do
|
||||
|
||||
# Get just the filename
|
||||
fname=$(basename "$img")
|
||||
name="${fname%.*}"
|
||||
ext="${fname##*.}"
|
||||
|
||||
# no magick file:
|
||||
temp_image_name="$THUMB_DIR/${name}_tmp.${ext}"
|
||||
|
||||
# Output path
|
||||
thumb="$THUMB_DIR/$fname"
|
||||
|
||||
|
||||
# If thumbnail already exists and is newer than the original, skip
|
||||
if [ -f "$thumb" ] && [ "$thumb" -nt "$img" ]; then
|
||||
remove_from_array files "$thumb"
|
||||
continue
|
||||
fi
|
||||
|
||||
((num++))
|
||||
num+=1
|
||||
|
||||
# Generate thumbnail using ImageMagick
|
||||
if [ -x "/usr/bin/magick" ]; then
|
||||
magick "$img" -resize "${MAX_DIM}x${MAX_DIM}" "$thumb"
|
||||
if [ -f "$temp_image_name" ]; then
|
||||
rm "$temp_image_name"
|
||||
fi
|
||||
else
|
||||
if [ -f "$temp_image_name" ] && [ "$temp_image_name" -nt "$img" ]; then
|
||||
continue
|
||||
fi
|
||||
cp $img $temp_image_name
|
||||
fi
|
||||
remove_from_array files "$thumb"
|
||||
done
|
||||
|
||||
# echo ""
|
||||
|
||||
# printf ' %s\n' "${files[@]}"
|
||||
|
||||
for img in $files; do
|
||||
# echo "$img"
|
||||
rm $img
|
||||
done
|
||||
|
||||
echo "$num Thumbnails generated in $THUMB_DIR"
|
||||
@@ -1,18 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
|
||||
# Folder with full-size wallpapers
|
||||
WALLPAPER_DIR=$("$SCRIPT_DIR/../../scripts/wallpaper_dir.sh")
|
||||
if [ -z "$WALLPAPER_DIR" ]; then
|
||||
echo "Failed to determine wallpaper directory. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Path to the image is the first argument
|
||||
IMAGE="$1"
|
||||
image_name=$(basename "$IMAGE")
|
||||
image="$WALLPAPER_DIR/$image_name"
|
||||
image="$HOME/Pictures/Wallpapers/$image_name"
|
||||
|
||||
echo "start"
|
||||
|
||||
# Check if an image was provided
|
||||
if [[ -z "$IMAGE" ]]; then
|
||||
@@ -20,7 +13,6 @@ if [[ -z "$IMAGE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "start"
|
||||
|
||||
if [ -x "/usr/bin/swww-daemon" ]; then
|
||||
echo "swww-daemon exists and is executable"
|
||||
@@ -50,25 +42,14 @@ elif [ -x "/usr/bin/hyprpaper" ]; then
|
||||
sleep 0.2
|
||||
echo "daemon started"
|
||||
fi
|
||||
sed -i "3s|path = .*|path = ${image}|" ~/.config/hypr/hyprpaper.conf
|
||||
|
||||
hyprctl monitors -j | jq -r 'sort_by(.id) | .[].name' | while IFS= read -r display; do
|
||||
# echo "hyprctl hyprpaper wallpaper $display, $image"
|
||||
hyprctl hyprpaper wallpaper "$display, $image"
|
||||
done
|
||||
hyprctl hyprpaper wallpaper ", $image"
|
||||
fi
|
||||
|
||||
if [ -x "/usr/bin/matugen" ]; then
|
||||
echo "matugen exists, creating colors"
|
||||
matugen image $image --mode dark --source-color-index 0
|
||||
echo "matugen image $image --mode dark --source-color-index 0"
|
||||
else
|
||||
cp "$SCRIPT_DIR/../../assets/example_colors.qml" "$SCRIPT_DIR/../../config/Colors.qml"
|
||||
fi
|
||||
matugen image $image --mode dark
|
||||
|
||||
### Blured Wallpaper for wlogout
|
||||
blur="50x30"
|
||||
static_wallpaper_dir="$WALLPAPER_DIR/Static"
|
||||
static_wallpaper_dir="$HOME/Pictures/Wallpapers/Static"
|
||||
|
||||
if [ ! -d $static_wallpaper_dir ]; then
|
||||
mkdir -p $static_wallpaper_dir;
|
||||
@@ -80,15 +61,6 @@ echo "$image" > $current_wallpaper
|
||||
|
||||
blurred_wp="$static_wallpaper_dir/blurred.jpg"
|
||||
|
||||
if command -v magick >/dev/null 2>&1; then
|
||||
magick $image -blur $blur $blurred_wp
|
||||
# echo "magick $image -blur $blur $blurred_wp"
|
||||
else
|
||||
cp $image $blurred_wp
|
||||
fi
|
||||
# if [ -x "/usr/bin/sddm" ] && [ -d "/usr/share/backgrounds" ]; then
|
||||
# sddm_background="/usr/share/backgrounds/background.jpg"
|
||||
# cp "$image" "$sddm_background"
|
||||
# fi
|
||||
|
||||
echo "test"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Setting wallpaper_dir
|
||||
|
||||
WALLPAPER_DIR="${XDG_PICTURES_DIR:-}"
|
||||
|
||||
# 2. If empty, use xdg-user-dir (most reliable)
|
||||
if [ -z "$WALLPAPER_DIR" ]; then
|
||||
if command -v xdg-user-dir >/dev/null 2>&1; then
|
||||
WALLPAPER_DIR="$(xdg-user-dir PICTURES 2>/dev/null)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. Final fallback (hardcoded default)
|
||||
if [ -z "$WALLPAPER_DIR" ] || [ ! -d "$WALLPAPER_DIR" ]; then
|
||||
WALLPAPER_DIR="$HOME/Pictures"
|
||||
fi
|
||||
|
||||
# 4. Your wallpaper subfolder
|
||||
WALLPAPER_DIR="$WALLPAPER_DIR/Wallpapers"
|
||||
|
||||
echo $WALLPAPER_DIR
|
||||
@@ -3,39 +3,16 @@
|
||||
import Quickshell //for PanelWindow
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick //for Text
|
||||
import Quickshell.Services.Notifications
|
||||
import "./modules/bar"
|
||||
import "./modules/wp"
|
||||
import "./modules/wlogout"
|
||||
import "./modules/jl"
|
||||
import "./modules/background"
|
||||
import "./modules/notifications"
|
||||
|
||||
ShellRoot {
|
||||
id: root
|
||||
|
||||
// Notifications
|
||||
NotificationManager { id: notificationManager }
|
||||
|
||||
NotificationPopup { manager: notificationManager }
|
||||
|
||||
// Bar
|
||||
BarLoader { manager: notificationManager }
|
||||
BarLoader {}
|
||||
|
||||
//Wallpaper picker
|
||||
WallpaperPickerLoader {}
|
||||
|
||||
//Japanese Lerner
|
||||
JLearnerLoader {}
|
||||
|
||||
//Wlogout
|
||||
WlogoutLoader {
|
||||
id : wlLoader
|
||||
}
|
||||
|
||||
Background {}
|
||||
|
||||
function openWlogout() {
|
||||
wlLoader.openWlogout()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user