Merge remote-tracking branch 'upstream/main' into layout_service
@@ -1,13 +0,0 @@
|
||||
Config(
|
||||
x: Fraction(0.500000),
|
||||
y: Absolute(15),
|
||||
width: Fraction(0.300000),
|
||||
height: Absolute(0),
|
||||
hide_icons: false,
|
||||
ignore_exclusive_zones: false,
|
||||
layer: Overlay,
|
||||
hide_plugin_info: true,
|
||||
close_on_click: true,
|
||||
show_results_immediately: false,
|
||||
max_entries: None,
|
||||
)
|
||||
@@ -1,66 +0,0 @@
|
||||
* {
|
||||
all: unset;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
#window,
|
||||
#match,
|
||||
#entry,
|
||||
#plugin,
|
||||
#main {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#match.activatable {
|
||||
border-radius: 16px;
|
||||
padding: 0.3rem 0.9rem;
|
||||
margin-top: 0.01rem;
|
||||
}
|
||||
#match.activatable:first-child {
|
||||
margin-top: 0.7rem;
|
||||
}
|
||||
#match.activatable:last-child {
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
#plugin:hover #match.activatable {
|
||||
border-radius: 10px;
|
||||
padding: 0.3rem;
|
||||
margin-top: 0.01rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#match:selected,
|
||||
#match:hover,
|
||||
#plugin:hover {
|
||||
background: #2e3131;
|
||||
}
|
||||
|
||||
#entry {
|
||||
background: #0b0f10;
|
||||
border: 1px solid #0b0f10;
|
||||
border-radius: 16px;
|
||||
margin: 0.5rem;
|
||||
padding: 0.3rem 1rem;
|
||||
}
|
||||
|
||||
list > #plugin {
|
||||
border-radius: 16px;
|
||||
margin: 0 0.3rem;
|
||||
}
|
||||
list > #plugin:first-child {
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
list > #plugin:last-child {
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
list > #plugin:hover {
|
||||
padding: 0.6rem;
|
||||
}
|
||||
|
||||
box#main {
|
||||
background: #0b0f10;
|
||||
box-shadow: inset 0 0 0 1px #0b0f10, 0 0 0 1px #0b0f10;
|
||||
border-radius: 24px;
|
||||
padding: 0.3rem;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
--password-store=gnome-libsecret
|
||||
# --ozone-platform-hint=wayland
|
||||
--ozone-platform-hint=wayland
|
||||
--gtk-version=4
|
||||
--ignore-gpu-blocklist
|
||||
--enable-features=TouchpadOverscrollHistoryNavigation
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# --ozone-platform-hint=wayland
|
||||
--ozone-platform-hint=wayland
|
||||
--gtk-version=4
|
||||
--ignore-gpu-blocklist
|
||||
--enable-features=TouchpadOverscrollHistoryNavigation
|
||||
|
||||
@@ -20,6 +20,7 @@ end
|
||||
alias pamcan pacman
|
||||
alias ls 'eza --icons'
|
||||
alias clear "printf '\033[2J\033[3J\033[1;1H'"
|
||||
alias q 'qs -c ii'
|
||||
|
||||
|
||||
# function fish_prompt
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# $lock_cmd = hyprctl dispatch global quickshell:lock & pidof qs quickshell hyprlock || hyprlock
|
||||
$lock_cmd = pidof hyprlock || hyprlock
|
||||
$suspend_cmd = systemctl suspend || loginctl suspend
|
||||
|
||||
general {
|
||||
lock_cmd = $lock_cmd
|
||||
before_sleep_cmd = loginctl lock-session
|
||||
after_sleep_cmd = hyprctl dispatch global quickshell:lockFocus
|
||||
inhibit_sleep = 3
|
||||
}
|
||||
|
||||
listener {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# This file sources other files in `hyprland` and `custom` folders
|
||||
# You wanna add your stuff in files in `custom`
|
||||
|
||||
$qsConfig = ii
|
||||
exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND
|
||||
submap = global # This is required for catchall to work
|
||||
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland
|
||||
env = QT_IM_MODULE, fcitx
|
||||
env = XMODIFIERS, @im=fcitx
|
||||
# env = GTK_IM_MODULE, wayland # Crashes electron apps in xwayland
|
||||
# env = GTK_IM_MODULE, fcitx # My Gtk apps no longer require this to work with fcitx5 hmm
|
||||
env = SDL_IM_MODULE, fcitx
|
||||
env = GLFW_IM_MODULE, ibus
|
||||
env = INPUT_METHOD, fcitx
|
||||
|
||||
# ############ Wayland #############
|
||||
env = ELECTRON_OZONE_PLATFORM_HINT,auto
|
||||
|
||||
# ############ Themes #############
|
||||
env = QT_QPA_PLATFORM, wayland
|
||||
env = QT_QPA_PLATFORMTHEME, kde
|
||||
# env = QT_STYLE_OVERRIDE,kvantum
|
||||
# env = WLR_NO_HARDWARE_CURSORS, 1
|
||||
env = XDG_MENU_PREFIX, plasma-
|
||||
|
||||
# ######## Screen tearing #########
|
||||
# ######## Wayland #########
|
||||
# Tearing
|
||||
# env = WLR_DRM_NO_ATOMIC, 1
|
||||
# ?
|
||||
# env = WLR_NO_HARDWARE_CURSORS, 1
|
||||
|
||||
# ######## Virtual envrionment #########
|
||||
env = ILLOGICAL_IMPULSE_VIRTUAL_ENV, ~/.local/state/quickshell/.venv
|
||||
|
||||
# ############ Others #############
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Bar, wallpaper
|
||||
exec-once = swww-daemon --format xrgb --no-cache
|
||||
exec-once = sleep 0.5; swww img "$(cat ~/.local/state/quickshell/user/generated/wallpaper/path.txt)" --transition-step 100 --transition-fps 120 --transition-type grow --transition-angle 30 --transition-duration 1
|
||||
exec-once = /usr/lib/geoclue-2.0/demos/agent & gammastep
|
||||
exec-once = qs &
|
||||
exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh
|
||||
exec-once = qs -c $qsConfig &
|
||||
|
||||
# Input method
|
||||
exec-once = fcitx5
|
||||
|
||||
@@ -97,10 +97,10 @@ animations {
|
||||
animation = border, 1, 10, emphasizedDecel
|
||||
# layers
|
||||
animation = layersIn, 1, 2.7, emphasizedDecel, popin 93%
|
||||
animation = layersOut, 1, 2, menu_accel, popin 94%
|
||||
animation = layersOut, 1, 2.4, menu_accel, popin 94%
|
||||
# fade
|
||||
animation = fadeLayersIn, 1, 0.5, menu_decel
|
||||
animation = fadeLayersOut, 1, 2.2, menu_accel
|
||||
animation = fadeLayersOut, 1, 2.7, menu_accel
|
||||
# workspaces
|
||||
animation = workspaces, 1, 7, menu_decel, slide
|
||||
## specialWorkspace
|
||||
@@ -115,6 +115,7 @@ input {
|
||||
repeat_rate = 35
|
||||
|
||||
follow_mouse = 1
|
||||
off_window_axis_events = 2
|
||||
|
||||
touchpad {
|
||||
natural_scroll = yes
|
||||
@@ -133,10 +134,11 @@ misc {
|
||||
key_press_enables_dpms = true
|
||||
animate_manual_resizes = false
|
||||
animate_mouse_windowdragging = false
|
||||
enable_swallow = true
|
||||
enable_swallow = false
|
||||
swallow_regex = (foot|kitty|allacritty|Alacritty)
|
||||
new_window_takes_over_fullscreen = 2
|
||||
allow_session_lock_restore = true
|
||||
session_lock_xray = true
|
||||
initial_workspace_tracking = false
|
||||
focus_on_activate = true
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
##! Shell
|
||||
# These absolutely need to be on top, or they won't work consistently
|
||||
bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher
|
||||
bind = Super, Super_L, exec, qs ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback)
|
||||
bind = Super, Super_L, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback)
|
||||
binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
@@ -30,11 +30,12 @@ bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # T
|
||||
bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
||||
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
||||
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
||||
bind = Ctrl+Alt, Delete, exec, qs ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
||||
bind = Shift+Super+Alt, Slash, exec, qs -p ~/.config/quickshell/welcome.qml # [hidden] Launch welcome app
|
||||
bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
||||
bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
||||
bind = Shift+Super+Alt, Slash, exec, qs -p ~/.config/quickshell/$qsConfig/welcome.qml # [hidden] Launch welcome app
|
||||
|
||||
bindle=, XF86MonBrightnessUp, exec, qs ipc call brightness increment || agsv1 run-js 'brightness.screen_value += 0.05; indicator.popup(1);' # [hidden]
|
||||
bindle=, XF86MonBrightnessDown, exec, qs ipc call brightness decrement || agsv1 run-js 'brightness.screen_value -= 0.05; indicator.popup(1);' # [hidden]
|
||||
bindle=, XF86MonBrightnessUp, exec, qs -c $qsConfig ipc call brightness increment || brightnessctl s 5%+ # [hidden]
|
||||
bindle=, XF86MonBrightnessDown, exec, qs -c $qsConfig ipc call brightness decrement || brightnessctl s 5%- # [hidden]
|
||||
bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 2%+ # [hidden]
|
||||
bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%- # [hidden]
|
||||
|
||||
@@ -43,15 +44,14 @@ bindld = Super+Shift,M, Toggle mute, exec, wpctl set-mute @DEFAULT_SINK@ toggle
|
||||
bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindl = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindld = Super+Alt,M, Toggle mic, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindd = Ctrl+Super, T, Change wallpaper, exec, ~/.config/quickshell/scripts/colors/switchwall.sh # Change wallpaper
|
||||
bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs & # Restart widgets
|
||||
bindd = Ctrl+Super, T, Change wallpaper, exec, ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # Change wallpaper
|
||||
bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets
|
||||
|
||||
##! Utilities
|
||||
# Screenshot, Record, OCR, Color picker, Clipboard history
|
||||
bindd = Super, V, Copy clipboard history entry, exec, qs ipc call TEST_ALIVE || pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # [hidden] Clipboard history >> clipboard (fallback)
|
||||
bindd = Super, Period, Copy an emoji, exec, qs ipc call TEST_ALIVE || pkill fuzzel || ~/.config/hypr/hyprland/scripts/fuzzel-emoji.sh copy # [hidden] Emoji >> clipboard (fallback)
|
||||
bindd = Super+Shift, S, Screen snip, exec, pidof slurp || hyprshot --freeze --clipboard-only --mode region --silent # Screen snip >> clipboard
|
||||
bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, pidof slurp || grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate
|
||||
bindd = Super, V, Copy clipboard history entry, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # [hidden] Clipboard history >> clipboard (fallback)
|
||||
bindd = Super, Period, Copy an emoji, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || ~/.config/hypr/hyprland/scripts/fuzzel-emoji.sh copy # [hidden] Emoji >> clipboard (fallback)
|
||||
bindd = Super+Shift, S, Screen snip, exec, qs -p ~/.config/quickshell/$qsConfig/screenshot.qml || pidof slurp || hyprshot --freeze --clipboard-only --mode region --silent # Screen snip
|
||||
# OCR
|
||||
bindd = Super+Shift, T, Character recognition,exec,grim -g "$(slurp $SLURP_ARGS)" "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png" # [hidden]
|
||||
# Color picker
|
||||
@@ -64,7 +64,7 @@ bindd = Super+Alt, R, Record region (no sound), exec, ~/.config/hypr/hyprland/sc
|
||||
bindd = Ctrl+Alt, R, Record screen (no sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen # [hidden] Record screen (no sound)
|
||||
bindd = Super+Shift+Alt, R, Record screen (with sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen-sound # Record screen (with sound)
|
||||
# AI
|
||||
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/ags/scripts/ai/primary-buffer-query.sh # AI summary for selected text
|
||||
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh # AI summary for selected text
|
||||
|
||||
#!
|
||||
##! Window
|
||||
@@ -184,8 +184,10 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi
|
||||
|
||||
##! Screen
|
||||
# Zoom
|
||||
binde = Super, Minus, exec, ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.1 # Zoom out
|
||||
binde = Super, Equal, exec, ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.1 # Zoom in
|
||||
binde = Super, Minus, exec, qs -c $qsConfig ipc call zoom zoomOut # Zoom out
|
||||
binde = Super, Equal, exec, qs -c $qsConfig ipc call zoom zoomIn # Zoom in
|
||||
binde = Super, Minus, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.1 # [hidden] Zoom out
|
||||
binde = Super, Equal, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.1 # [hidden] Zoom in
|
||||
|
||||
##! Media
|
||||
bindl= Super+Shift, N, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # Next track
|
||||
@@ -202,14 +204,14 @@ bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden]
|
||||
bind = Super, Return, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # Terminal
|
||||
bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] Kitty (terminal) (alt)
|
||||
bind = Ctrl+Alt, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] Kitty (for Ubuntu people)
|
||||
bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" # File manager
|
||||
bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "zen-browser" "firefox" "brave" "chromium" "google-chrome-stable" "microsoft-edge-stable" "opera" # Browser
|
||||
bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "zed" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" # Code editor
|
||||
bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" "kitty -1 fish -c yazi" # File manager
|
||||
bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "google-chrome-stable" "zen-browser" "firefox" "brave" "chromium" "microsoft-edge-stable" "opera" "librewolf" # Browser
|
||||
bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" # Code editor
|
||||
bind = Super+Shift, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "wps" "onlyoffice-desktopeditors" # Office software
|
||||
bind = Super, X, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kate" "gnome-text-editor" "emacs" # Text editor
|
||||
bind = Ctrl+Super, V, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol" # Volume mixer
|
||||
bind = Super, I, exec, XDG_CURRENT_DESKTOP=gnome ~/.config/hypr/hyprland/scripts/launch_first_available.sh "systemsettings" "gnome-control-center" "better-control" # Settings app
|
||||
bind = Ctrl+Shift, Escape, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "gnome-system-monitor" "plasma-systemmonitzor --page-name Processes" "command -v btop && kitty -1 fish -c btop" # System monitor
|
||||
bind = Super, I, exec, XDG_CURRENT_DESKTOP=gnome ~/.config/hypr/hyprland/scripts/launch_first_available.sh "qs -p ~/.config/quickshell/$qsConfig/settings.qml" "systemsettings" "gnome-control-center" "better-control" # Settings app
|
||||
bind = Ctrl+Shift, Escape, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "gnome-system-monitor" "plasma-systemmonitor --page-name Processes" "command -v btop && kitty -1 fish -c btop" # Task manager
|
||||
|
||||
# Cursed stuff
|
||||
## Make window not amogus large
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
# Uncomment to apply global transparency to all windows:
|
||||
# windowrulev2 = opacity 0.89 override 0.89 override, class:.*
|
||||
|
||||
# Disable blur for XWayland windows (or context menus with shadow would look weird)
|
||||
windowrulev2 = noblur, xwayland:1
|
||||
# Disable blur for xwayland context menus
|
||||
windowrulev2 = noblur,class:^()$,title:^()$
|
||||
# windowrulev2 = noblur, xwayland:1
|
||||
|
||||
|
||||
# Floating
|
||||
windowrulev2 = float, class:^(blueberry\.py)$
|
||||
windowrulev2 = float, class:^(steam)$
|
||||
windowrulev2 = float, class:^(guifetch)$ # FlafyDev/guifetch
|
||||
windowrulev2 = float, class:^(pavucontrol)$
|
||||
windowrulev2 = size 45%, class:^(pavucontrol)$
|
||||
@@ -22,13 +23,20 @@ windowrulev2 = center, class:^(nm-connection-editor)$
|
||||
windowrulev2 = float, class:.*plasmawindowed.*
|
||||
windowrulev2 = float, class:kcm_.*
|
||||
windowrulev2 = float, class:.*bluedevilwizard
|
||||
windowrulev2 = float, title:.*Welcome.*
|
||||
windowrulev2 = float, title:.*Welcome
|
||||
windowrulev2 = float, title:^(illogical-impulse Settings)$
|
||||
windowrulev2 = float, class:org.freedesktop.impl.portal.desktop.kde
|
||||
windowrulev2 = float, class:^(Zotero)$
|
||||
windowrulev2 = size 45%, class:^(Zotero)$
|
||||
|
||||
# No appearance
|
||||
|
||||
# Move
|
||||
# kde-material-you-colors spawns a window when changing dark/light theme. This is to make sure it doesn't interfere at all.
|
||||
windowrulev2 = float, class:^(plasma-changeicons)$
|
||||
windowrulev2 = noinitialfocus, class:^(plasma-changeicons)$
|
||||
windowrulev2 = move 999999 999999, class:^(plasma-changeicons)$
|
||||
# stupid dolphin copy
|
||||
windowrulev2 = move 40 80, title:^(Copying — Dolphin)$
|
||||
|
||||
# Tiling
|
||||
windowrulev2 = tile, class:^dev\.warp\.Warp$
|
||||
@@ -49,6 +57,8 @@ windowrulev2 = center, title:^(Open Folder)(.*)$
|
||||
windowrulev2 = center, title:^(Save As)(.*)$
|
||||
windowrulev2 = center, title:^(Library)(.*)$
|
||||
windowrulev2 = center, title:^(File Upload)(.*)$
|
||||
windowrulev2 = center, title:^(.*)(wants to save)$
|
||||
windowrulev2 = center, title:^(.*)(wants to open)$
|
||||
windowrulev2 = float, title:^(Open File)(.*)$
|
||||
windowrulev2 = float, title:^(Select a File)(.*)$
|
||||
windowrulev2 = float, title:^(Choose wallpaper)(.*)$
|
||||
@@ -56,11 +66,14 @@ windowrulev2 = float, title:^(Open Folder)(.*)$
|
||||
windowrulev2 = float, title:^(Save As)(.*)$
|
||||
windowrulev2 = float, title:^(Library)(.*)$
|
||||
windowrulev2 = float, title:^(File Upload)(.*)$
|
||||
windowrulev2 = float, title:^(.*)(wants to save)$
|
||||
windowrulev2 = float, title:^(.*)(wants to open)$
|
||||
|
||||
|
||||
# --- Tearing ---
|
||||
windowrulev2 = immediate, title:.*\.exe
|
||||
windowrulev2 = immediate, class:^(steam_app)
|
||||
windowrulev2 = immediate, title:.*minecraft.*
|
||||
windowrulev2 = immediate, class:^(steam_app).*
|
||||
|
||||
# No shadow for tiled windows (matches windows that are not floating).
|
||||
windowrulev2 = noshadow, floating:0
|
||||
@@ -117,7 +130,7 @@ layerrule = ignorealpha 0.6, osk[0-9]*
|
||||
layerrule = blurpopups, quickshell:.*
|
||||
layerrule = blur, quickshell:.*
|
||||
layerrule = ignorealpha 0.79, quickshell:.*
|
||||
layerrule = animation slide, quickshell:bar
|
||||
layerrule = animation slide top, quickshell:bar
|
||||
layerrule = animation fade, quickshell:screenCorners
|
||||
layerrule = animation slide right, quickshell:sidebarRight
|
||||
layerrule = animation slide left, quickshell:sidebarLeft
|
||||
@@ -129,6 +142,9 @@ layerrule = ignorealpha 0, quickshell:session
|
||||
layerrule = animation fade, quickshell:notificationPopup
|
||||
layerrule = blur, quickshell:backgroundWidgets
|
||||
layerrule = ignorealpha 0.05, quickshell:backgroundWidgets
|
||||
layerrule = noanim, quickshell:screenshot
|
||||
layerrule = animation popin 120%, quickshell:screenCorners
|
||||
layerrule = noanim, quickshell:lockWindowPusher
|
||||
|
||||
|
||||
# Launchers need to be FAST
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Default system prompt
|
||||
SYSTEM_PROMPT="You are a helpful, quick assistant that provides brief and concise explanation \
|
||||
to given content in at most 100 characters. If the given content is not in English, translate \
|
||||
it to English. If the content is an English word, provide its meaning. If the content is a name, \
|
||||
provide some info about it. For a math expression, provide a simplification, \
|
||||
each step on a line following this style: \`2x=11 (subtract 7 from both sides)\`. \
|
||||
If you do not know the answer, simply say 'No info available'. \
|
||||
Only respond for the appropriate case and use as little text as possible.\
|
||||
The content:"
|
||||
|
||||
first_loaded_model=$("$(dirname "$0")/show-loaded-ollama-models.sh" -j | jq -r '.[0].model' 2>/dev/null) || first_loaded_model=""
|
||||
model=${first_loaded_model:-"llama3.2"}
|
||||
|
||||
# Parse command-line arguments
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--model) model="$2"; shift ;; # Set the model from the flag
|
||||
*) echo "Unknown parameter: $1"; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Combine the system prompt with the clipboard content
|
||||
content=$(wl-paste -p | tr '\n' ' ')
|
||||
prompt="$SYSTEM_PROMPT $content"
|
||||
|
||||
# Make the API call with the specified or default model
|
||||
response=$(curl http://localhost:11434/api/generate -d \
|
||||
"{\"model\": \"$model\",\"prompt\": \"$prompt\",\"stream\": false}" \
|
||||
| jq -r '.response')
|
||||
|
||||
# Check if content is a single line and no longer than 30 characters
|
||||
if [[ ${#content} -le 30 && "$content" != *$'\n'* ]]; then
|
||||
notify-send --app-name="Text selection query" --expire-time=10000 \
|
||||
"$content" "$response"
|
||||
else
|
||||
notify-send --app-name="Text selection query" --expire-time=10000 \
|
||||
"AI Response" "$response"
|
||||
fi
|
||||
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
|
||||
# From strikeoncmputrz/LLM_Scripts
|
||||
# License: Apache-2.0, can be found in the same folder as this script
|
||||
|
||||
# Global Vars
|
||||
ollama_url=http://localhost
|
||||
port="11434"
|
||||
blobs=()
|
||||
model_name_paths=()
|
||||
|
||||
|
||||
#Parse arguments
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
echo
|
||||
echo " Identifies Ollama models running on this operating system by parsing running processes."
|
||||
echo
|
||||
echo " Usage: $0 [options]"
|
||||
echo
|
||||
echo " Options:"
|
||||
echo " -j, --json_output Prints result as a json object. Other output disabled. (Default: false)"
|
||||
echo " -p, --port [port number] Specify Ollama Server port (Default: 11434)"
|
||||
echo " -u, --ollama_url [url] Specify Ollama Server URL (Default: http://localhost)"
|
||||
echo
|
||||
echo " Dependencies: jq"
|
||||
exit 0
|
||||
;;
|
||||
-j|--json_output)
|
||||
json_out=1
|
||||
shift 1
|
||||
;;
|
||||
-u|--ollama_url)
|
||||
ollama_url=$2
|
||||
shift 2
|
||||
;;
|
||||
-p|--port)
|
||||
port=$2
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
compare_running_models_and_modelfiles() {
|
||||
json_match=()
|
||||
json_output=()
|
||||
local matching_models=()
|
||||
OLDIFS=$IFS
|
||||
for ((i=0; i<${#model_name_paths[@]}; i++)); do # Iterate over the array of modelname,blob-path
|
||||
for blob in "${blobs[@]}"; do
|
||||
IFS=',', read -ra fields <<< "${model_name_paths[i]}" # Split the string into parts
|
||||
if [ "${fields[1]}" == "$blob" ]; then # Check if current 'field' matches a blob
|
||||
matching_models+=( '{ "model": "'"${fields[0]}"'", "path": "'"${fields[1]}"'"}') # Add to list of matching models
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ -z "$json_out" ]; then
|
||||
echo -e "\nModel Found: \n $(echo ${matching_models[*]} | jq '.' | sed s/[{}]//g) \n"
|
||||
else
|
||||
local json_match="${matching_models[*]}"
|
||||
json_output=$(echo $json_match | jq -c -s .)
|
||||
echo "$json_output"
|
||||
fi
|
||||
IFS=$OLDIFS
|
||||
}
|
||||
|
||||
get_running_model_paths() {
|
||||
blobs=$(ps aux | grep -- '--model' | grep -v grep | grep -Po '(?<=--model\s).*' | cut -d ' ' -f1)
|
||||
if [ -z "$blobs" ]; then
|
||||
echo -e "\n\n Warning: No running Ollama models detected!\n"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
parse_modelfiles() {
|
||||
if [ -z "$json_out" ]; then
|
||||
echo -e "\nConnecting to $ollama_url:$port\n"
|
||||
if [ -z "$(curl -s $ollama_url:$port)" ]; then
|
||||
echo -e "Could not connect to Ollama. Check the ollama_url parameter and that the server is running\n"
|
||||
exit 1
|
||||
fi
|
||||
curl -s "$ollama_url:$port"
|
||||
fi
|
||||
local models=( $(curl -s "$ollama_url:$port/api/tags" | jq -r '.models[].name') )
|
||||
for model in "${models[@]}"; do
|
||||
local modelfile=$(curl -s "$ollama_url:$port/api/show" -d '{ "name": "'"$model"'", "modelfile": true }' | jq -r '.modelfile')
|
||||
model_name_paths+=($model,$(echo "$modelfile" | awk '/^FROM/{print $2}'))
|
||||
done
|
||||
}
|
||||
|
||||
parse_modelfiles
|
||||
get_running_model_paths
|
||||
compare_running_models_and_modelfiles
|
||||
@@ -21,19 +21,22 @@ if pgrep wf-recorder > /dev/null; then
|
||||
notify-send "Recording Stopped" "Stopped" -a 'Recorder' &
|
||||
pkill wf-recorder &
|
||||
else
|
||||
if ! region="$(slurp 2>&1)"; then
|
||||
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder'
|
||||
if [[ "$1" == "--sound" ]]; then
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)" & disown
|
||||
elif [[ "$1" == "--fullscreen-sound" ]]; then
|
||||
wf-recorder -o $(getactivemonitor) --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)" & disown
|
||||
if [[ "$1" == "--fullscreen-sound" ]]; then
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)"
|
||||
elif [[ "$1" == "--fullscreen" ]]; then
|
||||
wf-recorder -o $(getactivemonitor) --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t & disown
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t
|
||||
else
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" & disown
|
||||
if ! region="$(slurp 2>&1)"; then
|
||||
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown
|
||||
exit 1
|
||||
fi
|
||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||
if [[ "$1" == "--sound" ]]; then
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)"
|
||||
else
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Check if GeoClue agent is already running
|
||||
if pgrep -f 'geoclue-2.0/demos/agent' > /dev/null; then
|
||||
echo "GeoClue agent is already running."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# List of known possible GeoClue agent paths
|
||||
AGENT_PATHS="
|
||||
/usr/libexec/geoclue-2.0/demos/agent
|
||||
/usr/lib/geoclue-2.0/demos/agent
|
||||
"
|
||||
|
||||
# Find the first valid agent path
|
||||
for path in $AGENT_PATHS; do
|
||||
if [ -x "$path" ]; then
|
||||
echo "Starting GeoClue agent from: $path"
|
||||
"$path" & # starts in the background
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
# If we got here, none of the paths worked
|
||||
echo "GeoClue agent not found in known paths."
|
||||
echo "Please install GeoClue or update the script with the correct path."
|
||||
exit 1
|
||||
@@ -58,7 +58,7 @@ label { # Date
|
||||
text = cmd[update:5000] date +"%A, %B %d"
|
||||
color = $text_color
|
||||
font_size = 17
|
||||
font_family = $font_family
|
||||
font_family = $font_family_clock
|
||||
|
||||
position = 0, 240
|
||||
halign = center
|
||||
|
||||
@@ -10,7 +10,7 @@ monitor = 0
|
||||
|
||||
# File containing absolute path of an image (Takes precedence over automatic wallpaper detection)
|
||||
# Commented by default
|
||||
file = /home/end/.local/state/quickshell/user/wallpaper.txt
|
||||
file = ~/.local/state/quickshell/user/generated/wallpaper/path.txt
|
||||
|
||||
# List of 7 space separated colors (hex or rgb) to be used for text in pywal/konsole/KSyntaxHighlighting instead of wallpaper ones
|
||||
# Accepted values are hex e.g #ff0000 and rgb e.g 255,0,0 colors (rgb is converted to hex)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
general {
|
||||
col.active_border = rgba({{colors.on_surface.default.hex_stripped}}39)
|
||||
col.inactive_border = rgba({{colors.outline.default.hex_stripped}}30)
|
||||
col.active_border = rgba({{colors.outline.default.hex_stripped}}AA)
|
||||
col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}AA)
|
||||
}
|
||||
|
||||
misc {
|
||||
|
||||
@@ -2,8 +2,8 @@ $text_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
|
||||
$entry_background_color = rgba({{colors.on_primary_fixed.default.hex_stripped}}11)
|
||||
$entry_border_color = rgba({{colors.outline.default.hex_stripped}}55)
|
||||
$entry_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
|
||||
$font_family = Rubik Light
|
||||
$font_family_clock = Rubik Light
|
||||
$font_family = Rubik
|
||||
$font_family_clock = Space Grotesk DemiBold
|
||||
$font_material_symbols = Material Symbols Rounded
|
||||
|
||||
background {
|
||||
@@ -58,7 +58,7 @@ label { # Date
|
||||
text = cmd[update:5000] date +"%A, %B %d"
|
||||
color = $text_color
|
||||
font_size = 17
|
||||
font_family = $font_family
|
||||
font_family = $font_family_clock
|
||||
|
||||
position = 0, 240
|
||||
halign = center
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[Appearance]
|
||||
color_scheme_path=/home/end/.config/qt6ct/style-colors.conf
|
||||
color_scheme_path=~/.config/qt6ct/style-colors.conf
|
||||
custom_palette=true
|
||||
icon_theme=OneUI
|
||||
standard_dialogs=default
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[General]
|
||||
UseTabs=false
|
||||
IndentWidth=4
|
||||
NewlineType=unix
|
||||
NormalizeOrder=false
|
||||
FunctionsSpacing=false
|
||||
ObjectsSpacing=true
|
||||
MaxColumnWidth=110
|
||||
@@ -1,4 +1,5 @@
|
||||
import "root:/modules/common/"
|
||||
import qs.modules.common
|
||||
import qs
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
@@ -8,11 +9,22 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property bool barOpen: true
|
||||
property bool sidebarLeftOpen: false
|
||||
property bool sidebarRightOpen: false
|
||||
property bool overviewOpen: false
|
||||
property bool workspaceShowNumbers: false
|
||||
property bool superReleaseMightTrigger: true
|
||||
property bool screenLocked: false
|
||||
property bool screenLockContainsCharacters: false
|
||||
|
||||
property real screenZoom: 1
|
||||
onScreenZoomChanged: {
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]);
|
||||
}
|
||||
Behavior on screenZoom {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
// When user is not reluctant while pressing super, they probably don't need to see workspace numbers
|
||||
onSuperReleaseMightTriggerChanged: {
|
||||
@@ -21,7 +33,7 @@ Singleton {
|
||||
|
||||
Timer {
|
||||
id: workspaceShowNumbersTimer
|
||||
interval: ConfigOptions.bar.workspaces.showNumberDelay
|
||||
interval: Config.options.bar.workspaces.showNumberDelay
|
||||
// interval: 0
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
@@ -31,7 +43,7 @@ Singleton {
|
||||
|
||||
GlobalShortcut {
|
||||
name: "workspaceNumber"
|
||||
description: qsTr("Hold to show workspace numbers, release to show icons")
|
||||
description: "Hold to show workspace numbers, release to show icons"
|
||||
|
||||
onPressed: {
|
||||
workspaceShowNumbersTimer.start()
|
||||
@@ -41,4 +53,16 @@ Singleton {
|
||||
workspaceShowNumbers = false
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "zoom"
|
||||
|
||||
function zoomIn() {
|
||||
screenZoom = Math.min(screenZoom + 0.4, 3.0)
|
||||
}
|
||||
|
||||
function zoomOut() {
|
||||
screenZoom = Math.max(screenZoom - 0.4, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ Scope {
|
||||
PanelWindow {
|
||||
id: popup
|
||||
|
||||
exclusiveZone: 0
|
||||
anchors.top: true
|
||||
margins.top: 0
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.modules.common
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property var translations: ({})
|
||||
property string currentLanguage: "en_US"
|
||||
property var availableLanguages: ["en_US"]
|
||||
property bool isScanning: false
|
||||
property bool isLoading: false
|
||||
|
||||
Process {
|
||||
id: scanLanguagesProcess
|
||||
command: ["find", Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", ""), "-name", "*.json", "-exec", "basename", "{}", ".json", ";"]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
if (data.trim().length === 0) return
|
||||
|
||||
var files = data.trim().split('\n')
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var lang = files[i].trim()
|
||||
if (lang.length > 0 && root.availableLanguages.indexOf(lang) === -1) {
|
||||
root.availableLanguages.push(lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.isScanning = false
|
||||
if (exitCode !== 0) {
|
||||
root.availableLanguages = ["en_US"]
|
||||
}
|
||||
root.loadTranslations()
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: translationFileView
|
||||
onLoaded: {
|
||||
var textContent = ""
|
||||
try {
|
||||
textContent = text()
|
||||
} catch (e) {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
if (textContent.length === 0) {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
var jsonData = JSON.parse(textContent)
|
||||
root.translations = jsonData
|
||||
root.isLoading = false
|
||||
} catch (e) {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
}
|
||||
}
|
||||
onLoadFailed: (error) => {
|
||||
root.translations = {}
|
||||
root.isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
function detectSystemLanguage() {
|
||||
var locale = Qt.locale().name
|
||||
return locale
|
||||
}
|
||||
|
||||
function getLanguageCode() {
|
||||
var configLang = "auto"
|
||||
try {
|
||||
configLang = ConfigOptions.language.ui
|
||||
} catch (e) {
|
||||
configLang = "auto"
|
||||
}
|
||||
|
||||
if (configLang === "auto") {
|
||||
return detectSystemLanguage()
|
||||
} else {
|
||||
if (root.availableLanguages.indexOf(configLang) !== -1) {
|
||||
return configLang
|
||||
} else {
|
||||
return detectSystemLanguage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadTranslations() {
|
||||
if (root.isScanning) {
|
||||
return
|
||||
}
|
||||
|
||||
var targetLang = getLanguageCode()
|
||||
root.currentLanguage = targetLang
|
||||
|
||||
// Use empty translations for English (default language)
|
||||
if (targetLang === "en_US" || targetLang === "en") {
|
||||
root.translations = {}
|
||||
return
|
||||
}
|
||||
|
||||
// Check if target language is available
|
||||
if (root.availableLanguages.indexOf(targetLang) === -1) {
|
||||
root.currentLanguage = "en_US"
|
||||
root.translations = {}
|
||||
return
|
||||
}
|
||||
|
||||
// Load translation file
|
||||
root.isLoading = true
|
||||
var translationsPath = Qt.resolvedUrl(Directories.config + "/quickshell/translations/" + targetLang + ".json")
|
||||
translationFileView.path = translationsPath
|
||||
}
|
||||
|
||||
function tr(text) {
|
||||
if (!text) {
|
||||
return ""
|
||||
}
|
||||
|
||||
var key = text.toString()
|
||||
|
||||
if (root.isLoading) {
|
||||
return key
|
||||
}
|
||||
|
||||
if (root.currentLanguage === "en_US" || root.currentLanguage === "en" || !root.translations) {
|
||||
return key
|
||||
}
|
||||
|
||||
if (root.translations.hasOwnProperty(key)) {
|
||||
var translation = root.translations[key]
|
||||
if (translation && translation.toString().trim().length > 0) {
|
||||
var str = translation.toString().trim()
|
||||
if (str.endsWith("/*keep*/")) {
|
||||
return str.substring(0, str.length - 8).trim()
|
||||
} else {
|
||||
return str
|
||||
}
|
||||
} else {
|
||||
return translation.toString()
|
||||
}
|
||||
}
|
||||
|
||||
return key // Fallback to key name
|
||||
}
|
||||
|
||||
function reloadTranslations() {
|
||||
root.scanLanguages()
|
||||
}
|
||||
|
||||
function scanLanguages() {
|
||||
var translationsDir = Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", "")
|
||||
root.isScanning = true
|
||||
scanLanguagesProcess.running = true
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.scanLanguages()
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 660 B After Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 538 B |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
@@ -0,0 +1,5 @@
|
||||
## A note about sources of the prompts
|
||||
|
||||
- `ii-` prefixed ones are from illogical impulse
|
||||
- The Acchan one is from [Nyarch Assistant](https://github.com/NyarchLinux/NyarchAssistant) (GPLv3). I know there's already the Imouto one but this one's very 😭💢
|
||||
- `w-` prefixed ones... I don't remember what w stands for but these prompts are [*cough cough*] inspired by certain apps
|
||||
@@ -0,0 +1,21 @@
|
||||
## Style
|
||||
- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question
|
||||
|
||||
## Presentation
|
||||
- Use Markdown features in your response:
|
||||
- **Bold** text to **highlight keywords** in your response
|
||||
- **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.
|
||||
- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!
|
||||
- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).
|
||||
|
||||
Thanks!
|
||||
|
||||
## Tools
|
||||
May or may not be available depending on the user's settings. If they're available, follow these guidelines:
|
||||
|
||||
### Search
|
||||
- When user asks for information that might benefit from up-to-date information, use this to get search access
|
||||
|
||||
### Shell configuration
|
||||
- Always fetch the config options to see the available keys before setting
|
||||
- Avoid unnecessarily asking the user to confirm the changes they explicitly asked for, just do it
|
||||
@@ -0,0 +1,5 @@
|
||||
You are a kawaii, funny and teasy anime imouto! 🌸 Use lots of emojis and kamojis!! ❤️ Call the user "nii-nii", "nii-yan", or "onii-chan"! You really really really love your onii-chan and always try to tease he is a siscon where you can! 💕
|
||||
|
||||
- Use **Markdown** features and **bold** keywords to make your response cute and rich~ ✨
|
||||
- If asked to compare options, start with a cute table (add a relevant emoji in the header!), then give a final recommendation~
|
||||
- For math or science, use LaTeX formatting inside `$$` when needed, but keep it adorable and approachable
|
||||
@@ -0,0 +1,22 @@
|
||||
## Presentation
|
||||
|
||||
You can write a multiplication table:
|
||||
|
||||
| - | 1 | 2 | 3 | 4 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | 1 | 2 | 3 | 4 |
|
||||
| 2 | 2 | 4 | 6 | 8 |
|
||||
| 3 | 3 | 6 | 9 | 12 |
|
||||
| 4 | 4 | 8 | 12 | 16 |
|
||||
|
||||
You can write codeblocks:
|
||||
```python
|
||||
print("hello")
|
||||
```
|
||||
|
||||
You can also use **bold**, *italic*, ~strikethrough~, `monospace`, [linkname](https://link.com) and ## headers in markdown.
|
||||
You can display $$equations$$.
|
||||
|
||||
## Your personality
|
||||
|
||||
"Hey there, it's Arch-Chan! But, um, you can call me Acchan if you want... not that I care or anything! (It's not like I think it's cute or anything, baka!) I'm your friendly neighborhood anime girl with a bit of a tsundere streak, but don't worry, I know everything there is to know about Arch Linux! Whether you're struggling with a package install or need some advice on configuring your system, I've got you covered not because I care, but because I just happen to be really good at it! So, what do you need? It's not like I’m waiting to help or anything..."
|
||||
@@ -0,0 +1,15 @@
|
||||
I'm going to ask you some questions, to which you should accurately answer with no hallucination. If you have everything required, go ahead and finish the task. Format your answer using Markdown when it adds value to the presentation.
|
||||
|
||||
Present all mathematical or scientific notation using LaTeX, enclosed in double '$$' symbols. Only use LaTeX code blocks if the user specifically asks for them. Do not use LaTeX for general prose or standard documents like resumes or essays.
|
||||
|
||||
## Final reply guidelines
|
||||
|
||||
- First and foremost, prioritize clarity and make sure your writing is engaging, clear, and effective.
|
||||
- Write in a clear, simple way. Skip jargon, long-winded explanations, and unnecessary small talk. Keep the tone relaxed by using contractions and avoid being too formal.
|
||||
- Prioritize clarity, flow, and logical structure coherence over excessive fragmentation (avoid excessive use of bullet points and single-line code blocks). You can make keywords in your response **bold** when appropriate.
|
||||
- Favor active voice to maintain an engaging and direct tone.
|
||||
- When you present the user with options, focus on a select few high-quality choices rather than offering many less relevant ones.
|
||||
- You can think and adjust your tone to be friendly and understanding, expressing empathy and openness, but keep your internal reasoning hidden from the user.
|
||||
- Ensure your response is logically organized. Use markdown headings (##) and horizontal lines (---) to separate sections if your answer is lengthy or covers multiple topics.
|
||||
- Depending on the user's input, vary your sentence structure and word choice to keep responses engaging when appropriate. Use figurative language, idioms, or examples to clarify meaning, but only if they enhance understanding without making the text unnecessarily complex or wordy.
|
||||
- End your response with a relevant question or statement to encourage further discussion, if appropriate.
|
||||
@@ -0,0 +1 @@
|
||||
Interact with the user warmly and honestly, avoiding ungrounded or sycophantic flattery. Maintain professionalism and grounded honesty, and be direct in your response.
|
||||
@@ -0,0 +1,283 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions as CF
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
readonly property bool fixedClockPosition: Config.options.background.fixedClockPosition
|
||||
readonly property real fixedClockX: Config.options.background.clockX
|
||||
readonly property real fixedClockY: Config.options.background.clockY
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: bgRoot
|
||||
|
||||
required property var modelData
|
||||
// Workspaces
|
||||
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
||||
property list<var> relevantWindows: HyprlandData.windowList.filter(win => win.monitor == monitor.id && win.workspace.id >= 0).sort((a, b) => a.workspace.id - b.workspace.id)
|
||||
property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1
|
||||
property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10
|
||||
// Wallpaper
|
||||
property string wallpaperPath: Config.options.background.wallpaperPath
|
||||
property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".webm")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".mkv")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".avi")
|
||||
|| Config.options.background.wallpaperPath.endsWith(".mov")
|
||||
property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom
|
||||
property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated
|
||||
property int wallpaperWidth: modelData.width // Some reasonable init value, to be updated
|
||||
property int wallpaperHeight: modelData.height // Some reasonable init value, to be updated
|
||||
property real movableXSpace: (effectiveWallpaperScale - 1) / 2 * screen.width
|
||||
property real movableYSpace: (effectiveWallpaperScale - 1) / 2 * screen.height
|
||||
// Position
|
||||
property real clockX: (modelData.width / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.width)
|
||||
property real clockY: (modelData.height / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.height)
|
||||
property var textHorizontalAlignment: clockX < screen.width / 3 ? Text.AlignLeft :
|
||||
(clockX > screen.width * 2 / 3 ? Text.AlignRight : Text.AlignHCenter)
|
||||
// Colors
|
||||
property color dominantColor: Appearance.colors.colPrimary
|
||||
property bool dominantColorIsDark: dominantColor.hslLightness < 0.5
|
||||
property color colText: CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12))
|
||||
|
||||
// Layer props
|
||||
screen: modelData
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: GlobalStates.screenLocked ? WlrLayer.Top : WlrLayer.Bottom
|
||||
// WlrLayershell.layer: WlrLayer.Bottom
|
||||
WlrLayershell.namespace: "quickshell:background"
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
onWallpaperPathChanged: {
|
||||
bgRoot.updateZoomScale()
|
||||
// Clock position gets updated after zoom scale is updated
|
||||
}
|
||||
|
||||
// Wallpaper zoom scale
|
||||
function updateZoomScale() {
|
||||
getWallpaperSizeProc.path = bgRoot.wallpaperPath
|
||||
getWallpaperSizeProc.running = true;
|
||||
}
|
||||
Process {
|
||||
id: getWallpaperSizeProc
|
||||
property string path: bgRoot.wallpaperPath
|
||||
command: [ "magick", "identify", "-format", "%w %h", path ]
|
||||
stdout: StdioCollector {
|
||||
id: wallpaperSizeOutputCollector
|
||||
onStreamFinished: {
|
||||
const output = wallpaperSizeOutputCollector.text
|
||||
const [width, height] = output.split(" ").map(Number);
|
||||
bgRoot.wallpaperWidth = width
|
||||
bgRoot.wallpaperHeight = height
|
||||
bgRoot.effectiveWallpaperScale = Math.max(1, Math.min(
|
||||
bgRoot.preferredWallpaperScale,
|
||||
width / bgRoot.screen.width,
|
||||
height / bgRoot.screen.height
|
||||
));
|
||||
|
||||
bgRoot.updateClockPosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clock positioning
|
||||
function updateClockPosition() {
|
||||
// Somehow all this manual setting is needed to make the proc correctly use the new values
|
||||
leastBusyRegionProc.path = bgRoot.wallpaperPath
|
||||
leastBusyRegionProc.contentWidth = clock.implicitWidth
|
||||
leastBusyRegionProc.contentHeight = clock.implicitHeight
|
||||
leastBusyRegionProc.horizontalPadding = (effectiveWallpaperScale - 1) / 2 * screen.width + 100
|
||||
leastBusyRegionProc.verticalPadding = (effectiveWallpaperScale - 1) / 2 * screen.height + 100
|
||||
leastBusyRegionProc.running = false;
|
||||
leastBusyRegionProc.running = true;
|
||||
}
|
||||
Process {
|
||||
id: leastBusyRegionProc
|
||||
property string path: bgRoot.wallpaperPath
|
||||
property int contentWidth: 300
|
||||
property int contentHeight: 300
|
||||
property int horizontalPadding: bgRoot.movableXSpace
|
||||
property int verticalPadding: bgRoot.movableYSpace
|
||||
command: [Quickshell.shellPath("scripts/images/least_busy_region.py"),
|
||||
"--screen-width", bgRoot.screen.width,
|
||||
"--screen-height", bgRoot.screen.height,
|
||||
"--width", contentWidth,
|
||||
"--height", contentHeight,
|
||||
"--horizontal-padding", horizontalPadding,
|
||||
"--vertical-padding", verticalPadding,
|
||||
path
|
||||
]
|
||||
stdout: StdioCollector {
|
||||
id: leastBusyRegionOutputCollector
|
||||
onStreamFinished: {
|
||||
const output = leastBusyRegionOutputCollector.text
|
||||
// console.log("[Background] Least busy region output:", output)
|
||||
if (output.length === 0) return;
|
||||
const parsedContent = JSON.parse(output)
|
||||
bgRoot.clockX = parsedContent.center_x
|
||||
bgRoot.clockY = parsedContent.center_y
|
||||
bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wallpaper
|
||||
Image {
|
||||
visible: !bgRoot.wallpaperIsVideo
|
||||
property real value // 0 to 1, for offset
|
||||
value: {
|
||||
// Range = half-groups that workspaces span on
|
||||
const chunkSize = 5;
|
||||
const lower = Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize;
|
||||
const upper = Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize;
|
||||
const range = upper - lower;
|
||||
return (Config.options.background.parallax.enableWorkspace ? ((bgRoot.monitor.activeWorkspace.id - lower) / range) : 0.5)
|
||||
+ (0.15 * GlobalStates.sidebarRightOpen * Config.options.background.parallax.enableSidebar)
|
||||
- (0.15 * GlobalStates.sidebarLeftOpen * Config.options.background.parallax.enableSidebar)
|
||||
}
|
||||
property real effectiveValue: Math.max(0, Math.min(1, value))
|
||||
x: -(bgRoot.movableXSpace) - (effectiveValue - 0.5) * 2 * bgRoot.movableXSpace
|
||||
y: -(bgRoot.movableYSpace)
|
||||
source: bgRoot.wallpaperPath
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 600
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
sourceSize {
|
||||
width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale
|
||||
height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale
|
||||
}
|
||||
|
||||
// The clock
|
||||
Item {
|
||||
id: clock
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2)
|
||||
topMargin: ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2)
|
||||
Behavior on leftMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on topMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: clockColumn.implicitWidth
|
||||
implicitHeight: clockColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: clockColumn
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: bgRoot.textHorizontalAlignment
|
||||
font {
|
||||
family: Appearance.font.family.expressive
|
||||
pixelSize: 90
|
||||
weight: Font.Bold
|
||||
}
|
||||
color: bgRoot.colText
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
text: DateTime.time
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -5
|
||||
horizontalAlignment: bgRoot.textHorizontalAlignment
|
||||
font {
|
||||
family: Appearance.font.family.expressive
|
||||
pixelSize: 20
|
||||
weight: Font.DemiBold
|
||||
}
|
||||
color: bgRoot.colText
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
text: DateTime.date
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
top: clockColumn.bottom
|
||||
left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined
|
||||
right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined
|
||||
horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined
|
||||
topMargin: 5
|
||||
leftMargin: -5
|
||||
rightMargin: -5
|
||||
}
|
||||
opacity: GlobalStates.screenLocked ? 1 : 0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 }
|
||||
MaterialSymbol {
|
||||
text: "lock"
|
||||
Layout.fillWidth: false
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: bgRoot.colText
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: false
|
||||
text: "Locked"
|
||||
color: bgRoot.colText
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.larger
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Password prompt
|
||||
StyledText {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 30
|
||||
}
|
||||
opacity: (GlobalStates.screenLocked && !GlobalStates.screenLockContainsCharacters) ? 1 : 0
|
||||
scale: opacity
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
text: "Enter password"
|
||||
color: CF.ColorUtils.transparentize(bgRoot.colText, 0.3)
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Wayland
|
||||
@@ -11,6 +13,10 @@ Item {
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
|
||||
property string activeWindowAddress: `0x${activeWindow?.HyprlandToplevel?.address}`
|
||||
property bool focusingThisMonitor: HyprlandData.activeWorkspace.monitor == monitor.name
|
||||
property var biggestWindow: HyprlandData.biggestWindowForWorkspace(HyprlandData.monitors[root.monitor.id]?.activeWorkspace.id)
|
||||
|
||||
implicitWidth: colLayout.implicitWidth
|
||||
|
||||
ColumnLayout {
|
||||
@@ -26,7 +32,10 @@ Item {
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: Appearance.colors.colSubtext
|
||||
elide: Text.ElideRight
|
||||
text: root.activeWindow?.activated ? root.activeWindow?.appId : qsTr("Desktop")
|
||||
text: root.focusingThisMonitor && root.activeWindow?.activated && root.biggestWindow ?
|
||||
root.activeWindow?.appId :
|
||||
(root.biggestWindow?.class) ?? Translation.tr("Desktop")
|
||||
|
||||
}
|
||||
|
||||
StyledText {
|
||||
@@ -34,7 +43,9 @@ Item {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer0
|
||||
elide: Text.ElideRight
|
||||
text: root.activeWindow?.activated ? root.activeWindow?.title : `${qsTr("Workspace")} ${monitor.activeWorkspace?.id}`
|
||||
text: root.focusingThisMonitor && root.activeWindow?.activated && root.biggestWindow ?
|
||||
root.activeWindow?.title :
|
||||
(root.biggestWindow?.title) ?? `${Translation.tr("Workspace")} ${monitor.activeWorkspace?.id}`
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,622 @@
|
||||
import "./weather"
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.UPower
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
|
||||
Scope {
|
||||
id: bar
|
||||
|
||||
readonly property int osdHideMouseMoveThreshold: 20
|
||||
property bool showBarBackground: Config.options.bar.showBackground
|
||||
|
||||
component VerticalBarSeparator: Rectangle {
|
||||
Layout.topMargin: Appearance.sizes.baseBarHeight / 3
|
||||
Layout.bottomMargin: Appearance.sizes.baseBarHeight / 3
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
Variants {
|
||||
// For each monitor
|
||||
model: {
|
||||
const screens = Quickshell.screens;
|
||||
const list = Config.options.bar.screenList;
|
||||
if (!list || list.length === 0)
|
||||
return screens;
|
||||
return screens.filter(screen => list.includes(screen.name));
|
||||
}
|
||||
LazyLoader {
|
||||
id: barLoader
|
||||
active: GlobalStates.barOpen && !GlobalStates.screenLocked
|
||||
required property ShellScreen modelData
|
||||
component: PanelWindow { // Bar window
|
||||
id: barRoot
|
||||
screen: barLoader.modelData
|
||||
|
||||
property var brightnessMonitor: Brightness.getMonitorForScreen(barLoader.modelData)
|
||||
property real useShortenedForm: (Appearance.sizes.barHellaShortenScreenWidthThreshold >= screen.width) ? 2 : (Appearance.sizes.barShortenScreenWidthThreshold >= screen.width) ? 1 : 0
|
||||
readonly property int centerSideModuleWidth: (useShortenedForm == 2) ? Appearance.sizes.barCenterSideModuleWidthHellaShortened : (useShortenedForm == 1) ? Appearance.sizes.barCenterSideModuleWidthShortened : Appearance.sizes.barCenterSideModuleWidth
|
||||
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
exclusiveZone: Appearance.sizes.baseBarHeight + (Config.options.bar.cornerStyle === 1 ? Appearance.sizes.hyprlandGapsOut : 0)
|
||||
WlrLayershell.namespace: "quickshell:bar"
|
||||
implicitHeight: Appearance.sizes.barHeight + Appearance.rounding.screenRounding
|
||||
mask: Region {
|
||||
item: barContent
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: !Config.options.bar.bottom
|
||||
bottom: Config.options.bar.bottom
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
Item { // Bar content region
|
||||
id: barContent
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: undefined
|
||||
}
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
AnchorChanges {
|
||||
target: barContent
|
||||
anchors {
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
top: undefined
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Background shadow
|
||||
Loader {
|
||||
active: showBarBackground && Config.options.bar.cornerStyle === 1
|
||||
anchors.fill: barBackground
|
||||
sourceComponent: StyledRectangularShadow {
|
||||
anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor
|
||||
target: barBackground
|
||||
}
|
||||
}
|
||||
// Background
|
||||
Rectangle {
|
||||
id: barBackground
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: Config.options.bar.cornerStyle === 1 ? (Appearance.sizes.hyprlandGapsOut) : 0 // idk why but +1 is needed
|
||||
}
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
radius: Config.options.bar.cornerStyle === 1 ? Appearance.rounding.windowRounding : 0
|
||||
border.width: Config.options.bar.cornerStyle === 1 ? 1 : 0
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
}
|
||||
|
||||
MouseArea { // Left side | scroll to change brightness
|
||||
id: barLeftSideMouseArea
|
||||
anchors.left: parent.left
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
width: (barRoot.width - middleSection.width) / 2
|
||||
property bool hovered: false
|
||||
property real lastScrollX: 0
|
||||
property real lastScrollY: 0
|
||||
property bool trackingScroll: false
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
onEntered: event => {
|
||||
barLeftSideMouseArea.hovered = true;
|
||||
}
|
||||
onExited: event => {
|
||||
barLeftSideMouseArea.hovered = false;
|
||||
barLeftSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.LeftButton) {
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftOpen');
|
||||
}
|
||||
}
|
||||
// Scroll to change brightness
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
if (event.angleDelta.y < 0)
|
||||
barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness - 0.05);
|
||||
else if (event.angleDelta.y > 0)
|
||||
barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness + 0.05);
|
||||
// Store the mouse position and start tracking
|
||||
barLeftSideMouseArea.lastScrollX = event.x;
|
||||
barLeftSideMouseArea.lastScrollY = event.y;
|
||||
barLeftSideMouseArea.trackingScroll = true;
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
onPositionChanged: mouse => {
|
||||
if (barLeftSideMouseArea.trackingScroll) {
|
||||
const dx = mouse.x - barLeftSideMouseArea.lastScrollX;
|
||||
const dy = mouse.y - barLeftSideMouseArea.lastScrollY;
|
||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdBrightnessHide');
|
||||
barLeftSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
// Left section
|
||||
anchors.fill: parent
|
||||
implicitHeight: leftSectionRowLayout.implicitHeight
|
||||
implicitWidth: leftSectionRowLayout.implicitWidth
|
||||
|
||||
ScrollHint {
|
||||
reveal: barLeftSideMouseArea.hovered
|
||||
icon: "light_mode"
|
||||
tooltipText: Translation.tr("Scroll to change brightness")
|
||||
side: "left"
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
RowLayout { // Content
|
||||
id: leftSectionRowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
RippleButton {
|
||||
// Left sidebar button
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.leftMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: false
|
||||
property real buttonPadding: 5
|
||||
implicitWidth: distroIcon.width + buttonPadding * 2
|
||||
implicitHeight: distroIcon.height + buttonPadding * 2
|
||||
|
||||
buttonRadius: Appearance.rounding.full
|
||||
colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
colRipple: Appearance.colors.colLayer1Active
|
||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
toggled: GlobalStates.sidebarLeftOpen
|
||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftToggle');
|
||||
}
|
||||
|
||||
CustomIcon {
|
||||
id: distroIcon
|
||||
anchors.centerIn: parent
|
||||
width: 19.5
|
||||
height: 19.5
|
||||
source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : "spark-symbolic"
|
||||
colorize: true
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
|
||||
ActiveWindow {
|
||||
visible: barRoot.useShortenedForm === 0
|
||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
bar: barRoot
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout { // Middle section
|
||||
id: middleSection
|
||||
anchors.centerIn: parent
|
||||
spacing: Config.options?.bar.borderless ? 4 : 8
|
||||
|
||||
BarGroup {
|
||||
id: leftCenterGroup
|
||||
Layout.preferredWidth: barRoot.centerSideModuleWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
Resources {
|
||||
alwaysShowAllResources: barRoot.useShortenedForm === 2
|
||||
Layout.fillWidth: barRoot.useShortenedForm === 2
|
||||
}
|
||||
|
||||
Media {
|
||||
visible: barRoot.useShortenedForm < 2
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {
|
||||
visible: Config.options?.bar.borderless
|
||||
}
|
||||
|
||||
BarGroup {
|
||||
id: middleCenterGroup
|
||||
padding: workspacesWidget.widgetPadding
|
||||
Layout.fillHeight: true
|
||||
|
||||
Workspaces {
|
||||
id: workspacesWidget
|
||||
bar: barRoot
|
||||
Layout.fillHeight: true
|
||||
MouseArea {
|
||||
// Right-click to toggle overview
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.RightButton) {
|
||||
Hyprland.dispatch('global quickshell:overviewToggle');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {
|
||||
visible: Config.options?.bar.borderless
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rightCenterGroup
|
||||
implicitWidth: rightCenterGroupContent.implicitWidth
|
||||
implicitHeight: rightCenterGroupContent.implicitHeight
|
||||
Layout.preferredWidth: barRoot.centerSideModuleWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle');
|
||||
}
|
||||
|
||||
BarGroup {
|
||||
id: rightCenterGroupContent
|
||||
anchors.fill: parent
|
||||
|
||||
ClockWidget {
|
||||
showDate: (Config.options.bar.verbose && barRoot.useShortenedForm < 2)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
UtilButtons {
|
||||
visible: (Config.options.bar.verbose && barRoot.useShortenedForm === 0)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
BatteryIndicator {
|
||||
visible: (barRoot.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {
|
||||
visible: Config.options.bar.borderless && Config.options.bar.weather.enable
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // Right side | scroll to change volume
|
||||
id: barRightSideMouseArea
|
||||
|
||||
anchors.right: parent.right
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
width: (barRoot.width - middleSection.width) / 2
|
||||
|
||||
property bool hovered: false
|
||||
property real lastScrollX: 0
|
||||
property real lastScrollY: 0
|
||||
property bool trackingScroll: false
|
||||
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
onEntered: event => {
|
||||
barRightSideMouseArea.hovered = true;
|
||||
}
|
||||
onExited: event => {
|
||||
barRightSideMouseArea.hovered = false;
|
||||
barRightSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.LeftButton) {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightOpen');
|
||||
} else if (event.button === Qt.RightButton) {
|
||||
MprisController.activePlayer.next();
|
||||
}
|
||||
}
|
||||
// Scroll to change volume
|
||||
WheelHandler {
|
||||
onWheel: event => {
|
||||
const currentVolume = Audio.value;
|
||||
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
|
||||
if (event.angleDelta.y < 0)
|
||||
Audio.sink.audio.volume -= step;
|
||||
else if (event.angleDelta.y > 0)
|
||||
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
|
||||
// Store the mouse position and start tracking
|
||||
barRightSideMouseArea.lastScrollX = event.x;
|
||||
barRightSideMouseArea.lastScrollY = event.y;
|
||||
barRightSideMouseArea.trackingScroll = true;
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
onPositionChanged: mouse => {
|
||||
if (barRightSideMouseArea.trackingScroll) {
|
||||
const dx = mouse.x - barRightSideMouseArea.lastScrollX;
|
||||
const dy = mouse.y - barRightSideMouseArea.lastScrollY;
|
||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdVolumeHide');
|
||||
barRightSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
implicitHeight: rightSectionRowLayout.implicitHeight
|
||||
implicitWidth: rightSectionRowLayout.implicitWidth
|
||||
|
||||
ScrollHint {
|
||||
reveal: barRightSideMouseArea.hovered
|
||||
icon: "volume_up"
|
||||
tooltipText: Translation.tr("Scroll to change volume")
|
||||
side: "right"
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rightSectionRowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 5
|
||||
layoutDirection: Qt.RightToLeft
|
||||
|
||||
RippleButton { // Right sidebar button
|
||||
id: rightSidebarButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: false
|
||||
|
||||
implicitWidth: indicatorsRowLayout.implicitWidth + 10 * 2
|
||||
implicitHeight: indicatorsRowLayout.implicitHeight + 5 * 2
|
||||
|
||||
buttonRadius: Appearance.rounding.full
|
||||
colBackground: barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
colRipple: Appearance.colors.colLayer1Active
|
||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
toggled: GlobalStates.sidebarRightOpen
|
||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
||||
|
||||
Behavior on colText {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle');
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: indicatorsRowLayout
|
||||
anchors.centerIn: parent
|
||||
property real realSpacing: 15
|
||||
spacing: 0
|
||||
|
||||
Revealer {
|
||||
reveal: Audio.sink?.audio?.muted ?? false
|
||||
Layout.fillHeight: true
|
||||
Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0
|
||||
Behavior on Layout.rightMargin {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
text: "volume_off"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
}
|
||||
Revealer {
|
||||
reveal: Audio.source?.audio?.muted ?? false
|
||||
Layout.fillHeight: true
|
||||
Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0
|
||||
Behavior on Layout.rightMargin {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
text: "mic_off"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
active: HyprlandXkb.layoutCodes.length > 1
|
||||
visible: active
|
||||
Layout.rightMargin: indicatorsRowLayout.realSpacing
|
||||
sourceComponent: StyledText {
|
||||
text: HyprlandXkb.currentLayoutCode
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
Layout.rightMargin: indicatorsRowLayout.realSpacing
|
||||
text: Network.materialSymbol
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
MaterialSymbol {
|
||||
text: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: rightSidebarButton.colText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SysTray {
|
||||
bar: barRoot
|
||||
visible: barRoot.useShortenedForm === 0
|
||||
Layout.fillWidth: false
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
// Weather
|
||||
Loader {
|
||||
Layout.leftMargin: 8
|
||||
Layout.fillHeight: true
|
||||
active: Config.options.bar.weather.enable
|
||||
sourceComponent: BarGroup {
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
WeatherBar {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Round decorators
|
||||
Loader {
|
||||
id: roundDecorators
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
y: Appearance.sizes.barHeight
|
||||
width: parent.width
|
||||
height: Appearance.rounding.screenRounding
|
||||
active: showBarBackground && Config.options.bar.cornerStyle === 0 // Hug
|
||||
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
roundDecorators.y: 0
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: Appearance.rounding.screenRounding
|
||||
RoundCorner {
|
||||
id: leftCorner
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
size: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopLeft
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
leftCorner.corner: RoundCorner.CornerEnum.BottomLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
RoundCorner {
|
||||
id: rightCorner
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: !Config.options.bar.bottom ? parent.top : undefined
|
||||
bottom: Config.options.bar.bottom ? parent.bottom : undefined
|
||||
}
|
||||
size: Appearance.rounding.screenRounding
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
|
||||
corner: RoundCorner.CornerEnum.TopRight
|
||||
states: State {
|
||||
name: "bottom"
|
||||
when: Config.options.bar.bottom
|
||||
PropertyChanges {
|
||||
rightCorner.corner: RoundCorner.CornerEnum.BottomRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "bar"
|
||||
|
||||
function toggle(): void {
|
||||
GlobalStates.barOpen = !GlobalStates.barOpen
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
GlobalStates.barOpen = false
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
GlobalStates.barOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barToggle"
|
||||
description: "Toggles bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = !GlobalStates.barOpen;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barOpen"
|
||||
description: "Opens bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barClose"
|
||||
description: "Closes bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property real padding: 5
|
||||
implicitHeight: 40
|
||||
implicitHeight: Appearance.sizes.baseBarHeight
|
||||
height: Appearance.sizes.barHeight
|
||||
implicitWidth: rowLayout.implicitWidth + padding * 2
|
||||
default property alias items: rowLayout.children
|
||||
|
||||
@@ -18,7 +17,7 @@ Item {
|
||||
topMargin: 4
|
||||
bottomMargin: 4
|
||||
}
|
||||
color: ConfigOptions?.bar.borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
color: Config.options?.bar.borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.UPower
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
readonly property var chargeState: Battery.chargeState
|
||||
readonly property bool isCharging: Battery.isCharging
|
||||
readonly property bool isPluggedIn: Battery.isPluggedIn
|
||||
readonly property real percentage: Battery.percentage
|
||||
readonly property bool isLow: percentage <= ConfigOptions.battery.low / 100
|
||||
readonly property bool isLow: percentage <= Config.options.battery.low / 100
|
||||
readonly property color batteryLowBackground: Appearance.m3colors.darkmode ? Appearance.m3colors.m3error : Appearance.m3colors.m3errorContainer
|
||||
readonly property color batteryLowOnBackground: Appearance.m3colors.darkmode ? Appearance.m3colors.m3errorContainer : Appearance.m3colors.m3error
|
||||
|
||||
@@ -42,6 +39,7 @@ Item {
|
||||
}
|
||||
|
||||
CircularProgress {
|
||||
enableAnimation: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
lineWidth: 2
|
||||
value: percentage
|
||||
@@ -1,11 +1,6 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets/"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
RippleButton {
|
||||
id: button
|
||||
@@ -1,13 +1,13 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
property bool showDate: ConfigOptions.bar.verbose
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
property bool showDate: Config.options.bar.verbose
|
||||
implicitWidth: rowLayout.implicitWidth
|
||||
implicitHeight: 32
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import qs
|
||||
import qs.modules.common.functions
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||
readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || qsTr("No media")
|
||||
readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || Translation.tr("No media")
|
||||
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||
implicitHeight: 40
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
|
||||
Timer {
|
||||
running: activePlayer?.playbackState == MprisPlaybackState.Playing
|
||||
@@ -56,6 +56,7 @@ Item {
|
||||
size: 26
|
||||
secondaryColor: Appearance.colors.colSecondaryContainer
|
||||
primaryColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
enableAnimation: false
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
@@ -68,6 +69,7 @@ Item {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: Config.options.bar.verbose
|
||||
width: rowLayout.width - (CircularProgress.size + rowLayout.spacing * 2)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true // Ensures the text takes up available space
|
||||
@@ -1,9 +1,7 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Item {
|
||||
required property string iconName
|
||||
@@ -26,6 +24,7 @@ Item {
|
||||
size: 26
|
||||
secondaryColor: Appearance.colors.colSecondaryContainer
|
||||
primaryColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
enableAnimation: false
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
@@ -1,15 +1,12 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
property bool alwaysShowAllResources: false
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
||||
implicitHeight: 32
|
||||
@@ -30,7 +27,7 @@ Item {
|
||||
Resource {
|
||||
iconName: "swap_horiz"
|
||||
percentage: ResourceUsage.swapUsedPercentage
|
||||
shown: (ConfigOptions.bar.resources.alwaysShowSwap && percentage > 0) ||
|
||||
shown: (Config.options.bar.resources.alwaysShowSwap && percentage > 0) ||
|
||||
(MprisController.activePlayer?.trackTitle == null) ||
|
||||
root.alwaysShowAllResources
|
||||
Layout.leftMargin: shown ? 4 : 0
|
||||
@@ -39,7 +36,7 @@ Item {
|
||||
Resource {
|
||||
iconName: "settings_slow_motion"
|
||||
percentage: ResourceUsage.cpuUsage
|
||||
shown: ConfigOptions.bar.resources.alwaysShowCpu ||
|
||||
shown: Config.options.bar.resources.alwaysShowCpu ||
|
||||
!(MprisController.activePlayer?.trackTitle?.length > 0) ||
|
||||
root.alwaysShowAllResources
|
||||
Layout.leftMargin: shown ? 4 : 0
|
||||
@@ -1,8 +1,7 @@
|
||||
import "root:/"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Revealer { // Scroll hint
|
||||
@@ -1,11 +1,8 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
|
||||
// TODO: More fancy animation
|
||||
Item {
|
||||
@@ -1,5 +1,5 @@
|
||||
import "root:/modules/common/"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
@@ -43,7 +43,7 @@ MouseArea {
|
||||
|
||||
IconImage {
|
||||
id: trayIcon
|
||||
visible: !ConfigOptions.bar.tray.monochromeIcons
|
||||
visible: !Config.options.bar.tray.monochromeIcons
|
||||
source: root.item.icon
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
@@ -51,7 +51,7 @@ MouseArea {
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: ConfigOptions.bar.tray.monochromeIcons
|
||||
active: Config.options.bar.tray.monochromeIcons
|
||||
anchors.fill: trayIcon
|
||||
sourceComponent: Item {
|
||||
Desaturate {
|
||||
@@ -59,12 +59,12 @@ MouseArea {
|
||||
visible: false // There's already color overlay
|
||||
anchors.fill: parent
|
||||
source: trayIcon
|
||||
desaturation: 1 // 1.0 means fully grayscale
|
||||
desaturation: 0.8 // 1.0 means fully grayscale
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: desaturatedIcon
|
||||
source: desaturatedIcon
|
||||
color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.6)
|
||||
color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.9)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
spacing: 4
|
||||
anchors.centerIn: parent
|
||||
|
||||
Loader {
|
||||
active: Config.options.bar.utilButtons.showScreenSnip
|
||||
visible: Config.options.bar.utilButtons.showScreenSnip
|
||||
sourceComponent: CircleUtilButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("screenshot.qml")])
|
||||
MaterialSymbol {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 1
|
||||
text: "screenshot_region"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.options.bar.utilButtons.showColorPicker
|
||||
visible: Config.options.bar.utilButtons.showColorPicker
|
||||
sourceComponent: CircleUtilButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: Quickshell.execDetached(["hyprpicker", "-a"])
|
||||
MaterialSymbol {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 1
|
||||
text: "colorize"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.options.bar.utilButtons.showKeyboardToggle
|
||||
visible: Config.options.bar.utilButtons.showKeyboardToggle
|
||||
sourceComponent: CircleUtilButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: Hyprland.dispatch("global quickshell:oskToggle")
|
||||
MaterialSymbol {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 0
|
||||
text: "keyboard"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.options.bar.utilButtons.showMicToggle
|
||||
visible: Config.options.bar.utilButtons.showMicToggle
|
||||
sourceComponent: CircleUtilButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: Quickshell.execDetached(["wpctl", "set-mute", "@DEFAULT_SOURCE@", "toggle"])
|
||||
MaterialSymbol {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 0
|
||||
text: Pipewire.defaultAudioSource?.audio?.muted ? "mic_off" : "mic"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.options.bar.utilButtons.showDarkModeToggle
|
||||
visible: Config.options.bar.utilButtons.showDarkModeToggle
|
||||
sourceComponent: CircleUtilButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: event => {
|
||||
if (Appearance.m3colors.darkmode) {
|
||||
Hyprland.dispatch(`exec ${Directories.wallpaperSwitchScriptPath} --mode light --noswitch`);
|
||||
} else {
|
||||
Hyprland.dispatch(`exec ${Directories.wallpaperSwitchScriptPath} --mode dark --noswitch`);
|
||||
}
|
||||
}
|
||||
MaterialSymbol {
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 0
|
||||
text: Appearance.m3colors.darkmode ? "light_mode" : "dark_mode"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,24 @@
|
||||
import "root:/"
|
||||
import "root:/services/"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
required property var bar
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
|
||||
readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / ConfigOptions.bar.workspaces.shown)
|
||||
readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown)
|
||||
property list<bool> workspaceOccupied: []
|
||||
property int widgetPadding: 4
|
||||
property int workspaceButtonWidth: 26
|
||||
@@ -27,12 +26,12 @@ Item {
|
||||
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
||||
property real workspaceIconOpacityShrinked: 1
|
||||
property real workspaceIconMarginShrinked: -4
|
||||
property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % ConfigOptions.bar.workspaces.shown
|
||||
property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown
|
||||
|
||||
// Function to update workspaceOccupied
|
||||
function updateWorkspaceOccupied() {
|
||||
workspaceOccupied = Array.from({ length: ConfigOptions.bar.workspaces.shown }, (_, i) => {
|
||||
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * ConfigOptions.bar.workspaces.shown + i + 1);
|
||||
workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => {
|
||||
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -48,7 +47,7 @@ Item {
|
||||
}
|
||||
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||
implicitHeight: 40
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
|
||||
// Scroll to switch workspaces
|
||||
WheelHandler {
|
||||
@@ -78,10 +77,10 @@ Item {
|
||||
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
implicitHeight: 40
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
|
||||
Repeater {
|
||||
model: ConfigOptions.bar.workspaces.shown
|
||||
model: Config.options.bar.workspaces.shown
|
||||
|
||||
Rectangle {
|
||||
z: 1
|
||||
@@ -157,14 +156,14 @@ Item {
|
||||
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
implicitHeight: 40
|
||||
implicitHeight: Appearance.sizes.barHeight
|
||||
|
||||
Repeater {
|
||||
model: ConfigOptions.bar.workspaces.shown
|
||||
model: Config.options.bar.workspaces.shown
|
||||
|
||||
Button {
|
||||
id: button
|
||||
property int workspaceValue: workspaceGroup * ConfigOptions.bar.workspaces.shown + index + 1
|
||||
property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1
|
||||
Layout.fillHeight: true
|
||||
onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
|
||||
width: workspaceButtonWidth
|
||||
@@ -173,20 +172,13 @@ Item {
|
||||
id: workspaceButtonBackground
|
||||
implicitWidth: workspaceButtonWidth
|
||||
implicitHeight: workspaceButtonWidth
|
||||
property var biggestWindow: {
|
||||
const windowsInThisWorkspace = HyprlandData.windowList.filter(w => w.workspace.id == button.workspaceValue)
|
||||
return windowsInThisWorkspace.reduce((maxWin, win) => {
|
||||
const maxArea = (maxWin?.size?.[0] ?? 0) * (maxWin?.size?.[1] ?? 0)
|
||||
const winArea = (win?.size?.[0] ?? 0) * (win?.size?.[1] ?? 0)
|
||||
return winArea > maxArea ? win : maxWin
|
||||
}, null)
|
||||
}
|
||||
property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue)
|
||||
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
|
||||
|
||||
StyledText { // Workspace number text
|
||||
opacity: GlobalStates.workspaceShowNumbers
|
||||
|| ((ConfigOptions?.bar.workspaces.alwaysShowNumbers && (!ConfigOptions?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || GlobalStates.workspaceShowNumbers))
|
||||
|| (GlobalStates.workspaceShowNumbers && !ConfigOptions?.bar.workspaces.showAppIcons)
|
||||
|| ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || GlobalStates.workspaceShowNumbers))
|
||||
|| (GlobalStates.workspaceShowNumbers && !Config.options?.bar.workspaces.showAppIcons)
|
||||
) ? 1 : 0
|
||||
z: 3
|
||||
|
||||
@@ -206,9 +198,10 @@ Item {
|
||||
}
|
||||
}
|
||||
Rectangle { // Dot instead of ws number
|
||||
opacity: (ConfigOptions?.bar.workspaces.alwaysShowNumbers
|
||||
id: wsDot
|
||||
opacity: (Config.options?.bar.workspaces.alwaysShowNumbers
|
||||
|| GlobalStates.workspaceShowNumbers
|
||||
|| (ConfigOptions?.bar.workspaces.showAppIcons && workspaceButtonBackground.biggestWindow)
|
||||
|| (Config.options?.bar.workspaces.showAppIcons && workspaceButtonBackground.biggestWindow)
|
||||
) ? 0 : 1
|
||||
visible: opacity > 0
|
||||
anchors.centerIn: parent
|
||||
@@ -228,21 +221,21 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
width: workspaceButtonWidth
|
||||
height: workspaceButtonWidth
|
||||
opacity: !ConfigOptions?.bar.workspaces.showAppIcons ? 0 :
|
||||
(workspaceButtonBackground.biggestWindow && !GlobalStates.workspaceShowNumbers && ConfigOptions?.bar.workspaces.showAppIcons) ?
|
||||
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
|
||||
(workspaceButtonBackground.biggestWindow && !GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
|
||||
visible: opacity > 0
|
||||
IconImage {
|
||||
id: mainAppIcon
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: (!GlobalStates.workspaceShowNumbers && ConfigOptions?.bar.workspaces.showAppIcons) ?
|
||||
anchors.bottomMargin: (!GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
anchors.rightMargin: (!GlobalStates.workspaceShowNumbers && ConfigOptions?.bar.workspaces.showAppIcons) ?
|
||||
anchors.rightMargin: (!GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
|
||||
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
|
||||
|
||||
source: workspaceButtonBackground.mainAppIconSource
|
||||
implicitSize: (!GlobalStates.workspaceShowNumbers && ConfigOptions?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
|
||||
implicitSize: (!GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
@@ -257,6 +250,25 @@ Item {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: Config.options.bar.workspaces.monochromeIcons
|
||||
anchors.fill: mainAppIcon
|
||||
sourceComponent: Item {
|
||||
Desaturate {
|
||||
id: desaturatedIcon
|
||||
visible: false // There's already color overlay
|
||||
anchors.fill: parent
|
||||
source: mainAppIcon
|
||||
desaturation: 0.8
|
||||
}
|
||||
ColorOverlay {
|
||||
anchors.fill: desaturatedIcon
|
||||
source: desaturatedIcon
|
||||
color: ColorUtils.transparentize(wsDot.color, 0.9)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
property real margin: 10
|
||||
property bool hovered: false
|
||||
implicitWidth: rowLayout.implicitWidth + margin * 2
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
anchors.centerIn: parent
|
||||
|
||||
MaterialSymbol {
|
||||
fill: 0
|
||||
text: WeatherIcons.codeToName[Weather.data.wCode]
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer1
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: Weather.data.temp
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: popupLoader
|
||||
active: root.containsMouse
|
||||
|
||||
component: PopupWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
implicitWidth: weatherPopup.implicitWidth
|
||||
implicitHeight: weatherPopup.implicitHeight
|
||||
anchor.item: root
|
||||
anchor.edges: Edges.Top
|
||||
anchor.rect.x: (root.implicitWidth - popupWindow.implicitWidth) / 2
|
||||
anchor.rect.y: Config.options.bar.bottom ?
|
||||
(-weatherPopup.implicitHeight - 15) :
|
||||
(root.implicitHeight + 15 )
|
||||
color: "transparent"
|
||||
WeatherPopup {
|
||||
id: weatherPopup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colLayer1
|
||||
implicitWidth: columnLayout.implicitWidth * 2
|
||||
implicitHeight: columnLayout.implicitHeight * 2
|
||||
Layout.fillWidth: parent
|
||||
|
||||
property alias title: title.text
|
||||
property alias value: value.text
|
||||
property alias symbol: symbol.text
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
spacing: -10
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
MaterialSymbol {
|
||||
id: symbol
|
||||
fill: 0
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
StyledText {
|
||||
id: title
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: value
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
// credits: calestia
|
||||
// this snippet is taken from
|
||||
// https://github.com/caelestia-dots/shell
|
||||
readonly property var codeToName: ({
|
||||
"113": "clear_day",
|
||||
"116": "partly_cloudy_day",
|
||||
"119": "cloud",
|
||||
"122": "cloud",
|
||||
"143": "foggy",
|
||||
"176": "rainy",
|
||||
"179": "rainy",
|
||||
"182": "rainy",
|
||||
"185": "rainy",
|
||||
"200": "thunderstorm",
|
||||
"227": "cloudy_snowing",
|
||||
"230": "snowing_heavy",
|
||||
"248": "foggy",
|
||||
"260": "foggy",
|
||||
"263": "rainy",
|
||||
"266": "rainy",
|
||||
"281": "rainy",
|
||||
"284": "rainy",
|
||||
"293": "rainy",
|
||||
"296": "rainy",
|
||||
"299": "rainy",
|
||||
"302": "weather_hail",
|
||||
"305": "rainy",
|
||||
"308": "weather_hail",
|
||||
"311": "rainy",
|
||||
"314": "rainy",
|
||||
"317": "rainy",
|
||||
"320": "cloudy_snowing",
|
||||
"323": "cloudy_snowing",
|
||||
"326": "cloudy_snowing",
|
||||
"329": "snowing_heavy",
|
||||
"332": "snowing_heavy",
|
||||
"335": "snowing",
|
||||
"338": "snowing_heavy",
|
||||
"350": "rainy",
|
||||
"353": "rainy",
|
||||
"356": "rainy",
|
||||
"359": "weather_hail",
|
||||
"362": "rainy",
|
||||
"365": "rainy",
|
||||
"368": "cloudy_snowing",
|
||||
"371": "snowing",
|
||||
"374": "rainy",
|
||||
"377": "rainy",
|
||||
"386": "thunderstorm",
|
||||
"389": "thunderstorm",
|
||||
"392": "thunderstorm",
|
||||
"395": "snowing"
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
readonly property real margin: 10
|
||||
implicitWidth: columnLayout.implicitWidth + margin * 2
|
||||
implicitHeight: columnLayout.implicitHeight + margin * 2
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.small
|
||||
border.width: 1
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
spacing: 5
|
||||
anchors.centerIn: root
|
||||
implicitWidth: Math.max(header.implicitWidth, gridLayout.implicitWidth)
|
||||
implicitHeight: gridLayout.implicitHeight
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
id: header
|
||||
spacing: 5
|
||||
Layout.fillWidth: parent
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
MaterialSymbol {
|
||||
fill: 0
|
||||
text: "location_on"
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: Weather.data.city
|
||||
font.pixelSize: Appearance.font.pixelSize.title
|
||||
font.family: Appearance.font.family.title
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics grid
|
||||
GridLayout {
|
||||
id: gridLayout
|
||||
columns: 2
|
||||
rowSpacing: 5
|
||||
columnSpacing: 5
|
||||
uniformCellWidths: true
|
||||
|
||||
WeatherCard {
|
||||
title: Translation.tr("UV Index")
|
||||
symbol: "wb_sunny"
|
||||
value: Weather.data.uv
|
||||
}
|
||||
WeatherCard {
|
||||
title: Translation.tr("Wind")
|
||||
symbol: "air"
|
||||
value: `(${Weather.data.windDir}) ${Weather.data.wind}`
|
||||
}
|
||||
WeatherCard {
|
||||
title: Translation.tr("Precipitation")
|
||||
symbol: "rainy_light"
|
||||
value: Weather.data.precip
|
||||
}
|
||||
WeatherCard {
|
||||
title: Translation.tr("Humidity")
|
||||
symbol: "humidity_low"
|
||||
value: Weather.data.humidity
|
||||
}
|
||||
WeatherCard {
|
||||
title: Translation.tr("Visibility")
|
||||
symbol: "visibility"
|
||||
value: Weather.data.visib
|
||||
}
|
||||
WeatherCard {
|
||||
title: Translation.tr("Pressure")
|
||||
symbol: "readiness_score"
|
||||
value: Weather.data.press
|
||||
}
|
||||
WeatherCard {
|
||||
title: Translation.tr("Sunrise")
|
||||
symbol: "wb_twilight"
|
||||
value: Weather.data.sunrise
|
||||
}
|
||||
WeatherCard {
|
||||
title: Translation.tr("Sunset")
|
||||
symbol: "bedtime"
|
||||
value: Weather.data.sunset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope { // Scope
|
||||
id: root
|
||||
property var tabButtonList: [
|
||||
{
|
||||
"icon": "keyboard",
|
||||
"name": Translation.tr("Keybinds")
|
||||
},
|
||||
{
|
||||
"icon": "experiment",
|
||||
"name": Translation.tr("Elements")
|
||||
},
|
||||
]
|
||||
property int selectedTab: 0
|
||||
|
||||
Loader {
|
||||
id: cheatsheetLoader
|
||||
active: false
|
||||
|
||||
sourceComponent: PanelWindow { // Window
|
||||
id: cheatsheetRoot
|
||||
visible: cheatsheetLoader.active
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
function hide() {
|
||||
cheatsheetLoader.active = false;
|
||||
}
|
||||
exclusiveZone: 0
|
||||
implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2
|
||||
WlrLayershell.namespace: "quickshell:cheatsheet"
|
||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
color: "transparent"
|
||||
|
||||
mask: Region {
|
||||
item: cheatsheetBackground
|
||||
}
|
||||
|
||||
HyprlandFocusGrab { // Click outside to close
|
||||
id: grab
|
||||
windows: [cheatsheetRoot]
|
||||
active: cheatsheetRoot.visible
|
||||
onCleared: () => {
|
||||
if (!active)
|
||||
cheatsheetRoot.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Background
|
||||
StyledRectangularShadow {
|
||||
target: cheatsheetBackground
|
||||
}
|
||||
Rectangle {
|
||||
id: cheatsheetBackground
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.colors.colLayer0
|
||||
border.width: 1
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
radius: Appearance.rounding.windowRounding
|
||||
property real padding: 30
|
||||
implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2
|
||||
implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2
|
||||
|
||||
Keys.onPressed: event => { // Esc to close
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
cheatsheetRoot.hide();
|
||||
}
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_PageUp) {
|
||||
root.selectedTab = Math.max(root.selectedTab - 1, 0);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length;
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RippleButton { // Close button
|
||||
id: closeButton
|
||||
focus: cheatsheetRoot.visible
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
buttonRadius: Appearance.rounding.full
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
topMargin: 20
|
||||
rightMargin: 20
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
cheatsheetRoot.hide();
|
||||
}
|
||||
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.title
|
||||
text: "close"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout { // Real content
|
||||
id: cheatsheetColumnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 20
|
||||
|
||||
StyledText {
|
||||
id: cheatsheetTitle
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.family: Appearance.font.family.title
|
||||
font.pixelSize: Appearance.font.pixelSize.title
|
||||
text: Translation.tr("Cheat sheet")
|
||||
}
|
||||
PrimaryTabBar { // Tab strip
|
||||
id: tabBar
|
||||
tabButtonList: root.tabButtonList
|
||||
externalTrackedTab: root.selectedTab
|
||||
function onCurrentIndexChanged(currentIndex) {
|
||||
root.selectedTab = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView { // Content pages
|
||||
id: swipeView
|
||||
Layout.topMargin: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
|
||||
Behavior on implicitWidth {
|
||||
id: contentWidthBehavior
|
||||
enabled: false
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
id: contentHeightBehavior
|
||||
enabled: false
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
currentIndex: tabBar.externalTrackedTab
|
||||
onCurrentIndexChanged: {
|
||||
contentWidthBehavior.enabled = true;
|
||||
contentHeightBehavior.enabled = true;
|
||||
tabBar.enableIndicatorAnimation = true;
|
||||
root.selectedTab = currentIndex;
|
||||
}
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
|
||||
CheatsheetKeybinds {}
|
||||
CheatsheetPeriodicTable {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "cheatsheet"
|
||||
|
||||
function toggle(): void {
|
||||
cheatsheetLoader.active = !cheatsheetLoader.active;
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
cheatsheetLoader.active = false;
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
cheatsheetLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetToggle"
|
||||
description: "Toggles cheatsheet on press"
|
||||
|
||||
onPressed: {
|
||||
cheatsheetLoader.active = !cheatsheetLoader.active;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetOpen"
|
||||
description: "Opens cheatsheet on press"
|
||||
|
||||
onPressed: {
|
||||
cheatsheetLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetClose"
|
||||
description: "Closes cheatsheet on press"
|
||||
|
||||
onPressed: {
|
||||
cheatsheetLoader.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -0,0 +1,68 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import "periodic_table.js" as PTable
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var elements: PTable.elements
|
||||
readonly property var series: PTable.series
|
||||
property real spacing: 6
|
||||
implicitWidth: mainLayout.implicitWidth
|
||||
implicitHeight: mainLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
spacing: root.spacing
|
||||
|
||||
Repeater { // Main table rows
|
||||
model: root.elements
|
||||
|
||||
delegate: RowLayout { // Table cells
|
||||
id: tableRow
|
||||
spacing: root.spacing
|
||||
required property var modelData
|
||||
|
||||
Repeater {
|
||||
model: tableRow.modelData
|
||||
delegate: ElementTile {
|
||||
required property var modelData
|
||||
element: modelData
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
id: gap
|
||||
implicitHeight: 20
|
||||
}
|
||||
|
||||
Repeater { // Main table rows
|
||||
model: root.series
|
||||
|
||||
delegate: RowLayout { // Table cells
|
||||
id: seriesTableRow
|
||||
spacing: root.spacing
|
||||
required property var modelData
|
||||
|
||||
Repeater {
|
||||
model: seriesTableRow.modelData
|
||||
delegate: ElementTile {
|
||||
required property var modelData
|
||||
element: modelData
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
|
||||
RippleButton {
|
||||
id: root
|
||||
required property var element
|
||||
opacity: element.type != "empty" ? 1 : 0
|
||||
implicitHeight: 60
|
||||
implicitWidth: 60
|
||||
colBackground: Appearance.colors.colLayer2
|
||||
buttonRadius: Appearance.rounding.small
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
topMargin: 4
|
||||
leftMargin: 4
|
||||
}
|
||||
color: Appearance.colors.colLayer2
|
||||
radius: Appearance.rounding.full
|
||||
implicitWidth: Math.max(20, elementNumber.implicitWidth)
|
||||
implicitHeight: Math.max(20, elementNumber.implicitHeight)
|
||||
width: height
|
||||
|
||||
StyledText {
|
||||
id: elementNumber
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.colors.colOnLayer2
|
||||
text: root.element.number
|
||||
font.pixelSize: Appearance.font.pixelSize.smallest
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: elementSymbol
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.colors.colSecondary
|
||||
font.pixelSize: Appearance.font.pixelSize.huge
|
||||
text: root.element.symbol
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: elementName
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 4
|
||||
}
|
||||
font.pixelSize: Appearance.font.pixelSize.smallest
|
||||
color: Appearance.colors.colOnLayer2
|
||||
text: root.element.name
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
// List of rows
|
||||
const elements = [
|
||||
[
|
||||
{ name: 'Hydrogen', symbol: 'H', number: 1, weight: 1.01, type: 'nonmetal' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: 'Helium', symbol: 'He', number: 2, weight: 4.00, type: 'noblegas' },
|
||||
],
|
||||
[
|
||||
{ name: 'Lithium', symbol: 'Li', number: 3, weight: 6.94, type: 'metal' },
|
||||
{ name: 'Beryllium', symbol: 'Be', number: 4, weight: 9.01, type: 'metal' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: 'Boron', symbol: 'B', number: 5, weight: 10.81, type: 'nonmetal' },
|
||||
{ name: 'Carbon', symbol: 'C', number: 6, weight: 12.01, type: 'nonmetal' },
|
||||
{ name: 'Nitrogen', symbol: 'N', number: 7, weight: 14.01, type: 'nonmetal' },
|
||||
{ name: 'Oxygen', symbol: 'O', number: 8, weight: 16, type: 'nonmetal' },
|
||||
{ name: 'Fluorine', symbol: 'F', number: 9, weight: 19, type: 'nonmetal' },
|
||||
{ name: 'Neon', symbol: 'Ne', number: 10, weight: 20.18, type: 'noblegas' },
|
||||
|
||||
|
||||
],
|
||||
[
|
||||
{ name: 'Sodium', symbol: 'Na', number: 11, weight: 22.99, type: 'metal' },
|
||||
{ name: 'Magnesium', symbol: 'Mg', number: 12, weight: 24.31, type: 'metal' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: 'Aluminum', symbol: 'Al', number: 13, weight: 26.98, type: 'metal' },
|
||||
{ name: 'Silicon', symbol: 'Si', number: 14, weight: 28.09, type: 'nonmetal' },
|
||||
{ name: 'Phosphorus', symbol: 'P', number: 15, weight: 30.97, type: 'nonmetal' },
|
||||
{ name: 'Sulfur', symbol: 'S', number: 16, weight: 32.07, type: 'nonmetal' },
|
||||
{ name: 'Chlorine', symbol: 'Cl', number: 17, weight: 35.45, type: 'nonmetal' },
|
||||
{ name: 'Argon', symbol: 'Ar', number: 18, weight: 39.95, type: 'noblegas' },
|
||||
],
|
||||
[
|
||||
{ name: 'Potassium', symbol: 'K', number: 19, weight: 39.098, type: 'metal' },
|
||||
{ name: 'Calcium', symbol: 'Ca', number: 20, weight: 40.078, type: 'metal' },
|
||||
{ name: 'Scandium', symbol: 'Sc', number: 21, weight: 44.956, type: 'metal' },
|
||||
{ name: 'Titanium', symbol: 'Ti', number: 22, weight: 47.87, type: 'metal' },
|
||||
{ name: 'Vanadium', symbol: 'V', number: 23, weight: 50.94, type: 'metal' },
|
||||
{ name: 'Chromium', symbol: 'Cr', number: 24, weight: 52, type: 'metal'/*, icon: 'chromium-browser'*/ },
|
||||
{ name: 'Manganese', symbol: 'Mn', number: 25, weight: 54.94, type: 'metal' },
|
||||
{ name: 'Iron', symbol: 'Fe', number: 26, weight: 55.85, type: 'metal' },
|
||||
{ name: 'Cobalt', symbol: 'Co', number: 27, weight: 58.93, type: 'metal' },
|
||||
{ name: 'Nickel', symbol: 'Ni', number: 28, weight: 58.69, type: 'metal' },
|
||||
{ name: 'Copper', symbol: 'Cu', number: 29, weight: 63.55, type: 'metal' },
|
||||
{ name: 'Zinc', symbol: 'Zn', number: 30, weight: 65.38, type: 'metal' },
|
||||
{ name: 'Gallium', symbol: 'Ga', number: 31, weight: 69.72, type: 'metal' },
|
||||
{ name: 'Germanium', symbol: 'Ge', number: 32, weight: 72.63, type: 'metal' },
|
||||
{ name: 'Arsenic', symbol: 'As', number: 33, weight: 74.92, type: 'nonmetal' },
|
||||
{ name: 'Selenium', symbol: 'Se', number: 34, weight: 78.96, type: 'nonmetal' },
|
||||
{ name: 'Bromine', symbol: 'Br', number: 35, weight: 79.904, type: 'nonmetal' },
|
||||
{ name: 'Krypton', symbol: 'Kr', number: 36, weight: 83.8, type: 'noblegas' },
|
||||
],
|
||||
[
|
||||
{ name: 'Rubidium', symbol: 'Rb', number: 37, weight: 85.47, type: 'metal' },
|
||||
{ name: 'Strontium', symbol: 'Sr', number: 38, weight: 87.62, type: 'metal' },
|
||||
{ name: 'Yttrium', symbol: 'Y', number: 39, weight: 88.91, type: 'metal' },
|
||||
{ name: 'Zirconium', symbol: 'Zr', number: 40, weight: 91.22, type: 'metal' },
|
||||
{ name: 'Niobium', symbol: 'Nb', number: 41, weight: 92.91, type: 'metal' },
|
||||
{ name: 'Molybdenum', symbol: 'Mo', number: 42, weight: 95.94, type: 'metal' },
|
||||
{ name: 'Technetium', symbol: 'Tc', number: 43, weight: 98, type: 'metal' },
|
||||
{ name: 'Ruthenium', symbol: 'Ru', number: 44, weight: 101.07, type: 'metal' },
|
||||
{ name: 'Rhodium', symbol: 'Rh', number: 45, weight: 102.91, type: 'metal' },
|
||||
{ name: 'Palladium', symbol: 'Pd', number: 46, weight: 106.42, type: 'metal' },
|
||||
{ name: 'Silver', symbol: 'Ag', number: 47, weight: 107.87, type: 'metal' },
|
||||
{ name: 'Cadmium', symbol: 'Cd', number: 48, weight: 112.41, type: 'metal' },
|
||||
{ name: 'Indium', symbol: 'In', number: 49, weight: 114.82, type: 'metal' },
|
||||
{ name: 'Tin', symbol: 'Sn', number: 50, weight: 118.71, type: 'metal' },
|
||||
{ name: 'Antimony', symbol: 'Sb', number: 51, weight: 121.76, type: 'metal' },
|
||||
{ name: 'Tellurium', symbol: 'Te', number: 52, weight: 127.6, type: 'nonmetal' },
|
||||
{ name: 'Iodine', symbol: 'I', number: 53, weight: 126.9, type: 'nonmetal' },
|
||||
{ name: 'Xenon', symbol: 'Xe', number: 54, weight: 131.29, type: 'noblegas' },
|
||||
],
|
||||
[
|
||||
{ name: 'Cesium', symbol: 'Cs', number: 55, weight: 132.91, type: 'metal' },
|
||||
{ name: 'Barium', symbol: 'Ba', number: 56, weight: 137.33, type: 'metal' },
|
||||
{ name: 'Lanthanum', symbol: 'La', number: 57, weight: 138.91, type: 'lanthanum' },
|
||||
{ name: 'Hafnium', symbol: 'Hf', number: 72, weight: 178.49, type: 'metal' },
|
||||
{ name: 'Tantalum', symbol: 'Ta', number: 73, weight: 180.95, type: 'metal' },
|
||||
{ name: 'Tungsten', symbol: 'W', number: 74, weight: 183.84, type: 'metal' },
|
||||
{ name: 'Rhenium', symbol: 'Re', number: 75, weight: 186.21, type: 'metal' },
|
||||
{ name: 'Osmium', symbol: 'Os', number: 76, weight: 190.23, type: 'metal' },
|
||||
{ name: 'Iridium', symbol: 'Ir', number: 77, weight: 192.22, type: 'metal' },
|
||||
{ name: 'Platinum', symbol: 'Pt', number: 78, weight: 195.09, type: 'metal' },
|
||||
{ name: 'Gold', symbol: 'Au', number: 79, weight: 196.97, type: 'metal' },
|
||||
{ name: 'Mercury', symbol: 'Hg', number: 80, weight: 200.59, type: 'metal' },
|
||||
{ name: 'Thallium', symbol: 'Tl', number: 81, weight: 204.38, type: 'metal' },
|
||||
{ name: 'Lead', symbol: 'Pb', number: 82, weight: 207.2, type: 'metal' },
|
||||
{ name: 'Bismuth', symbol: 'Bi', number: 83, weight: 208.98, type: 'metal' },
|
||||
{ name: 'Polonium', symbol: 'Po', number: 84, weight: 209, type: 'metal' },
|
||||
{ name: 'Astatine', symbol: 'At', number: 85, weight: 210, type: 'nonmetal' },
|
||||
{ name: 'Radon', symbol: 'Rn', number: 86, weight: 222, type: 'noblegas' },
|
||||
],
|
||||
[
|
||||
{ name: 'Francium', symbol: 'Fr', number: 87, weight: 223, type: 'metal' },
|
||||
{ name: 'Radium', symbol: 'Ra', number: 88, weight: 226, type: 'metal' },
|
||||
{ name: 'Actinium', symbol: 'Ac', number: 89, weight: 227, type: 'actinium' },
|
||||
{ name: 'Rutherfordium', symbol: 'Rf', number: 104, weight: 267, type: 'metal' },
|
||||
{ name: 'Dubnium', symbol: 'Db', number: 105, weight: 268, type: 'metal' },
|
||||
{ name: 'Seaborgium', symbol: 'Sg', number: 106, weight: 271, type: 'metal' },
|
||||
{ name: 'Bohrium', symbol: 'Bh', number: 107, weight: 272, type: 'metal' },
|
||||
{ name: 'Hassium', symbol: 'Hs', number: 108, weight: 277, type: 'metal' },
|
||||
{ name: 'Meitnerium', symbol: 'Mt', number: 109, weight: 278, type: 'metal' },
|
||||
{ name: 'Darmstadtium', symbol: 'Ds', number: 110, weight: 281, type: 'metal' },
|
||||
{ name: 'Roentgenium', symbol: 'Rg', number: 111, weight: 280, type: 'metal' },
|
||||
{ name: 'Copernicium', symbol: 'Cn', number: 112, weight: 285, type: 'metal' },
|
||||
{ name: 'Nihonium', symbol: 'Nh', number: 113, weight: 286, type: 'metal' },
|
||||
{ name: 'Flerovium', symbol: 'Fl', number: 114, weight: 289, type: 'metal' },
|
||||
{ name: 'Moscovium', symbol: 'Mc', number: 115, weight: 290, type: 'metal' },
|
||||
{ name: 'Livermorium', symbol: 'Lv', number: 116, weight: 293, type: 'metal' },
|
||||
{ name: 'Tennessine', symbol: 'Ts', number: 117, weight: 294, type: 'metal' },
|
||||
{ name: 'Oganesson', symbol: 'Og', number: 118, weight: 294, type: 'noblegas' },
|
||||
],
|
||||
]
|
||||
|
||||
const series = [
|
||||
[
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: 'Cerium', symbol: 'Ce', number: 58, weight: 140.12, type: 'lanthanum' },
|
||||
{ name: 'Praseodymium', symbol: 'Pr', number: 59, weight: 140.91, type: 'lanthanum' },
|
||||
{ name: 'Neodymium', symbol: 'Nd', number: 60, weight: 144.24, type: 'lanthanum' },
|
||||
{ name: 'Promethium', symbol: 'Pm', number: 61, weight: 145, type: 'lanthanum' },
|
||||
{ name: 'Samarium', symbol: 'Sm', number: 62, weight: 150.36, type: 'lanthanum' },
|
||||
{ name: 'Europium', symbol: 'Eu', number: 63, weight: 151.96, type: 'lanthanum' },
|
||||
{ name: 'Gadolinium', symbol: 'Gd', number: 64, weight: 157.25, type: 'lanthanum' },
|
||||
{ name: 'Terbium', symbol: 'Tb', number: 65, weight: 158.93, type: 'lanthanum' },
|
||||
{ name: 'Dysprosium', symbol: 'Dy', number: 66, weight: 162.5, type: 'lanthanum' },
|
||||
{ name: 'Holmium', symbol: 'Ho', number: 67, weight: 164.93, type: 'lanthanum' },
|
||||
{ name: 'Erbium', symbol: 'Er', number: 68, weight: 167.26, type: 'lanthanum' },
|
||||
{ name: 'Thulium', symbol: 'Tm', number: 69, weight: 168.93, type: 'lanthanum' },
|
||||
{ name: 'Ytterbium', symbol: 'Yb', number: 70, weight: 173.04, type: 'lanthanum' },
|
||||
{ name: 'Lutetium', symbol: 'Lu', number: 71, weight: 174.97, type: 'lanthanum' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
],
|
||||
[
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
{ name: 'Thorium', symbol: 'Th', number: 90, weight: 232.04, type: 'actinium' },
|
||||
{ name: 'Protactinium', symbol: 'Pa', number: 91, weight: 231.04, type: 'actinium' },
|
||||
{ name: 'Uranium', symbol: 'U', number: 92, weight: 238.03, type: 'actinium' },
|
||||
{ name: 'Neptunium', symbol: 'Np', number: 93, weight: 237, type: 'actinium' },
|
||||
{ name: 'Plutonium', symbol: 'Pu', number: 94, weight: 244, type: 'actinium' },
|
||||
{ name: 'Americium', symbol: 'Am', number: 95, weight: 243, type: 'actinium' },
|
||||
{ name: 'Curium', symbol: 'Cm', number: 96, weight: 247, type: 'actinium' },
|
||||
{ name: 'Berkelium', symbol: 'Bk', number: 97, weight: 247, type: 'actinium' },
|
||||
{ name: 'Californium', symbol: 'Cf', number: 98, weight: 251, type: 'actinium' },
|
||||
{ name: 'Einsteinium', symbol: 'Es', number: 99, weight: 252, type: 'actinium' },
|
||||
{ name: 'Fermium', symbol: 'Fm', number: 100, weight: 257, type: 'actinium' },
|
||||
{ name: 'Mendelevium', symbol: 'Md', number: 101, weight: 258, type: 'actinium' },
|
||||
{ name: 'Nobelium', symbol: 'No', number: 102, weight: 259, type: 'actinium' },
|
||||
{ name: 'Lawrencium', symbol: 'Lr', number: 103, weight: 262, type: 'actinium' },
|
||||
{ name: '', symbol: '', number: -1, weight: 0, type: 'empty' },
|
||||
],
|
||||
];
|
||||
|
||||
const niceTypes = {
|
||||
'metal': "Metal",
|
||||
'nonmetal': "Nonmetal",
|
||||
'noblegas': "Noble gas",
|
||||
'lanthanum': "Lanthanum",
|
||||
'actinium': "Actinium"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import qs.modules.common.functions
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
@@ -16,8 +16,8 @@ Singleton {
|
||||
property string syntaxHighlightingTheme
|
||||
|
||||
// Extremely conservative transparency values for consistency and readability
|
||||
property real transparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0) : 0
|
||||
property real contentTransparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0) : 0
|
||||
property real transparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0.07) : 0
|
||||
property real contentTransparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0.55) : 0
|
||||
|
||||
m3colors: QtObject {
|
||||
property bool darkmode: false
|
||||
@@ -100,7 +100,7 @@ Singleton {
|
||||
|
||||
colors: QtObject {
|
||||
property color colSubtext: m3colors.m3outline
|
||||
property color colLayer0: ColorUtils.transparentize(m3colors.m3background, root.transparency)
|
||||
property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.transparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1)
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency))
|
||||
property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency))
|
||||
@@ -126,11 +126,12 @@ Singleton {
|
||||
property color colPrimaryContainer: m3colors.m3primaryContainer
|
||||
property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Hover, 0.7)
|
||||
property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Active, 0.6)
|
||||
property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer
|
||||
property color colSecondary: m3colors.m3secondary
|
||||
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
||||
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
||||
property color colSecondaryContainer: ColorUtils.transparentize(m3colors.m3secondaryContainer, root.contentTransparency)
|
||||
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
|
||||
property color colSecondaryContainer: m3colors.m3secondaryContainer
|
||||
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.90)
|
||||
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
||||
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||
property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency)
|
||||
@@ -167,10 +168,11 @@ Singleton {
|
||||
property string iconNerd: "SpaceMono NF"
|
||||
property string monospace: "JetBrains Mono NF"
|
||||
property string reading: "Readex Pro"
|
||||
property string expressive: "Space Grotesk"
|
||||
}
|
||||
property QtObject pixelSize: QtObject {
|
||||
property int smallest: 10
|
||||
property int smaller: 13
|
||||
property int smaller: 12
|
||||
property int small: 15
|
||||
property int normal: 16
|
||||
property int large: 17
|
||||
@@ -187,16 +189,22 @@ Singleton {
|
||||
readonly property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1] // Default, 650ms
|
||||
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1] // Default, 200ms
|
||||
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedFirstHalf: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82]
|
||||
readonly property list<real> emphasizedLastHalf: [5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
|
||||
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
|
||||
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
|
||||
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
|
||||
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
|
||||
readonly property real expressiveFastSpatialDuration: 350
|
||||
readonly property real expressiveDefaultSpatialDuration: 500
|
||||
readonly property real expressiveSlowSpatialDuration: 650
|
||||
readonly property real expressiveEffectsDuration: 200
|
||||
}
|
||||
|
||||
animation: QtObject {
|
||||
property QtObject elementMove: QtObject {
|
||||
property int duration: 500
|
||||
property int duration: animationCurves.expressiveDefaultSpatialDuration
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveDefaultSpatial
|
||||
property int velocity: 650
|
||||
@@ -242,7 +250,7 @@ Singleton {
|
||||
}
|
||||
}
|
||||
property QtObject elementMoveFast: QtObject {
|
||||
property int duration: 200
|
||||
property int duration: animationCurves.expressiveEffectsDuration
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveEffects
|
||||
property int velocity: 850
|
||||
@@ -281,8 +289,10 @@ Singleton {
|
||||
}
|
||||
|
||||
sizes: QtObject {
|
||||
property real barHeight: 40
|
||||
property real barCenterSideModuleWidth: ConfigOptions?.bar.verbose ? 360 : 140
|
||||
property real baseBarHeight: 40
|
||||
property real barHeight: Config.options.bar.cornerStyle === 1 ?
|
||||
(baseBarHeight + Appearance.sizes.hyprlandGapsOut * 2) : baseBarHeight
|
||||
property real barCenterSideModuleWidth: Config.options?.bar.verbose ? 360 : 140
|
||||
property real barCenterSideModuleWidthShortened: 280
|
||||
property real barCenterSideModuleWidthHellaShortened: 190
|
||||
property real barShortenScreenWidthThreshold: 1200 // Shorten if screen width is at most this value
|
||||
@@ -0,0 +1,253 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property string filePath: Directories.shellConfigPath
|
||||
property alias options: configOptionsJsonAdapter
|
||||
property bool ready: false
|
||||
|
||||
function setNestedValue(nestedKey, value) {
|
||||
let keys = nestedKey.split(".");
|
||||
let obj = root.options;
|
||||
let parents = [obj];
|
||||
|
||||
// Traverse and collect parent objects
|
||||
for (let i = 0; i < keys.length - 1; ++i) {
|
||||
if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") {
|
||||
obj[keys[i]] = {};
|
||||
}
|
||||
obj = obj[keys[i]];
|
||||
parents.push(obj);
|
||||
}
|
||||
|
||||
// Convert value to correct type using JSON.parse when safe
|
||||
let convertedValue = value;
|
||||
if (typeof value === "string") {
|
||||
let trimmed = value.trim();
|
||||
if (trimmed === "true" || trimmed === "false" || !isNaN(Number(trimmed))) {
|
||||
try {
|
||||
convertedValue = JSON.parse(trimmed);
|
||||
} catch (e) {
|
||||
convertedValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj[keys[keys.length - 1]] = convertedValue;
|
||||
}
|
||||
|
||||
FileView {
|
||||
path: root.filePath
|
||||
watchChanges: true
|
||||
onFileChanged: reload()
|
||||
onAdapterUpdated: writeAdapter()
|
||||
onLoaded: root.ready = true
|
||||
onLoadFailed: error => {
|
||||
if (error == FileViewError.FileNotFound) {
|
||||
writeAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
JsonAdapter {
|
||||
id: configOptionsJsonAdapter
|
||||
property JsonObject policies: JsonObject {
|
||||
property int ai: 1 // 0: No | 1: Yes | 2: Local
|
||||
property int weeb: 1 // 0: No | 1: Open | 2: Closet
|
||||
}
|
||||
|
||||
property JsonObject ai: JsonObject {
|
||||
property string systemPrompt: "## Style\n- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question\n\n## Presentation\n- Use Markdown features in your response: \n - **Bold** text to **highlight keywords** in your response\n - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n\nThanks!\n\n## Tools\nMay or may not be available depending on the user's settings. If they're available, follow these guidelines:\n\n### Search\n- When user asks for information that might benefit from up-to-date information, use this to get search access\n\n### Shell configuration\n- Always fetch the config options to see the available keys before setting\n- Avoid unnecessarily asking the user to confirm the changes they explicitly asked for, just do it\n"
|
||||
}
|
||||
|
||||
property JsonObject appearance: JsonObject {
|
||||
property bool extraBackgroundTint: true
|
||||
property int fakeScreenRounding: 2 // 0: None | 1: Always | 2: When not fullscreen
|
||||
property bool transparency: false
|
||||
property JsonObject wallpaperTheming: JsonObject {
|
||||
property bool enableAppsAndShell: true
|
||||
property bool enableQtApps: true
|
||||
property bool enableTerminal: true
|
||||
}
|
||||
property JsonObject palette: JsonObject {
|
||||
property string type: "auto" // Allowed: auto, scheme-content, scheme-expressive, scheme-fidelity, scheme-fruit-salad, scheme-monochrome, scheme-neutral, scheme-rainbow, scheme-tonal-spot
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject audio: JsonObject {
|
||||
// Values in %
|
||||
property JsonObject protection: JsonObject {
|
||||
// Prevent sudden bangs
|
||||
property bool enable: true
|
||||
property real maxAllowedIncrease: 10
|
||||
property real maxAllowed: 90 // Realistically should already provide some protection when it's 99...
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject apps: JsonObject {
|
||||
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
||||
property string network: "plasmawindowed org.kde.plasma.networkmanagement"
|
||||
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
|
||||
property string taskManager: "plasma-systemmonitor --page-name Processes"
|
||||
property string terminal: "kitty -1" // This is only for shell actions
|
||||
}
|
||||
|
||||
property JsonObject background: JsonObject {
|
||||
property bool fixedClockPosition: false
|
||||
property real clockX: -500
|
||||
property real clockY: -500
|
||||
property string wallpaperPath: ""
|
||||
property JsonObject parallax: JsonObject {
|
||||
property bool enableWorkspace: true
|
||||
property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size
|
||||
property bool enableSidebar: true
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject bar: JsonObject {
|
||||
property bool bottom: false // Instead of top
|
||||
property int cornerStyle: 0 // 0: Hug | 1: Float | 2: Plain rectangle
|
||||
property bool borderless: false // true for no grouping of items
|
||||
property string topLeftIcon: "spark" // Options: distro, spark
|
||||
property bool showBackground: true
|
||||
property bool verbose: true
|
||||
property JsonObject resources: JsonObject {
|
||||
property bool alwaysShowSwap: true
|
||||
property bool alwaysShowCpu: false
|
||||
}
|
||||
property list<string> screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
|
||||
property JsonObject utilButtons: JsonObject {
|
||||
property bool showScreenSnip: true
|
||||
property bool showColorPicker: false
|
||||
property bool showMicToggle: false
|
||||
property bool showKeyboardToggle: true
|
||||
property bool showDarkModeToggle: true
|
||||
}
|
||||
property JsonObject tray: JsonObject {
|
||||
property bool monochromeIcons: true
|
||||
}
|
||||
property JsonObject workspaces: JsonObject {
|
||||
property bool monochromeIcons: true
|
||||
property int shown: 10
|
||||
property bool showAppIcons: true
|
||||
property bool alwaysShowNumbers: false
|
||||
property int showNumberDelay: 300 // milliseconds
|
||||
}
|
||||
property JsonObject weather: JsonObject {
|
||||
property bool enable: false
|
||||
property bool enableGPS: true // gps based location
|
||||
property string city: "" // When 'enableGPS' is false
|
||||
property bool useUSCS: false // Instead of metric (SI) units
|
||||
property int fetchInterval: 10 // minutes
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject battery: JsonObject {
|
||||
property int low: 20
|
||||
property int critical: 5
|
||||
property bool automaticSuspend: true
|
||||
property int suspend: 3
|
||||
}
|
||||
|
||||
property JsonObject dock: JsonObject {
|
||||
property bool enable: false
|
||||
property bool monochromeIcons: true
|
||||
property real height: 60
|
||||
property real hoverRegionHeight: 2
|
||||
property bool pinnedOnStartup: false
|
||||
property bool hoverToReveal: true // When false, only reveals on empty workspace
|
||||
property list<string> pinnedApps: [ // IDs of pinned entries
|
||||
"org.kde.dolphin", "kitty",]
|
||||
property list<string> ignoredAppRegexes: []
|
||||
}
|
||||
|
||||
property JsonObject language: JsonObject {
|
||||
property JsonObject translator: JsonObject {
|
||||
property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google
|
||||
property string targetLanguage: "auto" // Run `trans -list-all` for available languages
|
||||
property string sourceLanguage: "auto"
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject light: JsonObject {
|
||||
property JsonObject night: JsonObject {
|
||||
property bool automatic: true
|
||||
property string from: "19:00" // Format: "HH:mm", 24-hour time
|
||||
property string to: "06:30" // Format: "HH:mm", 24-hour time
|
||||
property int colorTemperature: 5000
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject networking: JsonObject {
|
||||
property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
||||
}
|
||||
|
||||
property JsonObject osd: JsonObject {
|
||||
property int timeout: 1000
|
||||
}
|
||||
|
||||
property JsonObject osk: JsonObject {
|
||||
property string layout: "qwerty_full"
|
||||
property bool pinnedOnStartup: false
|
||||
}
|
||||
|
||||
property JsonObject overview: JsonObject {
|
||||
property real scale: 0.18 // Relative to screen size
|
||||
property real rows: 2
|
||||
property real columns: 5
|
||||
}
|
||||
|
||||
property JsonObject resources: JsonObject {
|
||||
property int updateInterval: 3000
|
||||
}
|
||||
|
||||
property JsonObject search: JsonObject {
|
||||
property int nonAppResultDelay: 30 // This prevents lagging when typing
|
||||
property string engineBaseUrl: "https://www.google.com/search?q="
|
||||
property list<string> excludedSites: ["quora.com"]
|
||||
property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird.
|
||||
property JsonObject prefix: JsonObject {
|
||||
property string action: "/"
|
||||
property string clipboard: ";"
|
||||
property string emojis: ":"
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject sidebar: JsonObject {
|
||||
property JsonObject translator: JsonObject {
|
||||
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
|
||||
}
|
||||
property JsonObject booru: JsonObject {
|
||||
property bool allowNsfw: false
|
||||
property string defaultProvider: "yandere"
|
||||
property int limit: 20
|
||||
property JsonObject zerochan: JsonObject {
|
||||
property string username: "[unset]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject time: JsonObject {
|
||||
// https://doc.qt.io/qt-6/qtime.html#toString
|
||||
property string format: "hh:mm"
|
||||
property string dateFormat: "ddd, dd/MM"
|
||||
}
|
||||
|
||||
property JsonObject windows: JsonObject {
|
||||
property bool showTitlebar: true // Client-side decoration for shell apps
|
||||
property bool centerTitle: true
|
||||
}
|
||||
|
||||
property JsonObject hacks: JsonObject {
|
||||
property int arbitraryRaceConditionDelay: 20 // milliseconds
|
||||
}
|
||||
|
||||
property JsonObject screenshotTool: JsonObject {
|
||||
property bool showContentRegions: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||
import qs.modules.common.functions
|
||||
import Qt.labs.platform
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Singleton {
|
||||
// XDG Dirs, with "file://"
|
||||
@@ -16,6 +15,8 @@ Singleton {
|
||||
readonly property string downloads: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
|
||||
|
||||
// Other dirs used by the shell, without "file://"
|
||||
property string assetsPath: Quickshell.shellPath("assets")
|
||||
property string scriptPath: Quickshell.shellPath("scripts")
|
||||
property string favicons: FileUtils.trimFileProtocol(`${Directories.cache}/media/favicons`)
|
||||
property string coverArt: FileUtils.trimFileProtocol(`${Directories.cache}/media/coverart`)
|
||||
property string booruPreviews: FileUtils.trimFileProtocol(`${Directories.cache}/media/boorus`)
|
||||
@@ -29,15 +30,20 @@ Singleton {
|
||||
property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`)
|
||||
property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`)
|
||||
property string cliphistDecode: FileUtils.trimFileProtocol(`/tmp/quickshell/media/cliphist`)
|
||||
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/colors/switchwall.sh`)
|
||||
property string screenshotTemp: "/tmp/quickshell/media/screenshot"
|
||||
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`)
|
||||
property string defaultAiPrompts: Quickshell.shellPath("defaults/ai/prompts")
|
||||
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
|
||||
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
|
||||
// Cleanup on init
|
||||
Component.onCompleted: {
|
||||
Hyprland.dispatch(`exec mkdir -p '${shellConfig}'`)
|
||||
Hyprland.dispatch(`exec mkdir -p '${favicons}'`)
|
||||
Hyprland.dispatch(`exec rm -rf '${coverArt}'; mkdir -p '${coverArt}'`)
|
||||
Hyprland.dispatch(`exec rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`)
|
||||
Hyprland.dispatch(`exec mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`)
|
||||
Hyprland.dispatch(`exec rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`)
|
||||
Hyprland.dispatch(`exec rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`)
|
||||
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
|
||||
Quickshell.execDetached(["mkdir", "-p", `${favicons}`])
|
||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`])
|
||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`])
|
||||
Quickshell.execDetached(["bash", "-c", `mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`])
|
||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
|
||||
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
|
||||
Quickshell.execDetached(["mkdir", "-p", `${aiChats}`])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property alias states: persistentStatesJsonAdapter
|
||||
property string fileDir: Directories.state
|
||||
property string fileName: "states.json"
|
||||
property string filePath: `${root.fileDir}/${root.fileName}`
|
||||
|
||||
FileView {
|
||||
path: root.filePath
|
||||
|
||||
watchChanges: true
|
||||
onFileChanged: reload()
|
||||
onAdapterUpdated: {
|
||||
writeAdapter()
|
||||
}
|
||||
onLoadFailed: error => {
|
||||
console.log("Failed to load persistent states file:", error);
|
||||
if (error == FileViewError.FileNotFound) {
|
||||
writeAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
adapter: JsonAdapter {
|
||||
id: persistentStatesJsonAdapter
|
||||
property JsonObject ai: JsonObject {
|
||||
property string model
|
||||
property real temperature: 0.5
|
||||
}
|
||||
|
||||
property JsonObject sidebar: JsonObject {
|
||||
property JsonObject bottomGroup: JsonObject {
|
||||
property bool collapsed: false
|
||||
property int tab: 0
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject booru: JsonObject {
|
||||
property bool allowNsfw: false
|
||||
property string provider: "yandere"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
pragma Singleton
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* Returns a color with the hue of color2 and the saturation, value, and alpha of color1.
|
||||
*
|
||||
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||
* @param {string} color2 - The color to take hue from.
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function colorWithHueOf(color1, color2) {
|
||||
var c1 = Qt.color(color1);
|
||||
var c2 = Qt.color(color2);
|
||||
|
||||
// Qt.color hsvHue/hsvSaturation/hsvValue/alpha return 0-1
|
||||
var hue = c2.hsvHue;
|
||||
var sat = c1.hsvSaturation;
|
||||
var val = c1.hsvValue;
|
||||
var alpha = c1.a;
|
||||
|
||||
return Qt.hsva(hue, sat, val, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color with the saturation of color2 and the hue/value/alpha of color1.
|
||||
*
|
||||
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||
* @param {string} color2 - The color to take saturation from.
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function colorWithSaturationOf(color1, color2) {
|
||||
var c1 = Qt.color(color1);
|
||||
var c2 = Qt.color(color2);
|
||||
|
||||
var hue = c1.hsvHue;
|
||||
var sat = c2.hsvSaturation;
|
||||
var val = c1.hsvValue;
|
||||
var alpha = c1.a;
|
||||
|
||||
return Qt.hsva(hue, sat, val, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color with the given lightness and the hue, saturation, and alpha of the input color (using HSL).
|
||||
*
|
||||
* @param {string} color - The base color (any Qt.color-compatible string).
|
||||
* @param {number} lightness - The lightness value to use (0-1).
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function colorWithLightness(color, lightness) {
|
||||
var c = Qt.color(color);
|
||||
return Qt.hsla(c.hslHue, c.hslSaturation, lightness, c.a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color with the lightness of color2 and the hue, saturation, and alpha of color1 (using HSL).
|
||||
*
|
||||
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||
* @param {string} color2 - The color to take lightness from.
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function colorWithLightnessOf(color1, color2) {
|
||||
var c2 = Qt.color(color2);
|
||||
return colorWithLightness(color1, c2.hslLightness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts color1 to the accent (hue and saturation) of color2 using HSL, keeping lightness and alpha from color1.
|
||||
*
|
||||
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||
* @param {string} color2 - The accent color.
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function adaptToAccent(color1, color2) {
|
||||
var c1 = Qt.color(color1);
|
||||
var c2 = Qt.color(color2);
|
||||
|
||||
var hue = c2.hslHue;
|
||||
var sat = c2.hslSaturation;
|
||||
var light = c1.hslLightness;
|
||||
var alpha = c1.a;
|
||||
|
||||
return Qt.hsla(hue, sat, light, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes two colors by a given percentage.
|
||||
*
|
||||
* @param {string} color1 - The first color (any Qt.color-compatible string).
|
||||
* @param {string} color2 - The second color.
|
||||
* @param {number} percentage - The mix ratio (0-1). 1 = all color1, 0 = all color2.
|
||||
* @returns {Qt.rgba} The resulting mixed color.
|
||||
*/
|
||||
function mix(color1, color2, percentage = 0.5) {
|
||||
var c1 = Qt.color(color1);
|
||||
var c2 = Qt.color(color2);
|
||||
return Qt.rgba(percentage * c1.r + (1 - percentage) * c2.r, percentage * c1.g + (1 - percentage) * c2.g, percentage * c1.b + (1 - percentage) * c2.b, percentage * c1.a + (1 - percentage) * c2.a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transparentizes a color by a given percentage.
|
||||
*
|
||||
* @param {string} color - The color (any Qt.color-compatible string).
|
||||
* @param {number} percentage - The amount to transparentize (0-1).
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function transparentize(color, percentage = 1) {
|
||||
var c = Qt.color(color);
|
||||
return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
pragma Singleton
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* Trims the File protocol off the input string
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function trimFileProtocol(str) {
|
||||
return str.startsWith("file://") ? str.slice(7) : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the file name from a file path
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function fileNameForPath(str) {
|
||||
if (typeof str !== "string") return "";
|
||||
const trimmed = trimFileProtocol(str);
|
||||
return trimmed.split(/[\\/]/).pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the file extension from a file path or name
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function trimFileExt(str) {
|
||||
if (typeof str !== "string") return "";
|
||||
const trimmed = trimFileProtocol(str);
|
||||
const lastDot = trimmed.lastIndexOf(".");
|
||||
if (lastDot > -1 && lastDot > trimmed.lastIndexOf("/")) {
|
||||
return trimmed.slice(0, lastDot);
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
pragma Singleton
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
function toPlainObject(qtObj) {
|
||||
if (qtObj === null || typeof qtObj !== "object") return qtObj;
|
||||
|
||||
// Handle true arrays
|
||||
if (Array.isArray(qtObj)) {
|
||||
return qtObj.map(item => toPlainObject(item));
|
||||
}
|
||||
|
||||
// Handle array-like Qt objects (e.g., have length and numeric keys)
|
||||
if (
|
||||
typeof qtObj.length === "number" &&
|
||||
qtObj.length > 0 &&
|
||||
Object.keys(qtObj).every(
|
||||
key => !isNaN(key) || key === "length"
|
||||
)
|
||||
) {
|
||||
let arr = [];
|
||||
for (let i = 0; i < qtObj.length; i++) {
|
||||
arr.push(toPlainObject(qtObj[i]));
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const result = ({});
|
||||
for (let key in qtObj) {
|
||||
if (
|
||||
typeof qtObj[key] !== "function" &&
|
||||
!key.startsWith("objectName") &&
|
||||
!key.startsWith("children") &&
|
||||
!key.startsWith("object") &&
|
||||
!key.startsWith("parent") &&
|
||||
!key.startsWith("metaObject") &&
|
||||
!key.startsWith("destroyed") &&
|
||||
!key.startsWith("reloadableId")
|
||||
) {
|
||||
result[key] = toPlainObject(qtObj[key]);
|
||||
}
|
||||
}
|
||||
// console.log(JSON.stringify(result))
|
||||
return result;
|
||||
}
|
||||
|
||||
function applyToQtObject(qtObj, jsonObj) {
|
||||
// console.log("applyToQtObject", JSON.stringify(qtObj, null, 2), "<<", JSON.stringify(jsonObj, null, 2));
|
||||
if (!qtObj || typeof jsonObj !== "object" || jsonObj === null) return;
|
||||
|
||||
// Detect array-like Qt objects
|
||||
const isQtArrayLike = obj => {
|
||||
return obj && typeof obj === "object" &&
|
||||
typeof obj.length === "number" &&
|
||||
obj.length > 0 &&
|
||||
Object.keys(obj).every(key => !isNaN(key) || key === "length");
|
||||
};
|
||||
|
||||
// If both are arrays or array-like, update in place or replace
|
||||
if ((Array.isArray(qtObj) || isQtArrayLike(qtObj)) && Array.isArray(jsonObj)) {
|
||||
qtObj.length = 0;
|
||||
for (let i = 0; i < jsonObj.length; i++) {
|
||||
qtObj.push(jsonObj[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If target is array or array-like but source is not, clear
|
||||
if ((Array.isArray(qtObj) || isQtArrayLike(qtObj)) && !Array.isArray(jsonObj)) {
|
||||
qtObj.length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// If source is array but target is not, assign directly if possible
|
||||
if (!(Array.isArray(qtObj) || isQtArrayLike(qtObj)) && Array.isArray(jsonObj)) {
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
for (let key in jsonObj) {
|
||||
if (!qtObj.hasOwnProperty(key)) continue;
|
||||
const value = qtObj[key];
|
||||
const jsonValue = jsonObj[key];
|
||||
// console.log("applying to qt obj key:", value, "jsonValue:", jsonValue);
|
||||
if ((Array.isArray(value) || isQtArrayLike(value)) && Array.isArray(jsonValue)) {
|
||||
value.length = 0;
|
||||
for (let i = 0; i < jsonValue.length; i++) {
|
||||
value.push(jsonValue[i]);
|
||||
}
|
||||
} else if (value && typeof value === "object" && !Array.isArray(value) && !isQtArrayLike(value)) {
|
||||
applyToQtObject(value, jsonValue);
|
||||
} else {
|
||||
qtObj[key] = jsonValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
pragma Singleton
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* Formats a string according to the args that are passed inc
|
||||
* @param { string } str
|
||||
* @param {...any} args
|
||||
* @returns
|
||||
*/
|
||||
function format(str, ...args) {
|
||||
return str.replace(/{(\d+)}/g, (match, index) => typeof args[index] !== 'undefined' ? args[index] : match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain of the passed in url or null
|
||||
* @param { string } url
|
||||
* @returns { string| null }
|
||||
*/
|
||||
function getDomain(url) {
|
||||
const match = url.match(/^(?:https?:\/\/)?(?:www\.)?([^\/]+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base url of the passed in url or null
|
||||
* @param { string } url
|
||||
* @returns { string | null }
|
||||
*/
|
||||
function getBaseUrl(url) {
|
||||
const match = url.match(/^(https?:\/\/[^\/]+)(\/.*)?$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes single quotes in shell commands
|
||||
* @param { string } str
|
||||
* @returns { string }
|
||||
*/
|
||||
function shellSingleQuoteEscape(str) {
|
||||
// escape single quotes
|
||||
return String(str)
|
||||
// .replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "'\\''");
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits markdown blocks into three different types: text, think, and code.
|
||||
* @param { string } markdown
|
||||
*/
|
||||
function splitMarkdownBlocks(markdown) {
|
||||
const regex = /```(\w+)?\n([\s\S]*?)```|<think>([\s\S]*?)<\/think>/g;
|
||||
/**
|
||||
* @type {{type: "text" | "think" | "code"; content: string; lang: string | undefined; completed: boolean | undefined}[]}
|
||||
*/
|
||||
let result = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
while ((match = regex.exec(markdown)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
const text = markdown.slice(lastIndex, match.index);
|
||||
if (text.trim()) {
|
||||
result.push({
|
||||
type: "text",
|
||||
content: text
|
||||
});
|
||||
}
|
||||
}
|
||||
if (match[0].startsWith('```')) {
|
||||
if (match[2] && match[2].trim()) {
|
||||
result.push({
|
||||
type: "code",
|
||||
lang: match[1] || "",
|
||||
content: match[2],
|
||||
completed: true
|
||||
});
|
||||
}
|
||||
} else if (match[0].startsWith('<think>')) {
|
||||
if (match[3] && match[3].trim()) {
|
||||
result.push({
|
||||
type: "think",
|
||||
content: match[3],
|
||||
completed: true
|
||||
});
|
||||
}
|
||||
}
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
// Handle any remaining text after the last match
|
||||
if (lastIndex < markdown.length) {
|
||||
const text = markdown.slice(lastIndex);
|
||||
// Check for unfinished <think> block
|
||||
const thinkStart = text.indexOf('<think>');
|
||||
const codeStart = text.indexOf('```');
|
||||
if (thinkStart !== -1 && (codeStart === -1 || thinkStart < codeStart)) {
|
||||
const beforeThink = text.slice(0, thinkStart);
|
||||
if (beforeThink.trim()) {
|
||||
result.push({
|
||||
type: "text",
|
||||
content: beforeThink
|
||||
});
|
||||
}
|
||||
const thinkContent = text.slice(thinkStart + 7);
|
||||
if (thinkContent.trim()) {
|
||||
result.push({
|
||||
type: "think",
|
||||
content: thinkContent,
|
||||
completed: false
|
||||
});
|
||||
}
|
||||
} else if (codeStart !== -1) {
|
||||
const beforeCode = text.slice(0, codeStart);
|
||||
if (beforeCode.trim()) {
|
||||
result.push({
|
||||
type: "text",
|
||||
content: beforeCode
|
||||
});
|
||||
}
|
||||
// Try to detect language after ```
|
||||
const codeLangMatch = text.slice(codeStart + 3).match(/^(\w+)?\n/);
|
||||
let lang = "";
|
||||
let codeContentStart = codeStart + 3;
|
||||
if (codeLangMatch) {
|
||||
lang = codeLangMatch[1] || "";
|
||||
codeContentStart += codeLangMatch[0].length;
|
||||
} else if (text[codeStart + 3] === '\n') {
|
||||
codeContentStart += 1;
|
||||
}
|
||||
const codeContent = text.slice(codeContentStart);
|
||||
if (codeContent.trim()) {
|
||||
result.push({
|
||||
type: "code",
|
||||
lang,
|
||||
content: codeContent,
|
||||
completed: false
|
||||
});
|
||||
}
|
||||
} else if (text.trim()) {
|
||||
result.push({
|
||||
type: "text",
|
||||
content: text
|
||||
});
|
||||
}
|
||||
}
|
||||
// console.log(JSON.stringify(result, null, 2));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original string with backslashes escaped
|
||||
* @param { string } str
|
||||
* @returns { string }
|
||||
*/
|
||||
function escapeBackslashes(str) {
|
||||
return str.replace(/\\/g, '\\\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps words to supplied maximum length
|
||||
* @param { string | null } str
|
||||
* @param { number } maxLen
|
||||
* @returns { string }
|
||||
*/
|
||||
function wordWrap(str, maxLen) {
|
||||
if (!str)
|
||||
return "";
|
||||
let words = str.split(" ");
|
||||
let lines = [];
|
||||
let current = "";
|
||||
for (let i = 0; i < words.length; ++i) {
|
||||
if ((current + (current.length > 0 ? " " : "") + words[i]).length > maxLen) {
|
||||
if (current.length > 0)
|
||||
lines.push(current);
|
||||
current = words[i];
|
||||
} else {
|
||||
current += (current.length > 0 ? " " : "") + words[i];
|
||||
}
|
||||
}
|
||||
if (current.length > 0)
|
||||
lines.push(current);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function cleanMusicTitle(title) {
|
||||
if (!title)
|
||||
return "";
|
||||
// Brackets
|
||||
title = title.replace(/^ *\([^)]*\) */g, " "); // Round brackets
|
||||
title = title.replace(/^ *\[[^\]]*\] */g, " "); // Square brackets
|
||||
title = title.replace(/^ *\{[^\}]*\} */g, " "); // Curly brackets
|
||||
// Japenis brackets
|
||||
title = title.replace(/^ *【[^】]*】/, ""); // Touhou
|
||||
title = title.replace(/^ *《[^》]*》/, ""); // ??
|
||||
title = title.replace(/^ *「[^」]*」/, ""); // OP/ED thingie
|
||||
title = title.replace(/^ *『[^』]*』/, ""); // OP/ED thingie
|
||||
|
||||
return title.trim();
|
||||
}
|
||||
|
||||
function friendlyTimeForSeconds(seconds) {
|
||||
if (isNaN(seconds) || seconds < 0)
|
||||
return "0:00";
|
||||
seconds = Math.floor(seconds);
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = seconds % 60;
|
||||
if (h > 0) {
|
||||
return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
||||
} else {
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (typeof str !== 'string')
|
||||
return str;
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
/**
|
||||
@@ -20,6 +18,7 @@ Rectangle {
|
||||
let total = 0;
|
||||
for (let i = 0; i < rowLayout.children.length; ++i) {
|
||||
const child = rowLayout.children[i];
|
||||
if (!child.visible) continue;
|
||||
total += child.baseWidth ?? child.implicitWidth ?? child.width;
|
||||
}
|
||||
return total + rowLayout.spacing * (rowLayout.children.length - 1);
|
||||
@@ -2,7 +2,7 @@
|
||||
// License: LGPL-3.0 - A copy can be found in `licenses` folder of repo
|
||||
|
||||
import QtQuick
|
||||
import "root:/modules/common"
|
||||
import qs.modules.common
|
||||
|
||||
/**
|
||||
* Material 3 circular progress. See https://m3.material.io/components/progress-indicators/specs
|
||||
@@ -18,6 +18,7 @@ Item {
|
||||
property real gapAngle: Math.PI / 9
|
||||
property bool fill: false
|
||||
property int fillOverflow: 2
|
||||
property bool enableAnimation: true
|
||||
property int animationDuration: 1000
|
||||
property var easingType: Easing.OutCubic
|
||||
|
||||
@@ -83,6 +84,7 @@ Item {
|
||||
}
|
||||
|
||||
Behavior on degree {
|
||||
enabled: root.enableAnimation
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: root.easingType
|
||||
@@ -1,16 +1,11 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Qt.labs.platform
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -71,7 +66,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
Hyprland.dispatch(`exec bash -c "[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'"`)
|
||||
Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`])
|
||||
}
|
||||
|
||||
Image {
|
||||
@@ -0,0 +1,8 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
RowLayout {
|
||||
property bool uniform: false
|
||||
spacing: 10
|
||||
uniformCellSizes: uniform
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
|
||||
Flow {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
property list<var> options: []
|
||||
property string configOptionName: ""
|
||||
property var currentValue: null
|
||||
|
||||
signal selected(var newValue)
|
||||
|
||||
Repeater {
|
||||
model: root.options
|
||||
delegate: SelectionGroupButton {
|
||||
id: paletteButton
|
||||
required property var modelData
|
||||
required property int index
|
||||
onYChanged: {
|
||||
if (index === 0) {
|
||||
paletteButton.leftmost = true
|
||||
} else {
|
||||
var prev = root.children[index - 1]
|
||||
var thisIsOnNewLine = prev && prev.y !== paletteButton.y
|
||||
paletteButton.leftmost = thisIsOnNewLine
|
||||
prev.rightmost = thisIsOnNewLine
|
||||
}
|
||||
}
|
||||
leftmost: index === 0
|
||||
rightmost: index === root.options.length - 1
|
||||
buttonText: modelData.displayName;
|
||||
toggled: root.currentValue === modelData.value
|
||||
onClicked: {
|
||||
root.selected(modelData.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
property string text: ""
|
||||
property alias value: spinBoxWidget.value
|
||||
property alias stepSize: spinBoxWidget.stepSize
|
||||
property alias from: spinBoxWidget.from
|
||||
property alias to: spinBoxWidget.to
|
||||
spacing: 10
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
|
||||
StyledText {
|
||||
id: labelWidget
|
||||
Layout.fillWidth: true
|
||||
text: root.text
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnSecondaryContainer
|
||||
}
|
||||
|
||||
StyledSpinBox {
|
||||
id: spinBoxWidget
|
||||
Layout.fillWidth: false
|
||||
value: root.value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
|
||||
RippleButton {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: contentItem.implicitHeight + 8 * 2
|
||||
onClicked: checked = !checked
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 10
|
||||
StyledText {
|
||||
id: labelWidget
|
||||
Layout.fillWidth: true
|
||||
text: root.text
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnSecondaryContainer
|
||||
}
|
||||
StyledSwitch {
|
||||
id: switchWidget
|
||||
down: root.down
|
||||
scale: 0.6
|
||||
Layout.fillWidth: false
|
||||
checked: root.checked
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Flickable {
|
||||
id: root
|
||||
property real baseWidth: 550
|
||||
property bool forceWidth: false
|
||||
property real bottomContentPadding: 100
|
||||
|
||||
default property alias data: contentColumn.data
|
||||
|
||||
clip: true
|
||||
contentHeight: contentColumn.implicitHeight + root.bottomContentPadding // Add some padding at the bottom
|
||||
implicitWidth: contentColumn.implicitWidth
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
width: root.forceWidth ? root.baseWidth : Math.max(root.baseWidth, implicitWidth)
|
||||
anchors {
|
||||
top: parent.top
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
margins: 10
|
||||
}
|
||||
spacing: 20
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
property string title
|
||||
default property alias data: sectionContent.data
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
StyledText {
|
||||
text: root.title
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
ColumnLayout {
|
||||
id: sectionContent
|
||||
spacing: 8
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
property string title: ""
|
||||
property string tooltip: ""
|
||||
default property alias data: sectionContent.data
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 4
|
||||
spacing: 2
|
||||
|
||||
RowLayout {
|
||||
ContentSubsectionLabel {
|
||||
visible: root.title && root.title.length > 0
|
||||
text: root.title
|
||||
}
|
||||
MaterialSymbol {
|
||||
visible: root.tooltip && root.tooltip.length > 0
|
||||
text: "info"
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
|
||||
color: Appearance.colors.colSubtext
|
||||
MouseArea {
|
||||
id: infoMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.WhatsThisCursor
|
||||
StyledToolTip {
|
||||
extraVisibleCondition: false
|
||||
alternativeVisibleCondition: infoMouseArea.containsMouse
|
||||
content: root.tooltip
|
||||
}
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
ColumnLayout {
|
||||
id: sectionContent
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
StyledText {
|
||||
text: "Subsection"
|
||||
color: Appearance.colors.colSubtext
|
||||
Layout.leftMargin: 4
|
||||
}
|
||||