[SCRIPT] Updated
This commit is contained in:
+53
-27
@@ -27,38 +27,41 @@ $launchpad = nwggrid
|
|||||||
|
|
||||||
# Cursor
|
# Cursor
|
||||||
env = XCURSOR_SIZE,24
|
env = XCURSOR_SIZE,24
|
||||||
env = XCURSOR_THEME,Adwaita
|
env = XCURSOR_THEME,macOS
|
||||||
|
env = HYPRCURSOR_SIZE,24
|
||||||
|
env = HYPRCURSOR_THEME,macOS
|
||||||
|
|
||||||
# Nvidia
|
# Nvidia
|
||||||
env = LIBVA_DRIVER_NAME,nvidia
|
env = LIBVA_DRIVER_NAME,nvidia
|
||||||
env = XDG_SESSION_TYPE,wayland
|
env = XDG_SESSION_TYPE,wayland
|
||||||
env = GBM_BACKEND,nvidia-drm
|
env = GBM_BACKEND,nvidia-drm
|
||||||
env = __GLX_VENDOR_LIBRARY_NAME,nvidia
|
env = __GLX_VENDOR_LIBRARY_NAME,nvidia
|
||||||
env = NVD_BACKEND,direct
|
# env = NVD_BACKEND,direct
|
||||||
|
|
||||||
# Electron
|
# Electron
|
||||||
env = ELECTRON_OZONE_PLATFORM_HINT,auto
|
# env = ELECTRON_OZONE_PLATFORM_HINT,auto
|
||||||
|
|
||||||
# Firefox
|
# Firefox
|
||||||
env = MOZ_DISABLE_RDD_SANDBOX,1
|
# env = MOZ_DISABLE_RDD_SANDBOX,1
|
||||||
env = EGL_PLATFORM,wayland
|
# env = EGL_PLATFORM,wayland
|
||||||
env = MOZ_ENABLE_WAYLAND,1
|
# env = MOZ_ENABLE_WAYLAND,1
|
||||||
|
|
||||||
# For qt apps
|
# For qt apps
|
||||||
env = QT_QPA_PLATFORM,wayland
|
# env = QT_QPA_PLATFORM,wayland
|
||||||
env = QT_QPA_PLATFORMTHEME,qt5ct
|
# env = QT_QPA_PLATFORMTHEME,qt5ct
|
||||||
|
|
||||||
env = GTK_THEME,Material
|
env = GTK_THEME,Material
|
||||||
|
|
||||||
env = XDG_CURRENT_DESKTOP,Hyprland
|
env = XDG_CURRENT_DESKTOP,Hyprland
|
||||||
env = XDG_SESSION_DESKTOP,Hyprland
|
env = XDG_SESSION_DESKTOP,Hyprland
|
||||||
|
|
||||||
|
env = bitdepth,10
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
# EXEC
|
# EXEC
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
exec-once = ignis init
|
exec-once = ignis init
|
||||||
exec-once=/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
|
|
||||||
exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
|
exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
|
||||||
exec-once = telegram-desktop -startintray
|
exec-once = telegram-desktop -startintray
|
||||||
exec-once = nm-applet
|
exec-once = nm-applet
|
||||||
@@ -70,14 +73,16 @@ exec-once = wl-paste --type text --watch cliphist store
|
|||||||
exec-once = wl-paste --type image --watch cliphist store
|
exec-once = wl-paste --type image --watch cliphist store
|
||||||
exec-once = fcitx5
|
exec-once = fcitx5
|
||||||
|
|
||||||
exec-once = hyprswitch init --show-title &
|
exec-once = hyprswitch init --show-title --size-factor 5.5 --workspaces-per-row 5 &
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
# INPUT
|
# INPUT
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
cursor {
|
cursor {
|
||||||
no_hardware_cursors = true
|
no_hardware_cursors = false
|
||||||
|
use_cpu_buffer = true
|
||||||
|
warp_on_change_workspace = true
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@@ -122,15 +127,15 @@ decoration {
|
|||||||
|
|
||||||
blur {
|
blur {
|
||||||
enabled = true
|
enabled = true
|
||||||
size = 12
|
size = 12 # 12
|
||||||
passes = 4
|
passes = 4
|
||||||
new_optimizations = true
|
new_optimizations = true
|
||||||
}
|
}
|
||||||
|
|
||||||
shadow {
|
shadow {
|
||||||
enabled = true
|
enabled = false # true
|
||||||
range = 30
|
range = 30 # 30
|
||||||
render_power = 4
|
render_power = 4 # 4
|
||||||
color = rgb(000000)
|
color = rgb(000000)
|
||||||
}
|
}
|
||||||
# old shadow settings
|
# old shadow settings
|
||||||
@@ -145,13 +150,16 @@ decoration {
|
|||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
animations {
|
animations {
|
||||||
|
# animation = NAME, ONOFF, SPEED, CURVE [, STYLE]
|
||||||
enabled = yes
|
enabled = yes
|
||||||
|
|
||||||
|
# bezier = quart, 0.25, 1, 0.5, 1
|
||||||
bezier = quart, 0.25, 1, 0.5, 1
|
bezier = quart, 0.25, 1, 0.5, 1
|
||||||
|
|
||||||
animation = windows, 1, 6, quart, slide
|
animation = windows, 1, 4, quart, slide
|
||||||
|
# animation = windows, 1, 4, quart, gnomed # hyprland 0.48.0 introduces gnomed
|
||||||
animation = border, 1, 6, quart
|
animation = border, 1, 6, quart
|
||||||
animation = borderangle, 1, 6, quart
|
# animation = borderangle, 1, 6, quart
|
||||||
animation = fade, 1, 6, quart
|
animation = fade, 1, 6, quart
|
||||||
animation = workspaces, 1, 6, quart
|
animation = workspaces, 1, 6, quart
|
||||||
}
|
}
|
||||||
@@ -165,6 +173,13 @@ dwindle {
|
|||||||
preserve_split = yes
|
preserve_split = yes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# MASTER
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
master {
|
||||||
|
new_status = master
|
||||||
|
}
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
# MISC
|
# MISC
|
||||||
@@ -182,24 +197,33 @@ render {
|
|||||||
explicit_sync = true
|
explicit_sync = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# EXPERIMENTAL
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# experimental {
|
||||||
|
# hdr = false
|
||||||
|
# }
|
||||||
|
|
||||||
#----------------------b-------------------------------------------------------------------------------------
|
#----------------------b-------------------------------------------------------------------------------------
|
||||||
# WINDOW RULES
|
# WINDOW RULES
|
||||||
#-----------------------------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
windowrule = float,^(pavucontrol)$
|
# windowrule = float,^(pavucontrol)$
|
||||||
windowrule = pin,^(pavucontrol)$
|
# windowrule = pin,^(pavucontrol)$
|
||||||
windowrule = size 900 500,^(pavucontrol)$
|
# windowrule = size 900 500,^(pavucontrol)$
|
||||||
|
|
||||||
#windowrule = float,^(kitty)$
|
#windowrule = float,^(kitty)$
|
||||||
#windowrule = size 640 400,^(kitty)$
|
#windowrule = size 640 400,^(kitty)$
|
||||||
|
|
||||||
windowrule = float,class:^(Material Settings)$
|
windowrule = float,class:^(Material Settings)$
|
||||||
|
|
||||||
windowrulev2 = opacity 0.0 override, class:^(xwaylandvideobridge)$
|
# windowrulev2 = opacity 0.0 override, class:^(xwaylandvideobridge)$
|
||||||
windowrulev2 = noanim, class:^(xwaylandvideobridge)$
|
# windowrulev2 = noanim, class:^(xwaylandvideobridge)$
|
||||||
windowrulev2 = noinitialfocus, class:^(xwaylandvideobridge)$
|
# windowrulev2 = noinitialfocus, class:^(xwaylandvideobridge)$
|
||||||
windowrulev2 = maxsize 1 1, class:^(xwaylandvideobridge)$
|
# windowrulev2 = maxsize 1 1, class:^(xwaylandvideobridge)$
|
||||||
windowrulev2 = noblur, class:^(xwaylandvideobridge)$
|
# windowrulev2 = noblur, class:^(xwaylandvideobridge)$
|
||||||
|
# windowrulev2 = nofocus, class:^(xwaylandvideobridge)$
|
||||||
|
|
||||||
layerrule = blur,^(ignis_BAR.*)$
|
layerrule = blur,^(ignis_BAR.*)$
|
||||||
layerrule = noanim,^(ignis_NOTIFICATION_POPUP.*)$
|
layerrule = noanim,^(ignis_NOTIFICATION_POPUP.*)$
|
||||||
@@ -308,6 +332,8 @@ bind = ,XF86AudioPrev, exec, playerctl previous
|
|||||||
bind = CONTROL SHIFT, ESCAPE, exec, missioncenter
|
bind = CONTROL SHIFT, ESCAPE, exec, missioncenter
|
||||||
|
|
||||||
# hyprswitch
|
# hyprswitch
|
||||||
bind = $mod, $key, exec, hyprswitch gui --mod-key $mod --key $key --close mod-key-release --reverse-key=mod=$reverse --sort-recent && hyprswitch dispatch
|
bind = $mod, $key, exec, hyprswitch gui --mod-key $mod --key $key --close mod-key-release --reverse-key=key=$reverse && hyprswitch dispatch
|
||||||
bind = $mod $reverse, $key, exec, hyprswitch gui --mod-key $mod --key $key --close mod-key-release --reverse-key=mod=$reverse --sort-recent && hyprswitch dispatch -r
|
bind = $mod $reverse, $key, exec, hyprswitch gui --mod-key $mod --key $key --close mod-key-release --reverse-key=key=$reverse && hyprswitch dispatch -r
|
||||||
|
|
||||||
|
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,325 @@
|
|||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# IMPORTING FILE WITH BORDER COLOR
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
source=~/.cache/ignis/material/dark_colors-hyprland.conf
|
||||||
|
source = ~/.config/hypr/monitors.conf
|
||||||
|
|
||||||
|
# Monitor primary
|
||||||
|
workspace = name:1, monitor:DP-3
|
||||||
|
exec-once = hyprctl dispatch workspace 1
|
||||||
|
|
||||||
|
# Set programs that you use
|
||||||
|
$terminal = kitty
|
||||||
|
$fileManager = nautilus
|
||||||
|
#$menu = tofi-drun --config .config/tofi/config | xargs hyprctl dispatch exec -- # wofi --show drun
|
||||||
|
$browser = zen-browser
|
||||||
|
#$shareclip = kdeconnect-cli --device=347E8D05994A40EEBFCFFBCE700FDA6B --send-clipboard
|
||||||
|
|
||||||
|
$key = tab
|
||||||
|
$mod = alt
|
||||||
|
$reverse = grave
|
||||||
|
$launchpad = nwggrid
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# ENVIRONMENT
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Cursor
|
||||||
|
env = XCURSOR_SIZE,24
|
||||||
|
# env = XCURSOR_THEME,Adwaita
|
||||||
|
|
||||||
|
# Nvidia
|
||||||
|
env = LIBVA_DRIVER_NAME,nvidia
|
||||||
|
env = XDG_SESSION_TYPE,wayland
|
||||||
|
env = GBM_BACKEND,nvidia-drm
|
||||||
|
env = __GLX_VENDOR_LIBRARY_NAME,nvidia
|
||||||
|
env = NVD_BACKEND,direct
|
||||||
|
|
||||||
|
# Electron
|
||||||
|
env = ELECTRON_OZONE_PLATFORM_HINT,auto
|
||||||
|
|
||||||
|
# Firefox
|
||||||
|
env = MOZ_DISABLE_RDD_SANDBOX,1
|
||||||
|
env = EGL_PLATFORM,wayland
|
||||||
|
env = MOZ_ENABLE_WAYLAND,1
|
||||||
|
|
||||||
|
# For qt apps
|
||||||
|
env = QT_QPA_PLATFORM,wayland
|
||||||
|
env = QT_QPA_PLATFORMTHEME,qt5ct
|
||||||
|
|
||||||
|
env = GTK_THEME,Material
|
||||||
|
|
||||||
|
env = XDG_CURRENT_DESKTOP,Hyprland
|
||||||
|
env = XDG_SESSION_DESKTOP,Hyprland
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# EXEC
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
exec-once = ignis init
|
||||||
|
exec-once=/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
|
||||||
|
exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
|
||||||
|
exec-once = telegram-desktop -startintray
|
||||||
|
exec-once = nm-applet
|
||||||
|
exec-once = blueman-applet
|
||||||
|
exec-once = /usr/lib/polkit-kde-authentication-agent-1
|
||||||
|
# exec-once = swww-daemon
|
||||||
|
# exec-once = swww img /home/timo/.config/hypr/wallpaper/wallpaper.jpg
|
||||||
|
exec-once = wl-paste --type text --watch cliphist store
|
||||||
|
exec-once = wl-paste --type image --watch cliphist store
|
||||||
|
exec-once = fcitx5
|
||||||
|
|
||||||
|
exec-once = hyprswitch init --show-title &
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# INPUT
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
cursor {
|
||||||
|
no_hardware_cursors = false
|
||||||
|
use_cpu_buffer = true
|
||||||
|
warp_on_change_workspace = true
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
kb_layout = de
|
||||||
|
kb_variant =
|
||||||
|
kb_model =
|
||||||
|
kb_options = grp:win_space_toggle
|
||||||
|
kb_rules =
|
||||||
|
accel_profile = flat
|
||||||
|
|
||||||
|
follow_mouse = 1
|
||||||
|
|
||||||
|
touchpad {
|
||||||
|
natural_scroll = no
|
||||||
|
}
|
||||||
|
|
||||||
|
#scroll_factor = 2
|
||||||
|
|
||||||
|
sensitivity = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# GENERAL
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
general {
|
||||||
|
gaps_in = 5
|
||||||
|
gaps_out = 20
|
||||||
|
border_size = 2
|
||||||
|
col.active_border = $primary
|
||||||
|
col.inactive_border = rgb(000000)
|
||||||
|
resize_on_border=true
|
||||||
|
layout = dwindle
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# DECORATION
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
decoration {
|
||||||
|
rounding = 15
|
||||||
|
|
||||||
|
blur {
|
||||||
|
enabled = true
|
||||||
|
size = 12 # 12
|
||||||
|
passes = 4
|
||||||
|
new_optimizations = true
|
||||||
|
}
|
||||||
|
|
||||||
|
shadow {
|
||||||
|
enabled = false # true
|
||||||
|
range = 30 # 30
|
||||||
|
render_power = 4 # 4
|
||||||
|
color = rgb(000000)
|
||||||
|
}
|
||||||
|
# old shadow settings
|
||||||
|
# drop_shadow = yes
|
||||||
|
# shadow_range = 30
|
||||||
|
# shadow_render_power = 4
|
||||||
|
# col.shadow = rgb(000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# ANIMATIONS
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
animations {
|
||||||
|
enabled = yes
|
||||||
|
|
||||||
|
# bezier = quart, 0.25, 1, 0.5, 1
|
||||||
|
bezier = quart, 0.25, 1, 0.5, 1
|
||||||
|
|
||||||
|
animation = windows, 1, 6, quart, slide
|
||||||
|
animation = border, 1, 6, quart
|
||||||
|
# animation = borderangle, 1, 6, quart
|
||||||
|
animation = fade, 1, 6, quart
|
||||||
|
animation = workspaces, 1, 6, quart
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# WINDOW LAYOUTS
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
dwindle {
|
||||||
|
pseudotile = yes
|
||||||
|
preserve_split = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# MISC
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
misc {
|
||||||
|
disable_hyprland_logo=true
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# RENDER
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
render {
|
||||||
|
explicit_sync = true
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# EXPERIMENTAL
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
experimental {
|
||||||
|
hdr = false
|
||||||
|
}
|
||||||
|
|
||||||
|
#----------------------b-------------------------------------------------------------------------------------
|
||||||
|
# WINDOW RULES
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
windowrule = float,^(pavucontrol)$
|
||||||
|
windowrule = pin,^(pavucontrol)$
|
||||||
|
windowrule = size 900 500,^(pavucontrol)$
|
||||||
|
|
||||||
|
#windowrule = float,^(kitty)$
|
||||||
|
#windowrule = size 640 400,^(kitty)$
|
||||||
|
|
||||||
|
windowrule = float,class:^(Material Settings)$
|
||||||
|
|
||||||
|
windowrulev2 = opacity 0.0 override, class:^(xwaylandvideobridge)$
|
||||||
|
windowrulev2 = noanim, class:^(xwaylandvideobridge)$
|
||||||
|
windowrulev2 = noinitialfocus, class:^(xwaylandvideobridge)$
|
||||||
|
windowrulev2 = maxsize 1 1, class:^(xwaylandvideobridge)$
|
||||||
|
windowrulev2 = noblur, class:^(xwaylandvideobridge)$
|
||||||
|
windowrulev2 = nofocus, class:^(xwaylandvideobridge)$
|
||||||
|
|
||||||
|
layerrule = blur,^(ignis_BAR.*)$
|
||||||
|
layerrule = noanim,^(ignis_NOTIFICATION_POPUP.*)$
|
||||||
|
layerrule = noanim,^(ignis_CONTROL_CENTER.*)$
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
# BINDS
|
||||||
|
#-----------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Key modifier
|
||||||
|
$mainMod = SUPER
|
||||||
|
|
||||||
|
# Binds to control windows
|
||||||
|
bind = $mainMod, Q, killactive,
|
||||||
|
bind = $mainMod SHIFT, M, exit, # force quit Hyprland
|
||||||
|
bind = $mainMod, T, togglefloating,
|
||||||
|
bind = $mainMod, P, pseudo, # dwindle
|
||||||
|
bind = $mainMod, J, togglesplit, # dwindle
|
||||||
|
bind = ,F11, fullscreen, 0
|
||||||
|
bind = $mainMod, C, centerwindow
|
||||||
|
bind = $mainMod, D, pin
|
||||||
|
|
||||||
|
# Used to switch windows left, right, ... with another windows, also move to another screen
|
||||||
|
bind = $mainMod SHIFT, left, movewindow, l
|
||||||
|
bind = $mainMod SHIFT, right, movewindow, r
|
||||||
|
bind = $mainMod SHIFT, up, movewindow, u
|
||||||
|
bind = $mainMod SHIFT, down, movewindow, d
|
||||||
|
|
||||||
|
# Rsize but not working after reboot
|
||||||
|
# bind = $mainMod CTRL, left, resizeactive, -50 0
|
||||||
|
# bind = $mainMod CTRL, up, resizeactive, 0 50
|
||||||
|
# bind = $mainMod CTRL, down, resizeactive, 0 -50
|
||||||
|
# bind = $mainMod CTRL, right, resizeactive, 50 0
|
||||||
|
|
||||||
|
# ignis
|
||||||
|
bind = $mainMod, SPACE, exec, ignis toggle ignis_LAUNCHER
|
||||||
|
bind = $mainMod, M, exec, ignis toggle ignis_POWERMENU
|
||||||
|
bind = ALT, F4, exec, ignis toggle ignis_POWERMENU
|
||||||
|
bind = $mainMod, R, exec, ~/.config/ignis/scripts/recording.py start
|
||||||
|
bind = $mainMod SHIFT, R, exec, ~/.config/ignis/scripts/recording.py continue
|
||||||
|
bind = $mainMod SHIFT, T, exec, ~/.config/ignis/scripts/recording.py stop
|
||||||
|
bind = $mainMod SHIFT, P, exec, ~/.config/ignis/scripts/recording.py pause
|
||||||
|
|
||||||
|
# Launch apps
|
||||||
|
bind = $mainMod, Return, exec, $terminal
|
||||||
|
bind = $mainMod, L, exec, hyprlock
|
||||||
|
bind = $mainMod, E, exec, $fileManager
|
||||||
|
bind = $mainMod SHIFT, S, exec, grimblast --notify --freeze copysave area
|
||||||
|
bind = $mainMod, S, exec, grimblast --notify --freeze copysave output
|
||||||
|
bind = ,PRINT, exec, grimblast --notify --freeze copysave output
|
||||||
|
bind = $mainMod, B, exec, $browser
|
||||||
|
bind = $mainMod SHIFT, SPACE, exec, $launchpad
|
||||||
|
|
||||||
|
# Focus control
|
||||||
|
bind = $mainMod, left, movefocus, l
|
||||||
|
bind = $mainMod, right, movefocus, r
|
||||||
|
bind = $mainMod, up, movefocus, u
|
||||||
|
bind = $mainMod, down, movefocus, d
|
||||||
|
|
||||||
|
# Workspace switching
|
||||||
|
bind = $mainMod, 1, workspace, 1
|
||||||
|
bind = $mainMod, 2, workspace, 2
|
||||||
|
bind = $mainMod, 3, workspace, 3
|
||||||
|
bind = $mainMod, 4, workspace, 4
|
||||||
|
bind = $mainMod, 5, workspace, 5
|
||||||
|
bind = $mainMod, 6, workspace, 6
|
||||||
|
bind = $mainMod, 7, workspace, 7
|
||||||
|
bind = $mainMod, 8, workspace, 8
|
||||||
|
bind = $mainMod, 9, workspace, 9
|
||||||
|
bind = $mainMod, 0, workspace, 10
|
||||||
|
|
||||||
|
# Move active window to workspace
|
||||||
|
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||||
|
bind = $mainMod SHIFT, 2, movetoworkspace, 2
|
||||||
|
bind = $mainMod SHIFT, 3, movetoworkspace, 3
|
||||||
|
bind = $mainMod SHIFT, 4, movetoworkspace, 4
|
||||||
|
bind = $mainMod SHIFT, 5, movetoworkspace, 5
|
||||||
|
bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
||||||
|
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||||
|
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||||
|
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||||
|
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
||||||
|
|
||||||
|
# Switch between existing workspaces by scrolling the mouse
|
||||||
|
bind = $mainMod, mouse_down, workspace, e+1
|
||||||
|
bind = $mainMod, mouse_up, workspace, e-1
|
||||||
|
|
||||||
|
# Move and resize window
|
||||||
|
bindm = $mainMod, mouse:272, movewindow
|
||||||
|
bindm = $mainMod, mouse:273, resizewindow
|
||||||
|
|
||||||
|
# Media binds
|
||||||
|
# bind = ,xf86audiomute, exec, pactl -- set-sink-mute @DEFAULT_SINK@ toggle
|
||||||
|
bind = ,XF86AudioRaiseVolume, exec, pamixer -i 5 && ignis open ignis_OSD
|
||||||
|
bind = ,XF86AudioLowerVolume, exec, pamixer -d 5 && ignis open ignis_OSD
|
||||||
|
bind = ,XF86AudioMute, exec, pamixer -t && ignis open ignis_OSD
|
||||||
|
bind = ,xf86monbrightnessdown, exec, blight -d backlight/amdgpu_bl1 set -25.5
|
||||||
|
bind = ,xf86monbrightnessup, exec, blight -d backlight/amdgpu_bl1 set +25.5
|
||||||
|
bind = ,XF86AudioPlay, exec, playerctl play-pause
|
||||||
|
bind = ,XF86AudioPause, exec, playerctl play-pause
|
||||||
|
bind = ,XF86AudioNext, exec, playerctl next
|
||||||
|
bind = ,XF86AudioPrev, exec, playerctl previous
|
||||||
|
|
||||||
|
|
||||||
|
# taskmgr
|
||||||
|
bind = CONTROL SHIFT, ESCAPE, exec, missioncenter
|
||||||
|
|
||||||
|
# hyprswitch
|
||||||
|
bind = $mod, $key, exec, hyprswitch gui --mod-key $mod --key $key --close mod-key-release --reverse-key=mod=$reverse --sort-recent && hyprswitch dispatch
|
||||||
|
bind = $mod $reverse, $key, exec, hyprswitch gui --mod-key $mod --key $key --close mod-key-release --reverse-key=mod=$reverse --sort-recent && hyprswitch dispatch -r
|
||||||
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
$rosewater = rgb(f5e0dc)
|
||||||
|
$rosewaterAlpha = f5e0dc
|
||||||
|
|
||||||
|
$flamingo = rgb(f2cdcd)
|
||||||
|
$flamingoAlpha = f2cdcd
|
||||||
|
|
||||||
|
$pink = rgb(f5c2e7)
|
||||||
|
$pinkAlpha = f5c2e7
|
||||||
|
|
||||||
|
$mauve = rgb(cba6f7)
|
||||||
|
$mauveAlpha = cba6f7
|
||||||
|
|
||||||
|
$red = rgb(f38ba8)
|
||||||
|
$redAlpha = f38ba8
|
||||||
|
|
||||||
|
$maroon = rgb(eba0ac)
|
||||||
|
$maroonAlpha = eba0ac
|
||||||
|
|
||||||
|
$peach = rgb(fab387)
|
||||||
|
$peachAlpha = fab387
|
||||||
|
|
||||||
|
$yellow = rgb(f9e2af)
|
||||||
|
$yellowAlpha = f9e2af
|
||||||
|
|
||||||
|
$green = rgb(a6e3a1)
|
||||||
|
$greenAlpha = a6e3a1
|
||||||
|
|
||||||
|
$teal = rgb(94e2d5)
|
||||||
|
$tealAlpha = 94e2d5
|
||||||
|
|
||||||
|
$sky = rgb(89dceb)
|
||||||
|
$skyAlpha = 89dceb
|
||||||
|
|
||||||
|
$sapphire = rgb(74c7ec)
|
||||||
|
$sapphireAlpha = 74c7ec
|
||||||
|
|
||||||
|
$blue = rgb(89b4fa)
|
||||||
|
$blueAlpha = 89b4fa
|
||||||
|
|
||||||
|
$lavender = rgb(b4befe)
|
||||||
|
$lavenderAlpha = b4befe
|
||||||
|
|
||||||
|
$text = rgb(cdd6f4)
|
||||||
|
$textAlpha = cdd6f4
|
||||||
|
|
||||||
|
$subtext1 = rgb(bac2de)
|
||||||
|
$subtext1Alpha = bac2de
|
||||||
|
|
||||||
|
$subtext0 = rgb(a6adc8)
|
||||||
|
$subtext0Alpha = a6adc8
|
||||||
|
|
||||||
|
$overlay2 = rgb(9399b2)
|
||||||
|
$overlay2Alpha = 9399b2
|
||||||
|
|
||||||
|
$overlay1 = rgb(7f849c)
|
||||||
|
$overlay1Alpha = 7f849c
|
||||||
|
|
||||||
|
$overlay0 = rgb(6c7086)
|
||||||
|
$overlay0Alpha = 6c7086
|
||||||
|
|
||||||
|
$surface2 = rgb(585b70)
|
||||||
|
$surface2Alpha = 585b70
|
||||||
|
|
||||||
|
$surface1 = rgb(45475a)
|
||||||
|
$surface1Alpha = 45475a
|
||||||
|
|
||||||
|
$surface0 = rgb(313244)
|
||||||
|
$surface0Alpha = 313244
|
||||||
|
|
||||||
|
$base = rgb(1e1e2e)
|
||||||
|
$baseAlpha = 1e1e2e
|
||||||
|
|
||||||
|
$mantle = rgb(181825)
|
||||||
|
$mantleAlpha = 181825
|
||||||
|
|
||||||
|
$crust = rgb(11111b)
|
||||||
|
$crustAlpha = 11111b
|
||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
# Generated by nwg-displays on 2025-01-26 at 00:56:09. Do not edit manually.
|
monitor=HDMI-A-1,3840x2160@60.0,-2048x0,1.0,mirror,DP-3,bitdepth,10
|
||||||
|
monitor=DP-2,2560x1080@60.0,644x-1080,1.0,mirror,10
|
||||||
|
monitor=DP-3,3840x2160@60.0,0x0,1.0,bitdepth,8 # bitdepth 8 for screensharing
|
||||||
|
|
||||||
monitor=HDMI-A-1,3840x2160@60.0,0x1080,1.0,mirror,DP-3,bitdepth,10
|
env = XWAYLAND_PRIMARY_OUTPUT,DP-3
|
||||||
monitor=DP-2,2560x1080@60.0,4484x0,1.0
|
|
||||||
monitor=DP-3,3840x2160@60.0,3840x1080,1.0,bitdepth,10
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,23 @@
|
|||||||
|
import os
|
||||||
|
from ignis.utils import Utils
|
||||||
|
|
||||||
|
EXPECT_VERSION = ["0", "2"]
|
||||||
|
|
||||||
|
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
SKIP_VER_CHECK = os.getenv("SKIP_VER_CHECK")
|
||||||
|
BYPASS_VER_MESSAGE = "To bypass this check set SKIP_VER_CHECK=1 env var."
|
||||||
|
|
||||||
|
def check_version() -> None:
|
||||||
|
if SKIP_VER_CHECK == "1":
|
||||||
|
return
|
||||||
|
|
||||||
|
VERSION = Utils.get_ignis_version().replace("dev0", "").split(".")
|
||||||
|
|
||||||
|
if int(VERSION[0]) < int(EXPECT_VERSION[0]) or int(VERSION[1]) < int(
|
||||||
|
EXPECT_VERSION[1]
|
||||||
|
):
|
||||||
|
print(
|
||||||
|
f"ERROR: My dotfiles requires at least Ignis v{'.'.join(EXPECT_VERSION)}, current version: v{'.'.join(VERSION)}. {BYPASS_VER_MESSAGE}"
|
||||||
|
)
|
||||||
|
exit(1)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
from check_version import check_version
|
||||||
|
|
||||||
|
check_version()
|
||||||
|
|
||||||
|
from ignis.utils import Utils # noqa: E402
|
||||||
|
from ignis.app import IgnisApp # noqa: E402
|
||||||
|
from modules.control_center import control_center # noqa: E402
|
||||||
|
from modules.bar import bar # noqa: E402
|
||||||
|
from modules.notification_popup import notification_popup # noqa: E402
|
||||||
|
from modules.osd import OSD # noqa: E402
|
||||||
|
from modules.powermenu import powermenu # noqa: E402
|
||||||
|
from modules.launcher import launcher # noqa: E402
|
||||||
|
|
||||||
|
Utils.exec_sh("gsettings set org.gnome.desktop.interface gtk-theme Material")
|
||||||
|
Utils.exec_sh("gsettings set org.gnome.desktop.interface icon-theme Papirus")
|
||||||
|
Utils.exec_sh(
|
||||||
|
'gsettings set org.gnome.desktop.interface font-name "JetBrains Mono Regular 11"'
|
||||||
|
)
|
||||||
|
Utils.exec_sh("hyprctl reload")
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
app.apply_css(Utils.get_current_dir() + "/style.scss")
|
||||||
|
|
||||||
|
|
||||||
|
control_center()
|
||||||
|
for monitor in range(Utils.get_n_monitors()):
|
||||||
|
bar(monitor)
|
||||||
|
launcher()
|
||||||
|
for monitor in range(Utils.get_n_monitors()):
|
||||||
|
notification_popup(monitor)
|
||||||
|
|
||||||
|
powermenu()
|
||||||
|
OSD()
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 742 B |
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,25 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from .workspaces import workspaces
|
||||||
|
from .kb_layout import kb_layout
|
||||||
|
from .clock import clock
|
||||||
|
from .pinned_apps import pinned_apps
|
||||||
|
from .tray import tray
|
||||||
|
from .battery import battery_widget
|
||||||
|
# from .wallpaper_engine import wallpaper_engine_widget
|
||||||
|
|
||||||
|
def bar(monitor: int) -> Widget.Window:
|
||||||
|
return Widget.Window(
|
||||||
|
anchor=["left", "top", "right"],
|
||||||
|
exclusivity="exclusive",
|
||||||
|
monitor=monitor,
|
||||||
|
namespace=f"ignis_BAR_{monitor}",
|
||||||
|
layer="top",
|
||||||
|
kb_mode="none",
|
||||||
|
child=Widget.CenterBox(
|
||||||
|
css_classes=["bar-widget"],
|
||||||
|
start_widget=Widget.Box(child=[workspaces()]),
|
||||||
|
center_widget=Widget.Box(child=[pinned_apps()]),
|
||||||
|
end_widget=Widget.Box(child=[tray(), kb_layout(), battery_widget(), clock(monitor)]),
|
||||||
|
), # wallpaper_engine_widget(),
|
||||||
|
css_classes=["unset"],
|
||||||
|
)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,35 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.services.upower import UPowerService, UPowerDevice
|
||||||
|
|
||||||
|
upower = UPowerService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def battery_item(device: UPowerDevice) -> Widget.Box:
|
||||||
|
return Widget.Box(
|
||||||
|
css_classes=["battery-item"],
|
||||||
|
setup=lambda self: device.connect("removed", lambda x: self.unparent()),
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
icon_name=device.bind("icon_name"), css_classes=["battery-icon"]
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label=device.bind("percent", lambda x: f"{int(x)}%"),
|
||||||
|
css_classes=["battery-percent"],
|
||||||
|
),
|
||||||
|
Widget.Scale(
|
||||||
|
min=0,
|
||||||
|
max=100,
|
||||||
|
value=device.bind("percent"),
|
||||||
|
sensitive=False,
|
||||||
|
css_classes=["battery-scale"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def battery_widget() -> Widget.Box:
|
||||||
|
return Widget.Box(
|
||||||
|
setup=lambda self: upower.connect(
|
||||||
|
"battery-added", lambda x, device: self.append(battery_item(device))
|
||||||
|
),
|
||||||
|
)
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import datetime
|
||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from ignis.variable import Variable
|
||||||
|
from .indicator import status_icons
|
||||||
|
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
current_time = Variable(
|
||||||
|
value=Utils.Poll(1000, lambda x: datetime.datetime.now().strftime("%H:%M:%S")).bind(
|
||||||
|
"output"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def clock(monitor):
|
||||||
|
window: Widget.Window = app.get_window("ignis_CONTROL_CENTER") # type: ignore
|
||||||
|
|
||||||
|
def on_click(x):
|
||||||
|
if window.monitor == monitor:
|
||||||
|
window.visible = not window.visible
|
||||||
|
else:
|
||||||
|
window.set_monitor(monitor)
|
||||||
|
window.visible = True
|
||||||
|
|
||||||
|
return Widget.Button(
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
status_icons(),
|
||||||
|
Widget.Label(
|
||||||
|
label=current_time.bind("value"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
css_classes=window.bind(
|
||||||
|
"visible",
|
||||||
|
lambda value: ["clock", "unset", "active"] if value else ["clock", "unset"],
|
||||||
|
),
|
||||||
|
on_click=on_click,
|
||||||
|
)
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.services.network import NetworkService
|
||||||
|
from ignis.services.notifications import NotificationService
|
||||||
|
from ignis.services.recorder import RecorderService
|
||||||
|
from ignis.services.audio import AudioService
|
||||||
|
|
||||||
|
network = NetworkService.get_default()
|
||||||
|
notifications = NotificationService.get_default()
|
||||||
|
recorder = RecorderService.get_default()
|
||||||
|
audio = AudioService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def indicator_icon(**kwargs):
|
||||||
|
return Widget.Icon(style="margin-right: 0.5rem;", css_classes=["unset"], **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def wifi_icon():
|
||||||
|
def check_visible(*args) -> bool:
|
||||||
|
if len(network.wifi.devices) > 0:
|
||||||
|
if network.ethernet.is_connected:
|
||||||
|
if network.wifi.is_connected:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
icon = indicator_icon(image=network.wifi.bind("icon-name"))
|
||||||
|
icon.visible = network.wifi.bind("devices", check_visible)
|
||||||
|
icon.visible = network.ethernet.bind("is_connected", check_visible)
|
||||||
|
icon.visible = network.wifi.bind("is_connected", check_visible)
|
||||||
|
return icon
|
||||||
|
|
||||||
|
|
||||||
|
def ethernet_icon():
|
||||||
|
def check_visible(*args) -> bool:
|
||||||
|
if len(network.ethernet.devices) > 0:
|
||||||
|
if network.wifi.is_connected:
|
||||||
|
if network.ethernet.is_connected:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
icon = indicator_icon(image=network.ethernet.bind("icon_name"))
|
||||||
|
icon.visible = network.ethernet.bind("devices", check_visible)
|
||||||
|
icon.visible = network.wifi.bind("is_connected", check_visible)
|
||||||
|
icon.visible = network.ethernet.bind("is_connected", check_visible)
|
||||||
|
return icon
|
||||||
|
|
||||||
|
|
||||||
|
def dnd_icon():
|
||||||
|
return indicator_icon(
|
||||||
|
image="notification-disabled-symbolic",
|
||||||
|
visible=notifications.bind("dnd"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def recorder_icon():
|
||||||
|
def check_state(icon: Widget.Icon) -> None:
|
||||||
|
if recorder.is_paused:
|
||||||
|
icon.remove_css_class("active")
|
||||||
|
else:
|
||||||
|
icon.add_css_class("active")
|
||||||
|
|
||||||
|
icon = indicator_icon(
|
||||||
|
image="media-record-symbolic",
|
||||||
|
visible=recorder.bind("active"),
|
||||||
|
)
|
||||||
|
|
||||||
|
icon.add_css_class("record-indicator")
|
||||||
|
|
||||||
|
recorder.connect("notify::is-paused", lambda x, y: check_state(icon))
|
||||||
|
|
||||||
|
return icon
|
||||||
|
|
||||||
|
|
||||||
|
def volume_icon():
|
||||||
|
return indicator_icon(
|
||||||
|
image=audio.speaker.bind("icon_name"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def status_icons():
|
||||||
|
return Widget.Box(
|
||||||
|
child=[recorder_icon(), wifi_icon(), ethernet_icon(), volume_icon(), dnd_icon()]
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.exceptions import HyprlandIPCNotFoundError
|
||||||
|
from ignis.services.hyprland import HyprlandService
|
||||||
|
|
||||||
|
try:
|
||||||
|
hyprland = HyprlandService.get_default()
|
||||||
|
|
||||||
|
def kb_layout():
|
||||||
|
return Widget.Button(
|
||||||
|
css_classes=["kb-layout", "unset"],
|
||||||
|
on_click=lambda x: hyprland.switch_kb_layout(),
|
||||||
|
child=Widget.Label(
|
||||||
|
label=hyprland.bind(
|
||||||
|
"kb_layout", transform=lambda value: value[:2].lower()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
except HyprlandIPCNotFoundError:
|
||||||
|
|
||||||
|
def kb_layout():
|
||||||
|
return
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from ignis.services.applications import ApplicationsService
|
||||||
|
|
||||||
|
applications = ApplicationsService.get_default()
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class AppItem(Widget.Button):
|
||||||
|
def __init__(self, app):
|
||||||
|
menu = Widget.PopoverMenu(
|
||||||
|
items=[
|
||||||
|
Widget.MenuItem(label="Launch", on_activate=lambda x: app.launch()),
|
||||||
|
Widget.Separator(),
|
||||||
|
]
|
||||||
|
+ [
|
||||||
|
Widget.MenuItem(
|
||||||
|
label=i.name, on_activate=lambda x, action=i: action.launch()
|
||||||
|
)
|
||||||
|
for i in app.actions
|
||||||
|
]
|
||||||
|
+ [
|
||||||
|
Widget.Separator(),
|
||||||
|
Widget.MenuItem(label="Unpin", on_activate=lambda x: app.unpin()),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
child=Widget.Box(child=[Widget.Icon(image=app.icon, pixel_size=32), menu]),
|
||||||
|
on_click=lambda x: app.launch(),
|
||||||
|
on_right_click=lambda x: menu.popup(),
|
||||||
|
css_classes=["pinned-app", "unset"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def launcher_button():
|
||||||
|
return Widget.Button(
|
||||||
|
child=Widget.Icon(image="start-here-symbolic", pixel_size=32),
|
||||||
|
on_click=lambda x: app.toggle_window("ignis_LAUNCHER"),
|
||||||
|
css_classes=["pinned-app", "unset"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pinned_apps():
|
||||||
|
return Widget.Box(
|
||||||
|
child=applications.bind(
|
||||||
|
"pinned",
|
||||||
|
transform=lambda value: [AppItem(app) for app in value]
|
||||||
|
+ [launcher_button()],
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.services.system_tray import SystemTrayService
|
||||||
|
|
||||||
|
system_tray = SystemTrayService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class TrayItem(Widget.Button):
|
||||||
|
def __init__(self, item):
|
||||||
|
if item.menu:
|
||||||
|
menu = item.menu.copy()
|
||||||
|
else:
|
||||||
|
menu = None
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image=item.bind("icon"), pixel_size=24),
|
||||||
|
menu,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
tooltip_text=item.bind("tooltip"),
|
||||||
|
on_click=lambda x: item.activate(),
|
||||||
|
on_right_click=lambda x: menu.popup() if menu else None,
|
||||||
|
css_classes=["tray-item", "unset"],
|
||||||
|
)
|
||||||
|
|
||||||
|
item.connect("removed", lambda x: self.unparent())
|
||||||
|
|
||||||
|
|
||||||
|
def tray():
|
||||||
|
return Widget.Box(
|
||||||
|
css_classes=["tray"],
|
||||||
|
setup=lambda self: system_tray.connect(
|
||||||
|
"added", lambda x, item: self.append(TrayItem(item))
|
||||||
|
),
|
||||||
|
spacing=10,
|
||||||
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.exceptions import HyprlandIPCNotFoundError
|
||||||
|
from ignis.services.hyprland import HyprlandService
|
||||||
|
import os
|
||||||
|
|
||||||
|
def toggle_engine():
|
||||||
|
os.system("cd /home/timo/.config/hypr/linux-wallpaperengine && ./linux-wallpaperengine 2657260174 --screen-root DP-3")
|
||||||
|
|
||||||
|
try:
|
||||||
|
hyprland = HyprlandService.get_default()
|
||||||
|
|
||||||
|
def wallpaper_engine_widget():
|
||||||
|
return Widget.Button(
|
||||||
|
css_classes=["kb-layout", "unset"],
|
||||||
|
on_click=lambda x: os.system("cd /home/timo/.config/hypr/linux-wallpaperengine && ./linux-wallpaperengine 2657260174 --screen-root DP-3"),
|
||||||
|
child=Widget.Label(
|
||||||
|
label=hyprland.bind(
|
||||||
|
"kb_layout", transform=lambda value: value[:2].lower()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
except HyprlandIPCNotFoundError:
|
||||||
|
|
||||||
|
def kb_layout():
|
||||||
|
return
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.exceptions import HyprlandIPCNotFoundError
|
||||||
|
from ignis.services.hyprland import HyprlandService
|
||||||
|
|
||||||
|
|
||||||
|
class WorkspaceButton(Widget.Button):
|
||||||
|
def __init__(self, workspace: dict) -> None:
|
||||||
|
super().__init__(
|
||||||
|
css_classes=["workspace", "unset"],
|
||||||
|
on_click=lambda x, id=workspace["id"]: hyprland.switch_to_workspace(id),
|
||||||
|
halign="start",
|
||||||
|
valign="center",
|
||||||
|
)
|
||||||
|
if workspace["id"] == hyprland.active_workspace["id"]:
|
||||||
|
self.add_css_class("active")
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
hyprland = HyprlandService.get_default()
|
||||||
|
|
||||||
|
def scroll_workspaces(direction: str) -> None:
|
||||||
|
current = hyprland.active_workspace["id"]
|
||||||
|
if direction == "up":
|
||||||
|
target = current - 1
|
||||||
|
hyprland.switch_to_workspace(target)
|
||||||
|
else:
|
||||||
|
target = current + 1
|
||||||
|
if target == 11:
|
||||||
|
return
|
||||||
|
hyprland.switch_to_workspace(target)
|
||||||
|
|
||||||
|
def workspaces():
|
||||||
|
return Widget.EventBox(
|
||||||
|
on_scroll_up=lambda x: scroll_workspaces("up"),
|
||||||
|
on_scroll_down=lambda x: scroll_workspaces("down"),
|
||||||
|
css_classes=["workspaces"],
|
||||||
|
child=hyprland.bind(
|
||||||
|
"workspaces",
|
||||||
|
transform=lambda value: [WorkspaceButton(i) for i in value],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
except HyprlandIPCNotFoundError:
|
||||||
|
|
||||||
|
def workspaces():
|
||||||
|
return
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from .volume import volume_control
|
||||||
|
from .quick_settings import quick_settings
|
||||||
|
from .user import user
|
||||||
|
from .media import media
|
||||||
|
from .notification_center import notification_center
|
||||||
|
from .brightness import brightness_slider
|
||||||
|
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def control_center_widget() -> Widget.Box:
|
||||||
|
return Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["control-center"],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["control-center-widget"],
|
||||||
|
child=[
|
||||||
|
quick_settings(),
|
||||||
|
volume_control(),
|
||||||
|
brightness_slider(),
|
||||||
|
user(),
|
||||||
|
media(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
notification_center(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def control_center() -> Widget.RevealerWindow:
|
||||||
|
revealer = Widget.Revealer(
|
||||||
|
transition_type="slide_left",
|
||||||
|
child=control_center_widget(),
|
||||||
|
transition_duration=300,
|
||||||
|
reveal_child=True,
|
||||||
|
)
|
||||||
|
box = Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Button(
|
||||||
|
vexpand=True,
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=["unset"],
|
||||||
|
on_click=lambda x: app.close_window("ignis_CONTROL_CENTER"),
|
||||||
|
),
|
||||||
|
revealer,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return Widget.RevealerWindow(
|
||||||
|
visible=False,
|
||||||
|
popup=True,
|
||||||
|
kb_mode="on_demand",
|
||||||
|
layer="top",
|
||||||
|
css_classes=["unset"],
|
||||||
|
anchor=["top", "right", "bottom", "left"],
|
||||||
|
namespace="ignis_CONTROL_CENTER",
|
||||||
|
child=box,
|
||||||
|
revealer=revealer,
|
||||||
|
)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
|
|||||||
|
from ignis.services.backlight import BacklightService
|
||||||
|
from ignis.widgets import Widget
|
||||||
|
|
||||||
|
backlight = BacklightService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def brightness_slider() -> Widget.Scale:
|
||||||
|
return Widget.Box(
|
||||||
|
visible=backlight.bind("available"),
|
||||||
|
hexpand=True,
|
||||||
|
style="margin-top: 0.25rem;",
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
image="display-brightness-symbolic",
|
||||||
|
css_classes=["material-slider-icon"],
|
||||||
|
pixel_size=18,
|
||||||
|
),
|
||||||
|
Widget.Scale(
|
||||||
|
min=0,
|
||||||
|
max=backlight.max_brightness,
|
||||||
|
hexpand=True,
|
||||||
|
value=backlight.bind("brightness"),
|
||||||
|
css_classes=["material-slider"],
|
||||||
|
on_change=lambda x: backlight.set_brightness(x.value),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from services.material import MaterialService
|
||||||
|
from .qs_button import QSButton
|
||||||
|
|
||||||
|
material = MaterialService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def dark_mode_button() -> QSButton:
|
||||||
|
return QSButton(
|
||||||
|
label="Dark",
|
||||||
|
icon_name="night-light-symbolic",
|
||||||
|
on_activate=lambda x: material.set_dark_mode(True),
|
||||||
|
on_deactivate=lambda x: material.set_dark_mode(False),
|
||||||
|
active=material.bind("dark-mode"),
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from .qs_button import QSButton
|
||||||
|
from ignis.services.notifications import NotificationService
|
||||||
|
|
||||||
|
notifications = NotificationService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def dnd_button() -> QSButton:
|
||||||
|
return QSButton(
|
||||||
|
label=notifications.bind("dnd", lambda value: "Silent" if value else "Noisy"),
|
||||||
|
icon_name=notifications.bind(
|
||||||
|
"dnd",
|
||||||
|
transform=lambda value: "notification-disabled-symbolic"
|
||||||
|
if value
|
||||||
|
else "notification-symbolic",
|
||||||
|
),
|
||||||
|
on_activate=lambda x: notifications.set_dnd(not notifications.dnd),
|
||||||
|
on_deactivate=lambda x: notifications.set_dnd(not notifications.dnd),
|
||||||
|
active=notifications.bind("dnd"),
|
||||||
|
)
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
from typing import List
|
||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from .qs_button import QSButton
|
||||||
|
from ignis.services.network import NetworkService, EthernetDevice
|
||||||
|
|
||||||
|
network = NetworkService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class EthernetConnectionItem(Widget.Button):
|
||||||
|
def __init__(self, device: EthernetDevice):
|
||||||
|
super().__init__(
|
||||||
|
css_classes=["ethernet-connection-item", "unset"],
|
||||||
|
on_click=lambda x: device.disconnect_from()
|
||||||
|
if device.is_connected
|
||||||
|
else device.connect_to(),
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image="network-wired-symbolic"),
|
||||||
|
Widget.Label(
|
||||||
|
label=device.name,
|
||||||
|
ellipsize="end",
|
||||||
|
max_width_chars=20,
|
||||||
|
halign="start",
|
||||||
|
css_classes=["ethernet-connection-label"],
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(
|
||||||
|
label=device.bind(
|
||||||
|
"is_connected",
|
||||||
|
lambda value: "Disconnect" if value else "Connect",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
css_classes=["ethernet-connection-item-connect-label", "unset"],
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ethernet_control() -> List[QSButton]:
|
||||||
|
networks_list = Widget.Revealer(
|
||||||
|
transition_duration=300,
|
||||||
|
transition_type="slide_down",
|
||||||
|
child=Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["control-center-menu"],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
css_classes=["ethernet-header-box"],
|
||||||
|
child=[
|
||||||
|
Widget.Icon(icon_name="network-wired-symbolic", pixel_size=28),
|
||||||
|
Widget.Label(
|
||||||
|
label="Wired connections",
|
||||||
|
css_classes=["ethernet-header-label"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=network.ethernet.bind(
|
||||||
|
"devices",
|
||||||
|
lambda value: [EthernetConnectionItem(i) for i in value],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Widget.Separator(css_classes=["ethernet-network-list-separator"]),
|
||||||
|
Widget.Button(
|
||||||
|
css_classes=["ethernet-connection-item", "unset"],
|
||||||
|
style="margin-bottom: 0;",
|
||||||
|
on_click=lambda x: Utils.exec_sh_async("nm-connection-editor"),
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image="preferences-system-symbolic"),
|
||||||
|
Widget.Label(
|
||||||
|
label="Network Settings",
|
||||||
|
halign="start",
|
||||||
|
css_classes=["ethernet-connection-label"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(network.ethernet.devices) > 0:
|
||||||
|
return [
|
||||||
|
QSButton(
|
||||||
|
label="Wired",
|
||||||
|
icon_name="network-wired-symbolic",
|
||||||
|
on_activate=lambda x: networks_list.toggle(),
|
||||||
|
on_deactivate=lambda x: networks_list.toggle(),
|
||||||
|
content=networks_list,
|
||||||
|
active=network.ethernet.bind("is_connected"),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
import os
|
||||||
|
import ignis
|
||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.services.mpris import MprisService, MprisPlayer
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from services.material import MaterialService
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from ignis.exceptions import StylePathNotFoundError
|
||||||
|
|
||||||
|
|
||||||
|
mpris = MprisService.get_default()
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
material = MaterialService.get_default()
|
||||||
|
|
||||||
|
MEDIA_TEMPLATE = Utils.get_current_dir() + "/../../scss/media.scss"
|
||||||
|
MEDIA_SCSS_CACHE_DIR = ignis.CACHE_DIR + "/media" # type: ignore
|
||||||
|
MEDIA_ART_FALLBACK = Utils.get_current_dir() + "/../../misc/media-art-fallback.png"
|
||||||
|
os.makedirs(MEDIA_SCSS_CACHE_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
PLAYER_ICONS = {"spotify": "", "firefox": "", "chrome": "", None: ""}
|
||||||
|
|
||||||
|
|
||||||
|
class Player(Widget.Revealer):
|
||||||
|
def __init__(self, player: MprisPlayer) -> None:
|
||||||
|
self._player = player
|
||||||
|
self._colors_path = f"{MEDIA_SCSS_CACHE_DIR}/{self._player.desktop_entry}.scss"
|
||||||
|
player.connect("closed", lambda x: self.destroy())
|
||||||
|
player.connect("notify::art-url", lambda x, y: self.load_colors())
|
||||||
|
self.load_colors()
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
transition_type="slide_down",
|
||||||
|
reveal_child=False,
|
||||||
|
css_classes=[self.get_css("media")],
|
||||||
|
child=Widget.Overlay(
|
||||||
|
child=Widget.Box(css_classes=[self.get_css("media-image")]),
|
||||||
|
overlays=[
|
||||||
|
Widget.Box(
|
||||||
|
hexpand=True,
|
||||||
|
vexpand=True,
|
||||||
|
css_classes=[self.get_css("media-image-gradient")],
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label=self.get_player_icon(),
|
||||||
|
halign="start",
|
||||||
|
valign="start",
|
||||||
|
css_classes=[self.get_css("media-player-icon")],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=[self.get_css("media-content")],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
vexpand=True,
|
||||||
|
valign="center",
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
hexpand=True,
|
||||||
|
vertical=True,
|
||||||
|
child=[
|
||||||
|
Widget.Label(
|
||||||
|
ellipsize="end",
|
||||||
|
label=player.bind("title"),
|
||||||
|
max_width_chars=30,
|
||||||
|
halign="start",
|
||||||
|
css_classes=[
|
||||||
|
self.get_css("media-title")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label=player.bind("artist"),
|
||||||
|
max_width_chars=30,
|
||||||
|
ellipsize="end",
|
||||||
|
halign="start",
|
||||||
|
css_classes=[
|
||||||
|
self.get_css("media-artist")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Icon(
|
||||||
|
image=player.bind(
|
||||||
|
"playback_status",
|
||||||
|
lambda value: "media-playback-pause-symbolic"
|
||||||
|
if value == "Playing"
|
||||||
|
else "media-playback-start-symbolic",
|
||||||
|
),
|
||||||
|
pixel_size=18,
|
||||||
|
),
|
||||||
|
on_click=lambda x: player.play_pause(),
|
||||||
|
visible=player.bind("can_play"),
|
||||||
|
css_classes=player.bind(
|
||||||
|
"playback_status",
|
||||||
|
lambda value: [
|
||||||
|
self.get_css("media-playback-button"),
|
||||||
|
"playing",
|
||||||
|
]
|
||||||
|
if value == "Playing"
|
||||||
|
else [
|
||||||
|
self.get_css("media-playback-button"),
|
||||||
|
"paused",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vexpand=True,
|
||||||
|
valign="end",
|
||||||
|
style="padding: 1rem;",
|
||||||
|
child=[
|
||||||
|
Widget.Scale(
|
||||||
|
value=player.bind("position"),
|
||||||
|
max=player.bind("length"),
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=[self.get_css("media-scale")],
|
||||||
|
on_change=lambda x: player.set_position(x.value),
|
||||||
|
visible=player.bind(
|
||||||
|
"position", lambda value: value != -1
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Icon(
|
||||||
|
image="media-skip-backward-symbolic",
|
||||||
|
pixel_size=20,
|
||||||
|
),
|
||||||
|
css_classes=[self.get_css("media-skip-button")],
|
||||||
|
on_click=lambda x: player.previous(),
|
||||||
|
visible=player.bind("can_go_previous"),
|
||||||
|
style="margin-left: 1rem;",
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Icon(
|
||||||
|
image="media-skip-forward-symbolic",
|
||||||
|
pixel_size=20,
|
||||||
|
),
|
||||||
|
css_classes=[self.get_css("media-skip-button")],
|
||||||
|
on_click=lambda x: player.next(),
|
||||||
|
visible=player.bind("can_go_next"),
|
||||||
|
style="margin-left: 1rem;",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_player_icon(self) -> str:
|
||||||
|
if self._player.desktop_entry == "firefox":
|
||||||
|
return PLAYER_ICONS["firefox"]
|
||||||
|
elif self._player.desktop_entry == "spotify":
|
||||||
|
return PLAYER_ICONS["spotify"]
|
||||||
|
elif "chromium" in self._player.track_id or "chrome" in self._player.track_id:
|
||||||
|
return PLAYER_ICONS["chrome"]
|
||||||
|
else:
|
||||||
|
return PLAYER_ICONS[None]
|
||||||
|
|
||||||
|
def destroy(self) -> None:
|
||||||
|
self.set_reveal_child(False)
|
||||||
|
Utils.Timeout(self.transition_duration, super().unparent)
|
||||||
|
|
||||||
|
def get_css(self, class_name: str) -> str:
|
||||||
|
return f"{class_name}-{self._player.desktop_entry}"
|
||||||
|
|
||||||
|
def load_colors(self) -> None:
|
||||||
|
if not self._player.art_url:
|
||||||
|
art_url = MEDIA_ART_FALLBACK
|
||||||
|
else:
|
||||||
|
art_url = self._player.art_url
|
||||||
|
|
||||||
|
try:
|
||||||
|
app.remove_css(self._colors_path)
|
||||||
|
except StylePathNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
colors = material.get_colors_from_img(art_url, True)
|
||||||
|
colors["art_url"] = art_url
|
||||||
|
colors["desktop_entry"] = self._player.desktop_entry
|
||||||
|
material.render_template(
|
||||||
|
colors, input_file=MEDIA_TEMPLATE, output_file=self._colors_path
|
||||||
|
)
|
||||||
|
app.apply_css(self._colors_path)
|
||||||
|
|
||||||
|
|
||||||
|
def media() -> Widget.Box:
|
||||||
|
def add_player(box: Widget.Box, obj: MprisPlayer) -> None:
|
||||||
|
player = Player(obj)
|
||||||
|
box.append(player)
|
||||||
|
player.set_reveal_child(True)
|
||||||
|
|
||||||
|
return Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
setup=lambda self: mpris.connect(
|
||||||
|
"player_added", lambda x, player: add_player(self, player)
|
||||||
|
),
|
||||||
|
css_classes=["rec-unset"],
|
||||||
|
)
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.services.notifications import Notification, NotificationService
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from gi.repository import GLib # type: ignore
|
||||||
|
|
||||||
|
notifications = NotificationService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class ScreenshotLayout(Widget.Box):
|
||||||
|
def __init__(self, notification: Notification) -> None:
|
||||||
|
super().__init__(
|
||||||
|
vertical=True,
|
||||||
|
hexpand=True,
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Picture(
|
||||||
|
image=notification.icon,
|
||||||
|
content_fit="cover",
|
||||||
|
width=1920 // 7,
|
||||||
|
height=1080 // 7,
|
||||||
|
style="border-radius: 1rem; background-color: black;",
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Icon(
|
||||||
|
image="window-close-symbolic", pixel_size=20
|
||||||
|
),
|
||||||
|
halign="end",
|
||||||
|
valign="start",
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=["notification-close"],
|
||||||
|
on_click=lambda x: notification.close(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label="Screenshot saved",
|
||||||
|
css_classes=["notification-screenshot-label"],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
homogeneous=True,
|
||||||
|
style="margin-top: 0.75rem;",
|
||||||
|
spacing=10,
|
||||||
|
child=[
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(label="Open"),
|
||||||
|
css_classes=["notification-action"],
|
||||||
|
on_click=lambda x: Utils.exec_sh_async(
|
||||||
|
f"xdg-open {notification.icon}"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(label="Close"),
|
||||||
|
css_classes=["notification-action"],
|
||||||
|
on_click=lambda x: notification.close(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NormalLayout(Widget.Box):
|
||||||
|
def __init__(self, notification: Notification) -> None:
|
||||||
|
super().__init__(
|
||||||
|
vertical=True,
|
||||||
|
hexpand=True,
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
image=notification.icon
|
||||||
|
if notification.icon
|
||||||
|
else "dialog-information-symbolic",
|
||||||
|
pixel_size=48,
|
||||||
|
halign="start",
|
||||||
|
valign="start",
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
style="margin-left: 0.75rem;",
|
||||||
|
child=[
|
||||||
|
Widget.Label(
|
||||||
|
ellipsize="end",
|
||||||
|
label=notification.summary,
|
||||||
|
halign="start",
|
||||||
|
visible=notification.summary != "",
|
||||||
|
css_classes=["notification-summary"],
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label=notification.body,
|
||||||
|
ellipsize="end",
|
||||||
|
halign="start",
|
||||||
|
css_classes=["notification-body"],
|
||||||
|
visible=notification.body != "",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Icon(
|
||||||
|
image="window-close-symbolic", pixel_size=20
|
||||||
|
),
|
||||||
|
halign="end",
|
||||||
|
valign="start",
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=["notification-close"],
|
||||||
|
on_click=lambda x: notification.close(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(label=action.label),
|
||||||
|
on_click=lambda x, action=action: action.invoke(),
|
||||||
|
css_classes=["notification-action"],
|
||||||
|
)
|
||||||
|
for action in notification.actions
|
||||||
|
],
|
||||||
|
homogeneous=True,
|
||||||
|
style="margin-top: 0.75rem;" if notification.actions else "",
|
||||||
|
spacing=10,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationWidget(Widget.Box):
|
||||||
|
def __init__(self, notification: Notification) -> None:
|
||||||
|
layout: NormalLayout | ScreenshotLayout
|
||||||
|
|
||||||
|
if notification.app_name == "grimblast":
|
||||||
|
layout = ScreenshotLayout(notification)
|
||||||
|
else:
|
||||||
|
layout = NormalLayout(notification)
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
css_classes=["notification"],
|
||||||
|
child=[layout],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Popup(Widget.Revealer):
|
||||||
|
def __init__(self, notification: Notification, **kwargs):
|
||||||
|
widget = NotificationWidget(notification)
|
||||||
|
super().__init__(child=widget, transition_type="slide_down", **kwargs)
|
||||||
|
|
||||||
|
notification.connect("closed", lambda x: self.destroy())
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
self.reveal_child = False
|
||||||
|
Utils.Timeout(self.transition_duration, self.unparent)
|
||||||
|
|
||||||
|
|
||||||
|
def loading_notif_label() -> Widget.Label:
|
||||||
|
return Widget.Label(
|
||||||
|
label="Loading notifications...",
|
||||||
|
valign="center",
|
||||||
|
vexpand=True,
|
||||||
|
css_classes=["notification-center-info-label"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def no_notifications_label() -> Widget.Label:
|
||||||
|
return Widget.Label(
|
||||||
|
label="No notifications",
|
||||||
|
valign="center",
|
||||||
|
vexpand=True,
|
||||||
|
visible=notifications.bind("notifications", lambda value: len(value) == 0),
|
||||||
|
css_classes=["notification-center-info-label"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def on_notified(box: Widget.Box, notification: Notification) -> None:
|
||||||
|
notify = Popup(notification)
|
||||||
|
box.prepend(notify)
|
||||||
|
notify.reveal_child = True
|
||||||
|
|
||||||
|
|
||||||
|
def load_notifications() -> list[Widget.Label | Popup]:
|
||||||
|
widgets = []
|
||||||
|
for i in notifications.notifications:
|
||||||
|
GLib.idle_add(lambda i=i: widgets.append(Popup(i, reveal_child=True)))
|
||||||
|
return widgets
|
||||||
|
|
||||||
|
|
||||||
|
def notification_list() -> Widget.Box:
|
||||||
|
box = Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=[loading_notif_label()],
|
||||||
|
vexpand=True,
|
||||||
|
css_classes=["rec-unset"],
|
||||||
|
setup=lambda self: notifications.connect(
|
||||||
|
"notified",
|
||||||
|
lambda x, notification: on_notified(self, notification),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
Utils.ThreadTask(
|
||||||
|
load_notifications,
|
||||||
|
lambda result: box.set_child(result + [no_notifications_label()]),
|
||||||
|
).run()
|
||||||
|
|
||||||
|
return box
|
||||||
|
|
||||||
|
|
||||||
|
def notification_center() -> Widget.Box:
|
||||||
|
main_box = Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
vexpand=True,
|
||||||
|
css_classes=["notification-center"],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
css_classes=["notification-center-header", "rec-unset"],
|
||||||
|
child=[
|
||||||
|
Widget.Label(
|
||||||
|
label=notifications.bind(
|
||||||
|
"notifications", lambda value: str(len(value))
|
||||||
|
),
|
||||||
|
css_classes=["notification-count"],
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label="notifications",
|
||||||
|
css_classes=["notification-header-label"],
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(label="Clear all"),
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
on_click=lambda x: notifications.clear_all(),
|
||||||
|
css_classes=["notification-clear-all"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Scroll(
|
||||||
|
child=notification_list(),
|
||||||
|
vexpand=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
return main_box
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from gi.repository import GObject # type: ignore
|
||||||
|
from typing import Callable
|
||||||
|
from ignis.gobject import Binding
|
||||||
|
|
||||||
|
|
||||||
|
class QSButton(Widget.Button):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
label: str | Binding,
|
||||||
|
icon_name: str | Binding,
|
||||||
|
on_activate: Callable | None = None,
|
||||||
|
on_deactivate: Callable | None = None,
|
||||||
|
content: Widget.Revealer | None = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
self.on_activate = on_activate
|
||||||
|
self.on_deactivate = on_deactivate
|
||||||
|
self._active = False
|
||||||
|
self._content = content
|
||||||
|
super().__init__(
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image=icon_name),
|
||||||
|
Widget.Label(label=label, css_classes=["qs-button-label"]),
|
||||||
|
Widget.Arrow(
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
pixel_size=20,
|
||||||
|
rotated=content.bind("reveal_child"),
|
||||||
|
)
|
||||||
|
if content
|
||||||
|
else None,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
on_click=self.__callback,
|
||||||
|
css_classes=["qs-button", "unset"],
|
||||||
|
hexpand=True,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __callback(self, *args) -> None:
|
||||||
|
if self.active:
|
||||||
|
if self.on_deactivate:
|
||||||
|
self.on_deactivate(self)
|
||||||
|
else:
|
||||||
|
if self.on_activate:
|
||||||
|
self.on_activate(self)
|
||||||
|
|
||||||
|
@GObject.Property
|
||||||
|
def active(self) -> bool:
|
||||||
|
return self._active
|
||||||
|
|
||||||
|
@active.setter
|
||||||
|
def active(self, value: bool) -> None:
|
||||||
|
self._active = value
|
||||||
|
if value:
|
||||||
|
self.add_css_class("active")
|
||||||
|
else:
|
||||||
|
self.remove_css_class("active")
|
||||||
|
|
||||||
|
@GObject.Property
|
||||||
|
def content(self) -> Widget.Revealer | None:
|
||||||
|
return self._content
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from .wifi import wifi_control
|
||||||
|
from .record import record_control
|
||||||
|
from .dnd import dnd_button
|
||||||
|
from .dark_mode import dark_mode_button
|
||||||
|
from .ethernet import ethernet_control
|
||||||
|
from .vpn import vpn_control
|
||||||
|
from .qs_button import QSButton
|
||||||
|
from ignis.services.network import NetworkService
|
||||||
|
|
||||||
|
network = NetworkService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def add_button(main_box: Widget.Box, buttons: tuple[QSButton, ...], i: int) -> None:
|
||||||
|
row = Widget.Box(homogeneous=True)
|
||||||
|
if len(main_box.child) > 0:
|
||||||
|
row.style = "margin-top: 0.5rem;"
|
||||||
|
|
||||||
|
main_box.append(row)
|
||||||
|
|
||||||
|
button1 = buttons[i]
|
||||||
|
row.append(button1)
|
||||||
|
|
||||||
|
if button1.content:
|
||||||
|
main_box.append(button1.content)
|
||||||
|
|
||||||
|
if i + 1 < len(buttons):
|
||||||
|
button2 = buttons[i + 1]
|
||||||
|
button2.style = "margin-left: 0.5rem;"
|
||||||
|
row.append(button2)
|
||||||
|
|
||||||
|
if button2.content:
|
||||||
|
main_box.append(button2.content)
|
||||||
|
|
||||||
|
|
||||||
|
def qs_fabric(main_box: Widget.Box, *buttons: QSButton) -> None:
|
||||||
|
for i in range(0, len(buttons), 2):
|
||||||
|
add_button(main_box, buttons, i)
|
||||||
|
|
||||||
|
|
||||||
|
def qs_config(main_box: Widget.Box) -> None:
|
||||||
|
qs_fabric(
|
||||||
|
main_box,
|
||||||
|
*wifi_control(),
|
||||||
|
*ethernet_control(),
|
||||||
|
*vpn_control(),
|
||||||
|
dnd_button(),
|
||||||
|
dark_mode_button(),
|
||||||
|
record_control(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_box(main_box: Widget.Box):
|
||||||
|
main_box.child = []
|
||||||
|
qs_config(main_box)
|
||||||
|
|
||||||
|
|
||||||
|
def quick_settings() -> Widget.Box:
|
||||||
|
main_box = Widget.Box(vertical=True, css_classes=["qs-main-box"])
|
||||||
|
network.wifi.connect("notify::devices", lambda x, y: update_box(main_box))
|
||||||
|
network.ethernet.connect("notify::devices", lambda x, y: update_box(main_box))
|
||||||
|
network.vpn.connect("notify::connections", lambda x, y: update_box(main_box))
|
||||||
|
|
||||||
|
return main_box
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from .qs_button import QSButton
|
||||||
|
from ignis.services.recorder import RecorderService
|
||||||
|
|
||||||
|
recorder = RecorderService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def record_control() -> QSButton:
|
||||||
|
record_audio_switch = Widget.Switch(halign="end", hexpand=True, valign="center")
|
||||||
|
dropdown = Widget.DropDown(
|
||||||
|
items=["Internal audio", "Microphone", "Both sources"],
|
||||||
|
css_classes=["record-dropdown"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def start_recording(record_menu: Widget.Revealer) -> None:
|
||||||
|
record_menu.set_reveal_child(False)
|
||||||
|
microphone = False
|
||||||
|
internal = False
|
||||||
|
if record_audio_switch.active:
|
||||||
|
if dropdown.selected == "Internal audio":
|
||||||
|
internal = True
|
||||||
|
elif dropdown.selected == "Microphone":
|
||||||
|
microphone = True
|
||||||
|
else:
|
||||||
|
internal = True
|
||||||
|
microphone = True
|
||||||
|
|
||||||
|
recorder.start_recording(
|
||||||
|
record_microphone=microphone, record_internal_audio=internal
|
||||||
|
)
|
||||||
|
|
||||||
|
record_menu = Widget.Revealer(
|
||||||
|
transition_duration=300,
|
||||||
|
transition_type="slide_down",
|
||||||
|
child=Widget.Box(
|
||||||
|
css_classes=["record-menu"],
|
||||||
|
vertical=True,
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
image="media-record-symbolic",
|
||||||
|
pixel_size=36,
|
||||||
|
halign="center",
|
||||||
|
css_classes=["record-icon"],
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label="Start recording?",
|
||||||
|
halign="center",
|
||||||
|
style="font-size: 1.2rem;",
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
style="margin-top: 0.5rem;",
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
image="microphone-sensitivity-medium-symbolic",
|
||||||
|
pixel_size=20,
|
||||||
|
style="margin-right: 0.5rem;",
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=[
|
||||||
|
Widget.Label(
|
||||||
|
label="Record audio",
|
||||||
|
style="font-size: 1.1rem;",
|
||||||
|
halign="start",
|
||||||
|
),
|
||||||
|
dropdown,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
record_audio_switch,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
style="margin-top: 1rem;",
|
||||||
|
child=[
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(label="Cancel"),
|
||||||
|
css_classes=["record-cancel-button", "unset"],
|
||||||
|
on_click=lambda x: record_menu.set_reveal_child(False), # type: ignore
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(label="Start recording"),
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=["record-start-button", "unset"],
|
||||||
|
on_click=lambda x: start_recording(record_menu), # type: ignore
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return QSButton(
|
||||||
|
label="Recording",
|
||||||
|
icon_name="media-record-symbolic",
|
||||||
|
on_activate=lambda x: record_menu.toggle(),
|
||||||
|
on_deactivate=lambda x: recorder.stop_recording(),
|
||||||
|
active=recorder.bind("active"),
|
||||||
|
content=record_menu,
|
||||||
|
)
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import os
|
||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from ignis.services.fetch import FetchService
|
||||||
|
from ..settings import settings_window
|
||||||
|
from options import avatar_opt
|
||||||
|
|
||||||
|
fetch = FetchService.get_default()
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def format_uptime(value):
|
||||||
|
days, hours, minutes, seconds = value
|
||||||
|
if days:
|
||||||
|
return f"up {days:02}:{hours:02}:{minutes:02}"
|
||||||
|
else:
|
||||||
|
return f"up {hours:02}:{minutes:02}"
|
||||||
|
|
||||||
|
|
||||||
|
def user_image() -> Widget.Picture:
|
||||||
|
return Widget.Picture(
|
||||||
|
image=avatar_opt.bind(
|
||||||
|
"value",
|
||||||
|
lambda value: "user-info" if not os.path.exists(value) else value,
|
||||||
|
),
|
||||||
|
width=44,
|
||||||
|
height=44,
|
||||||
|
content_fit="cover",
|
||||||
|
style="border-radius: 10rem;",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def username() -> Widget.Box:
|
||||||
|
return Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Label(
|
||||||
|
label=os.getenv("USER"), css_classes=["user-name"], halign="start"
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label=Utils.Poll(
|
||||||
|
timeout=60 * 1000, callback=lambda x: fetch.uptime
|
||||||
|
).bind("output", lambda value: format_uptime(value)),
|
||||||
|
halign="start",
|
||||||
|
css_classes=["user-name-secondary"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["user-name-box"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def settings_button() -> Widget.Button:
|
||||||
|
return Widget.Button(
|
||||||
|
child=Widget.Icon(image="emblem-system-symbolic", pixel_size=20),
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
css_classes=["user-settings", "unset"],
|
||||||
|
on_click=lambda x: settings_window(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def power_button() -> Widget.Button:
|
||||||
|
return Widget.Button(
|
||||||
|
child=Widget.Icon(image="system-shutdown-symbolic", pixel_size=20),
|
||||||
|
halign="end",
|
||||||
|
css_classes=["user-power", "unset"],
|
||||||
|
on_click=lambda x: app.toggle_window("ignis_POWERMENU"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def user() -> Widget.Box:
|
||||||
|
return Widget.Box(
|
||||||
|
child=[user_image(), username(), settings_button(), power_button()],
|
||||||
|
css_classes=["user"],
|
||||||
|
)
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from ignis.services.audio import AudioService, Stream
|
||||||
|
|
||||||
|
audio = AudioService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def volume_scale(stream: Stream) -> Widget.Scale:
|
||||||
|
return Widget.Scale(
|
||||||
|
css_classes=["material-slider"],
|
||||||
|
value=stream.bind("volume"),
|
||||||
|
step=5,
|
||||||
|
hexpand=True,
|
||||||
|
on_change=lambda x: stream.set_volume(x.value),
|
||||||
|
sensitive=stream.bind("is_muted", lambda value: not value),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def device_entry(stream: Stream, _type: str) -> Widget.Button:
|
||||||
|
widget = Widget.Button(
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image="audio-card-symbolic"),
|
||||||
|
Widget.Label(
|
||||||
|
label=stream.description,
|
||||||
|
ellipsize="end",
|
||||||
|
max_width_chars=30,
|
||||||
|
halign="start",
|
||||||
|
css_classes=["volume-entry-label"],
|
||||||
|
),
|
||||||
|
Widget.Icon(
|
||||||
|
image="object-select-symbolic",
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
visible=stream.bind("is_default"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
css_classes=["volume-entry", "unset"],
|
||||||
|
hexpand=True,
|
||||||
|
on_click=lambda x: setattr(audio, _type, stream),
|
||||||
|
)
|
||||||
|
stream.connect("removed", lambda x: widget.unparent())
|
||||||
|
|
||||||
|
return widget
|
||||||
|
|
||||||
|
|
||||||
|
def device_list(
|
||||||
|
header_label: str, header_icon: str, _type: str, **kwargs
|
||||||
|
) -> Widget.Revealer:
|
||||||
|
box = Widget.Box(
|
||||||
|
css_classes=["control-center-menu"],
|
||||||
|
vertical=True,
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image=header_icon, pixel_size=24),
|
||||||
|
Widget.Label(
|
||||||
|
label=header_label,
|
||||||
|
halign="start",
|
||||||
|
css_classes=["volume-entry-list-header-label"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
css_classes=["volume-entry-list-header-box"],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
setup=lambda self: audio.connect(
|
||||||
|
f"{_type}-added",
|
||||||
|
lambda x, stream: self.append(device_entry(stream, _type)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Widget.Separator(css_classes=["volume-entry-list-separator"]),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image="preferences-system-symbolic"),
|
||||||
|
Widget.Label(
|
||||||
|
label="Sound Settings",
|
||||||
|
halign="start",
|
||||||
|
css_classes=["volume-entry-label"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
css_classes=["volume-entry", "unset"],
|
||||||
|
style="margin-bottom: 0;",
|
||||||
|
on_click=lambda x: Utils.exec_sh_async("pavucontrol"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
return Widget.Revealer(
|
||||||
|
child=box, transition_type="slide_down", transition_duration=300
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def volume_icon(stream: Stream) -> Widget.Button:
|
||||||
|
return Widget.Button(
|
||||||
|
child=Widget.Icon(
|
||||||
|
image=stream.bind("icon_name"),
|
||||||
|
pixel_size=18,
|
||||||
|
),
|
||||||
|
css_classes=["material-slider-icon", "unset", "hover-surface"],
|
||||||
|
on_click=lambda x: stream.set_is_muted(not stream.is_muted),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def device_list_arrow(device_list: Widget.Revealer) -> Widget.Button:
|
||||||
|
return Widget.ArrowButton(
|
||||||
|
arrow=Widget.Arrow(pixel_size=20),
|
||||||
|
css_classes=["material-slider-arrow", "hover-surface"],
|
||||||
|
on_click=lambda x: device_list.toggle(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def volume_control():
|
||||||
|
speakers_list = device_list(
|
||||||
|
header_label="Sound Output",
|
||||||
|
header_icon="audio-headphones-symbolic",
|
||||||
|
_type="speaker",
|
||||||
|
style="margin-bottom: 1rem;",
|
||||||
|
)
|
||||||
|
|
||||||
|
microphones_list = device_list(
|
||||||
|
header_label="Sound Input",
|
||||||
|
header_icon="microphone-sensitivity-high-symbolic",
|
||||||
|
_type="microphone",
|
||||||
|
)
|
||||||
|
|
||||||
|
speaker_icon = volume_icon(audio.speaker)
|
||||||
|
microphone_icon = volume_icon(audio.microphone)
|
||||||
|
|
||||||
|
speaker_scale = volume_scale(audio.speaker)
|
||||||
|
microphone_scale = volume_scale(audio.microphone)
|
||||||
|
|
||||||
|
speaker_arrow = device_list_arrow(speakers_list)
|
||||||
|
microphone_arrow = device_list_arrow(microphones_list)
|
||||||
|
|
||||||
|
speaker_control = Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=[
|
||||||
|
Widget.Box(child=[speaker_icon, speaker_scale, speaker_arrow]),
|
||||||
|
speakers_list,
|
||||||
|
],
|
||||||
|
style="margin-top: 1rem;",
|
||||||
|
)
|
||||||
|
|
||||||
|
microphone_control = Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=[
|
||||||
|
Widget.Box(child=[microphone_icon, microphone_scale, microphone_arrow]),
|
||||||
|
microphones_list,
|
||||||
|
],
|
||||||
|
style="margin-top: 0.25rem;",
|
||||||
|
)
|
||||||
|
|
||||||
|
return Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=[speaker_control, microphone_control],
|
||||||
|
)
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from .qs_button import QSButton
|
||||||
|
from typing import List
|
||||||
|
from ignis.services.network import NetworkService, VpnConnection
|
||||||
|
|
||||||
|
|
||||||
|
network = NetworkService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class VpnNetworkItem(Widget.Button):
|
||||||
|
def __init__(self, conn: VpnConnection):
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
css_classes=["vpn-connection-item", "unset"],
|
||||||
|
on_click=lambda x: conn.toggle_connection(),
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Label(
|
||||||
|
label=conn.name,
|
||||||
|
ellipsize="end",
|
||||||
|
max_width_chars=20,
|
||||||
|
halign="start",
|
||||||
|
css_classes=["vpn-connection-label"],
|
||||||
|
),
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Label(
|
||||||
|
label=conn.bind(
|
||||||
|
"is_connected",
|
||||||
|
lambda value: "Disconnect" if value else "Connect",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
css_classes=["vpn-connection-item-connect-label", "unset"],
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def vpn_qsbutton() -> QSButton:
|
||||||
|
networks_list = Widget.Revealer(
|
||||||
|
transition_duration=300,
|
||||||
|
transition_type="slide_down",
|
||||||
|
child=Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["control-center-menu"],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
css_classes=["vpn-header-box"],
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
icon_name="network-vpn-symbolic", pixel_size=28),
|
||||||
|
Widget.Label(
|
||||||
|
label="VPN connections",
|
||||||
|
css_classes=["vpn-header-label"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=network.vpn.bind(
|
||||||
|
"connections",
|
||||||
|
transform=lambda value: [
|
||||||
|
VpnNetworkItem(i) for i in value],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Widget.Separator(
|
||||||
|
css_classes=["vpn-connection-list-separator"]),
|
||||||
|
Widget.Button(
|
||||||
|
css_classes=["vpn-connection-item", "unset"],
|
||||||
|
on_click=lambda x: Utils.exec_sh_async(
|
||||||
|
"nm-connection-editor"),
|
||||||
|
style="margin-bottom: 0;",
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image="preferences-system-symbolic"),
|
||||||
|
Widget.Label(
|
||||||
|
label="Network Manager",
|
||||||
|
halign="start",
|
||||||
|
css_classes=["vpn-connection-label"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_label(id: str) -> str:
|
||||||
|
if id:
|
||||||
|
return id
|
||||||
|
else:
|
||||||
|
return "VPN"
|
||||||
|
|
||||||
|
def get_icon(icon_name: str) -> str:
|
||||||
|
if network.vpn.is_connected:
|
||||||
|
return icon_name
|
||||||
|
else:
|
||||||
|
return "network-vpn-symbolic"
|
||||||
|
|
||||||
|
def toggle_list(x) -> None:
|
||||||
|
networks_list.toggle()
|
||||||
|
|
||||||
|
return QSButton(
|
||||||
|
label=network.vpn.bind("active_vpn_id", get_label),
|
||||||
|
icon_name=network.vpn.bind("icon-name", get_icon),
|
||||||
|
on_activate=toggle_list,
|
||||||
|
on_deactivate=toggle_list,
|
||||||
|
active=network.vpn.bind("is-connected"),
|
||||||
|
content=networks_list,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def vpn_control() -> List[QSButton]:
|
||||||
|
if len(network.vpn.connections) > 0:
|
||||||
|
return [vpn_qsbutton()]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from .qs_button import QSButton
|
||||||
|
from ignis.services.network import NetworkService, WifiAccessPoint, WifiDevice
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
network = NetworkService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class WifiNetworkItem(Widget.Button):
|
||||||
|
def __init__(self, access_point: WifiAccessPoint):
|
||||||
|
super().__init__(
|
||||||
|
css_classes=["wifi-network-item", "unset"],
|
||||||
|
on_click=lambda x: access_point.connect_to_graphical(),
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
image=access_point.bind(
|
||||||
|
"strength", transform=lambda value: access_point.icon_name
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Widget.Label(
|
||||||
|
label=access_point.ssid,
|
||||||
|
halign="start",
|
||||||
|
css_classes=["wifi-network-label"],
|
||||||
|
),
|
||||||
|
Widget.Icon(
|
||||||
|
image="object-select-symbolic",
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
visible=access_point.bind("is_connected"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def wifi_qsbutton(device: WifiDevice) -> QSButton:
|
||||||
|
networks_list = Widget.Revealer(
|
||||||
|
transition_duration=300,
|
||||||
|
transition_type="slide_down",
|
||||||
|
child=Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["control-center-menu"],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Label(label="Wi-Fi", css_classes=["wifi-header-label"]),
|
||||||
|
Widget.Switch(
|
||||||
|
halign="end",
|
||||||
|
hexpand=True,
|
||||||
|
active=network.wifi.enabled,
|
||||||
|
on_change=lambda x, state: network.wifi.set_enabled(state),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
css_classes=["toggle-box", "wifi-header-box"],
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
child=device.bind(
|
||||||
|
"access_points",
|
||||||
|
transform=lambda value: [WifiNetworkItem(i) for i in value],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Widget.Separator(css_classes=["wifi-network-list-separator"]),
|
||||||
|
Widget.Button(
|
||||||
|
css_classes=["wifi-network-item", "unset"],
|
||||||
|
on_click=lambda x: Utils.exec_sh_async("nm-connection-editor"),
|
||||||
|
style="margin-bottom: 0;",
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image="preferences-system-symbolic"),
|
||||||
|
Widget.Label(
|
||||||
|
label="Network Settings",
|
||||||
|
halign="start",
|
||||||
|
css_classes=["wifi-network-label"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_label(ssid: str) -> str:
|
||||||
|
if ssid:
|
||||||
|
return ssid
|
||||||
|
else:
|
||||||
|
return "Wi-Fi"
|
||||||
|
|
||||||
|
def get_icon(icon_name: str) -> str:
|
||||||
|
if device.ap.is_connected:
|
||||||
|
return icon_name
|
||||||
|
else:
|
||||||
|
return "network-wireless-symbolic"
|
||||||
|
|
||||||
|
def toggle_list(x) -> None:
|
||||||
|
device.scan()
|
||||||
|
networks_list.toggle()
|
||||||
|
|
||||||
|
return QSButton(
|
||||||
|
label=device.ap.bind("ssid", get_label),
|
||||||
|
icon_name=device.ap.bind("icon-name", get_icon),
|
||||||
|
on_activate=toggle_list,
|
||||||
|
on_deactivate=toggle_list,
|
||||||
|
active=network.wifi.bind("enabled"),
|
||||||
|
content=networks_list,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def wifi_control() -> List[QSButton]:
|
||||||
|
return [wifi_qsbutton(dev) for dev in network.wifi.devices]
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
import re
|
||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from ignis.services.applications import (
|
||||||
|
ApplicationsService,
|
||||||
|
Application,
|
||||||
|
ApplicationAction,
|
||||||
|
)
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from gi.repository import Gio # type: ignore
|
||||||
|
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
applications = ApplicationsService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
def is_url(url: str) -> bool:
|
||||||
|
regex = re.compile(
|
||||||
|
r"^(?:http|ftp)s?://" # http:// or https://
|
||||||
|
r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain
|
||||||
|
r"localhost|" # localhost
|
||||||
|
r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|" # or ipv4
|
||||||
|
r"\[?[A-F0-9]*:[A-F0-9:]+\]?)" # or ipv6
|
||||||
|
r"(?::\d+)?" # optional port
|
||||||
|
r"(?:/?|[/?]\S+)$",
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
return re.match(regex, url) is not None
|
||||||
|
|
||||||
|
|
||||||
|
class LauncherAppItem(Widget.Button):
|
||||||
|
def __init__(self, application: Application) -> None:
|
||||||
|
self._application = application
|
||||||
|
super().__init__(
|
||||||
|
on_click=lambda x: self.launch(),
|
||||||
|
on_right_click=lambda x: self._menu.popup(),
|
||||||
|
css_classes=["launcher-app"],
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image=application.icon, pixel_size=48),
|
||||||
|
Widget.Label(
|
||||||
|
label=application.name,
|
||||||
|
ellipsize="end",
|
||||||
|
max_width_chars=30,
|
||||||
|
css_classes=["launcher-app-label"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.__sync_menu()
|
||||||
|
application.connect("notify::is-pinned", lambda x, y: self.__sync_menu())
|
||||||
|
|
||||||
|
def launch(self) -> None:
|
||||||
|
self._application.launch()
|
||||||
|
app.close_window("ignis_LAUNCHER")
|
||||||
|
|
||||||
|
def launch_action(self, action: ApplicationAction) -> None:
|
||||||
|
action.launch()
|
||||||
|
app.close_window("ignis_LAUNCHER")
|
||||||
|
|
||||||
|
def __sync_menu(self) -> None:
|
||||||
|
self._menu = Widget.PopoverMenu(
|
||||||
|
items=[
|
||||||
|
Widget.MenuItem(label="Launch", on_activate=lambda x: self.launch()),
|
||||||
|
Widget.Separator(),
|
||||||
|
]
|
||||||
|
+ [
|
||||||
|
Widget.MenuItem(
|
||||||
|
label=i.name,
|
||||||
|
on_activate=lambda x, action=i: self.launch_action(action),
|
||||||
|
)
|
||||||
|
for i in self._application.actions
|
||||||
|
]
|
||||||
|
+ [
|
||||||
|
Widget.Separator(),
|
||||||
|
Widget.MenuItem(
|
||||||
|
label="Pin", on_activate=lambda x: self._application.pin()
|
||||||
|
)
|
||||||
|
if not self._application.is_pinned
|
||||||
|
else Widget.MenuItem(
|
||||||
|
label="Unpin", on_activate=lambda x: self._application.unpin()
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.child.append(self._menu)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchWebButton(Widget.Button):
|
||||||
|
def __init__(self, query: str):
|
||||||
|
self._query = query
|
||||||
|
self._url = ""
|
||||||
|
|
||||||
|
browser_desktop_file = Utils.exec_sh(
|
||||||
|
"xdg-settings get default-web-browser"
|
||||||
|
).stdout.replace("\n", "")
|
||||||
|
|
||||||
|
app_info = Gio.DesktopAppInfo.new(desktop_id=browser_desktop_file)
|
||||||
|
|
||||||
|
icon_name = "applications-internet-symbolic"
|
||||||
|
if app_info:
|
||||||
|
icon_string = app_info.get_string("Icon")
|
||||||
|
if icon_string:
|
||||||
|
icon_name = icon_string
|
||||||
|
|
||||||
|
if not query.startswith(("http://", "https://")) and "." in query:
|
||||||
|
query = "https://" + query
|
||||||
|
|
||||||
|
if is_url(query):
|
||||||
|
label = f"Visit {query}"
|
||||||
|
self._url = query
|
||||||
|
else:
|
||||||
|
label = "Search in Google"
|
||||||
|
self._url = f"https://www.google.com/search?q={query.replace(' ', '+')}"
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
on_click=lambda x: self.launch(),
|
||||||
|
css_classes=["launcher-app"],
|
||||||
|
child=Widget.Box(
|
||||||
|
child=[
|
||||||
|
Widget.Icon(image=icon_name, pixel_size=48),
|
||||||
|
Widget.Label(
|
||||||
|
label=label,
|
||||||
|
css_classes=["launcher-app-label"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def launch(self) -> None:
|
||||||
|
Utils.exec_sh_async(f"xdg-open {self._url}")
|
||||||
|
app.close_window("ignis_LAUNCHER")
|
||||||
|
|
||||||
|
|
||||||
|
def launcher() -> Widget.Window:
|
||||||
|
def search(entry: Widget.Entry, app_list: Widget.Box) -> None:
|
||||||
|
query = entry.text
|
||||||
|
|
||||||
|
if query == "":
|
||||||
|
entry.grab_focus()
|
||||||
|
app_list.visible = False
|
||||||
|
return
|
||||||
|
|
||||||
|
apps = applications.search(applications.apps, query)
|
||||||
|
if apps == []:
|
||||||
|
app_list.child = [SearchWebButton(query)]
|
||||||
|
else:
|
||||||
|
app_list.visible = True
|
||||||
|
app_list.child = [LauncherAppItem(i) for i in apps[:5]]
|
||||||
|
|
||||||
|
def on_open(window: Widget.Window, entry: Widget.Entry) -> None:
|
||||||
|
if not window.visible:
|
||||||
|
return
|
||||||
|
|
||||||
|
entry.text = ""
|
||||||
|
entry.grab_focus()
|
||||||
|
|
||||||
|
app_list = Widget.Box(vertical=True, visible=False, style="margin-top: 1rem;")
|
||||||
|
entry = Widget.Entry(
|
||||||
|
hexpand=True,
|
||||||
|
placeholder_text="Search",
|
||||||
|
css_classes=["launcher-search"],
|
||||||
|
on_change=lambda x: search(x, app_list),
|
||||||
|
on_accept=lambda x: app_list.child[0].launch()
|
||||||
|
if len(app_list.child) > 0
|
||||||
|
else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
main_box = Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
valign="start",
|
||||||
|
halign="center",
|
||||||
|
css_classes=["launcher"],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
css_classes=["launcher-search-box"],
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
icon_name="system-search-symbolic",
|
||||||
|
pixel_size=24,
|
||||||
|
style="margin-right: 0.5rem;",
|
||||||
|
),
|
||||||
|
entry,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
app_list,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
return Widget.Window(
|
||||||
|
namespace="ignis_LAUNCHER",
|
||||||
|
visible=False,
|
||||||
|
popup=True,
|
||||||
|
kb_mode="on_demand",
|
||||||
|
# exclusivity="ignore",
|
||||||
|
css_classes=["unset"],
|
||||||
|
setup=lambda self: self.connect(
|
||||||
|
"notify::visible", lambda x, y: on_open(self, entry)
|
||||||
|
),
|
||||||
|
anchor=["top", "right", "bottom", "left"],
|
||||||
|
child=Widget.Overlay(
|
||||||
|
child=Widget.Button(
|
||||||
|
vexpand=True,
|
||||||
|
hexpand=True,
|
||||||
|
can_focus=False,
|
||||||
|
css_classes=["unset"],
|
||||||
|
on_click=lambda x: app.close_window("ignis_LAUNCHER"),
|
||||||
|
style="background-color: rgba(0, 0, 0, 0.3);",
|
||||||
|
),
|
||||||
|
overlays=[main_box],
|
||||||
|
),
|
||||||
|
)
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,95 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from ignis.services.notifications import Notification, NotificationService
|
||||||
|
from ..control_center.notification_center import NotificationWidget
|
||||||
|
|
||||||
|
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
notifications = NotificationService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class Popup(Widget.Box):
|
||||||
|
def __init__(self, notification: Notification):
|
||||||
|
widget = NotificationWidget(notification)
|
||||||
|
widget.css_classes = ["notification-popup"]
|
||||||
|
self._inner = Widget.Revealer(transition_type="slide_left", child=widget)
|
||||||
|
self._outer = Widget.Revealer(transition_type="slide_down", child=self._inner)
|
||||||
|
super().__init__(child=[self._outer], halign="end")
|
||||||
|
|
||||||
|
notification.connect("dismissed", lambda x: self.destroy())
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
def box_destroy():
|
||||||
|
box: Widget.Box = self.get_parent() # type: ignore
|
||||||
|
if not box:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.unparent()
|
||||||
|
if len(notifications.popups) == 0:
|
||||||
|
window: Widget.Window = box.get_parent() # type: ignore
|
||||||
|
if not window:
|
||||||
|
return
|
||||||
|
window.visible = False
|
||||||
|
else:
|
||||||
|
change_window_input_region(box)
|
||||||
|
|
||||||
|
def outer_close():
|
||||||
|
self._outer.reveal_child = False
|
||||||
|
Utils.Timeout(self._outer.transition_duration, box_destroy)
|
||||||
|
|
||||||
|
self._inner.transition_type = "crossfade"
|
||||||
|
self._inner.reveal_child = False
|
||||||
|
Utils.Timeout(self._outer.transition_duration, outer_close)
|
||||||
|
|
||||||
|
|
||||||
|
def on_notified(box: Widget.Box, notification: Notification, monitor: int) -> None:
|
||||||
|
window: Widget.Window = app.get_window("ignis_CONTROL_CENTER") # type: ignore
|
||||||
|
if window.visible and window.monitor == monitor:
|
||||||
|
return
|
||||||
|
|
||||||
|
app.open_window(f"ignis_NOTIFICATION_POPUP_{monitor}")
|
||||||
|
popup = Popup(notification)
|
||||||
|
box.prepend(popup)
|
||||||
|
popup._outer.reveal_child = True
|
||||||
|
Utils.Timeout(popup._outer.transition_duration, reveal_popup, box, popup)
|
||||||
|
|
||||||
|
|
||||||
|
def reveal_popup(box: Widget.Box, popup: Popup) -> None:
|
||||||
|
popup._inner.set_reveal_child(True)
|
||||||
|
change_window_input_region(box)
|
||||||
|
|
||||||
|
|
||||||
|
def change_window_input_region(box: Widget.Box) -> None:
|
||||||
|
def callback() -> None:
|
||||||
|
width = box.get_width()
|
||||||
|
height = box.get_height()
|
||||||
|
window: Widget.Window = box.get_parent() # type: ignore
|
||||||
|
|
||||||
|
window.input_width = width
|
||||||
|
window.input_height = height
|
||||||
|
|
||||||
|
Utils.Timeout(ms=50, target=callback)
|
||||||
|
|
||||||
|
|
||||||
|
def notification_popup(monitor: int) -> Widget.Window:
|
||||||
|
notifications_box = Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
valign="start",
|
||||||
|
setup=lambda self: notifications.connect(
|
||||||
|
"new_popup",
|
||||||
|
lambda x, notification: on_notified(self, notification, monitor),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Widget.Window(
|
||||||
|
anchor=["right", "top", "bottom"],
|
||||||
|
monitor=monitor,
|
||||||
|
namespace=f"ignis_NOTIFICATION_POPUP_{monitor}",
|
||||||
|
layer="top",
|
||||||
|
child=notifications_box,
|
||||||
|
visible=False,
|
||||||
|
css_classes=["rec-unset"],
|
||||||
|
style="min-width: 29rem;",
|
||||||
|
)
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,43 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from ignis.services.audio import AudioService
|
||||||
|
|
||||||
|
audio = AudioService.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class OSD(Widget.Window):
|
||||||
|
def __init__(self):
|
||||||
|
self._timer = None
|
||||||
|
super().__init__(
|
||||||
|
layer="overlay",
|
||||||
|
anchor=["bottom"],
|
||||||
|
namespace="ignis_OSD",
|
||||||
|
visible=False,
|
||||||
|
css_classes=["rec-unset"],
|
||||||
|
child=Widget.Box(
|
||||||
|
css_classes=["osd"],
|
||||||
|
child=[
|
||||||
|
Widget.Icon(
|
||||||
|
pixel_size=26,
|
||||||
|
style="margin-right: 0.5rem;",
|
||||||
|
image=audio.speaker.bind("icon_name"),
|
||||||
|
),
|
||||||
|
Widget.Scale(
|
||||||
|
value=audio.speaker.bind("volume"),
|
||||||
|
css_classes=["material-slider"],
|
||||||
|
sensitive=False,
|
||||||
|
hexpand=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_property(self, property_name, value):
|
||||||
|
if property_name == "visible":
|
||||||
|
if self._timer:
|
||||||
|
self._timer.cancel()
|
||||||
|
self._timer = None
|
||||||
|
|
||||||
|
self._timer = Utils.Timeout(3000, self.set_visible, False)
|
||||||
|
|
||||||
|
super().set_property(property_name, value)
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,98 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.utils import Utils
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class PowermenuButton(Widget.Box):
|
||||||
|
def __init__(self, label: str, icon_name: str, on_click: Callable) -> None:
|
||||||
|
super().__init__(
|
||||||
|
child=[
|
||||||
|
Widget.Button(
|
||||||
|
child=Widget.Icon(image=icon_name, pixel_size=36),
|
||||||
|
on_click=on_click,
|
||||||
|
css_classes=["powermenu-button", "unset"],
|
||||||
|
),
|
||||||
|
Widget.Label(label=label, css_classes=["powermenu-button-label"]),
|
||||||
|
],
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["powermenu-button-box"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def poweroff(*args) -> None:
|
||||||
|
Utils.exec_sh_async("poweroff")
|
||||||
|
|
||||||
|
|
||||||
|
def reboot(*args) -> None:
|
||||||
|
Utils.exec_sh_async("reboot")
|
||||||
|
|
||||||
|
|
||||||
|
def suspend(*args) -> None:
|
||||||
|
app.close_window("ignis_POWERMENU")
|
||||||
|
Utils.exec_sh_async("systemctl suspend && hyprlock")
|
||||||
|
|
||||||
|
|
||||||
|
def hypr_exit(*args) -> None:
|
||||||
|
Utils.exec_sh_async("hyprctl dispatch exit 0")
|
||||||
|
|
||||||
|
|
||||||
|
def powermenu():
|
||||||
|
return Widget.Window(
|
||||||
|
popup=True,
|
||||||
|
kb_mode="on_demand",
|
||||||
|
namespace="ignis_POWERMENU",
|
||||||
|
exclusivity="ignore",
|
||||||
|
anchor=["left", "right", "top", "bottom"],
|
||||||
|
visible=False,
|
||||||
|
child=Widget.Overlay(
|
||||||
|
child=Widget.Button(
|
||||||
|
vexpand=True,
|
||||||
|
hexpand=True,
|
||||||
|
can_focus=False,
|
||||||
|
css_classes=["unset", "powermenu-overlay"],
|
||||||
|
on_click=lambda x: app.close_window("ignis_POWERMENU"),
|
||||||
|
),
|
||||||
|
overlays=[
|
||||||
|
Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
valign="center",
|
||||||
|
halign="center",
|
||||||
|
css_classes=["powermenu"],
|
||||||
|
child=[
|
||||||
|
Widget.Box(
|
||||||
|
child=[
|
||||||
|
PowermenuButton(
|
||||||
|
label="Power off",
|
||||||
|
icon_name="system-shutdown-symbolic",
|
||||||
|
on_click=poweroff,
|
||||||
|
),
|
||||||
|
PowermenuButton(
|
||||||
|
label="Reboot",
|
||||||
|
icon_name="system-reboot-symbolic",
|
||||||
|
on_click=reboot,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
Widget.Box(
|
||||||
|
child=[
|
||||||
|
PowermenuButton(
|
||||||
|
label="Suspend",
|
||||||
|
icon_name="night-light-symbolic",
|
||||||
|
on_click=suspend,
|
||||||
|
),
|
||||||
|
PowermenuButton(
|
||||||
|
label="Sign out",
|
||||||
|
icon_name="system-log-out-symbolic",
|
||||||
|
on_click=hypr_exit,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
css_classes=["unset"],
|
||||||
|
)
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,83 @@
|
|||||||
|
from ignis.widgets import Widget
|
||||||
|
from ignis.gobject import IgnisGObject
|
||||||
|
from gi.repository import GObject # type: ignore
|
||||||
|
from .notifications import notifications_entry
|
||||||
|
from .about import about_entry
|
||||||
|
from .appearance import appearance_entry
|
||||||
|
from .recorder import recorder_entry
|
||||||
|
from .user import user_entry
|
||||||
|
from .elements import SettingsPage
|
||||||
|
from ignis.app import IgnisApp
|
||||||
|
from ignis.exceptions import WindowNotFoundError
|
||||||
|
from options import settings_last_page
|
||||||
|
|
||||||
|
app = IgnisApp.get_default()
|
||||||
|
|
||||||
|
|
||||||
|
class ActivePage(IgnisGObject):
|
||||||
|
def __init__(self, name: str | None, page: SettingsPage | None):
|
||||||
|
super().__init__()
|
||||||
|
self._name = name
|
||||||
|
self._page = page
|
||||||
|
|
||||||
|
@GObject.Property
|
||||||
|
def name(self) -> str | None:
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, value: str | None) -> None:
|
||||||
|
self._name = value
|
||||||
|
|
||||||
|
@GObject.Property
|
||||||
|
def page(self) -> SettingsPage | None:
|
||||||
|
return self._page
|
||||||
|
|
||||||
|
@page.setter
|
||||||
|
def page(self, value: SettingsPage | None) -> None:
|
||||||
|
self._page = value
|
||||||
|
|
||||||
|
|
||||||
|
def settings_widget():
|
||||||
|
active_page = ActivePage(name="Settings", page=None)
|
||||||
|
content = Widget.Box(
|
||||||
|
hexpand=True,
|
||||||
|
vexpand=True,
|
||||||
|
child=active_page.bind("page", transform=lambda value: [value]),
|
||||||
|
)
|
||||||
|
listbox = Widget.ListBox(
|
||||||
|
rows=[
|
||||||
|
notifications_entry(active_page),
|
||||||
|
recorder_entry(active_page),
|
||||||
|
appearance_entry(active_page),
|
||||||
|
user_entry(active_page),
|
||||||
|
about_entry(active_page),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
listbox.select_row(listbox.rows[settings_last_page.value])
|
||||||
|
|
||||||
|
navigation_sidebar = Widget.Box(
|
||||||
|
vertical=True,
|
||||||
|
css_classes=["settings-sidebar"],
|
||||||
|
child=[
|
||||||
|
Widget.Label(
|
||||||
|
label="Settings", halign="start", css_classes=["settings-sidebar-label"]
|
||||||
|
),
|
||||||
|
listbox,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
return Widget.Box(child=[navigation_sidebar, content])
|
||||||
|
|
||||||
|
|
||||||
|
def settings_window():
|
||||||
|
try:
|
||||||
|
app.get_window("ignis_SETTINGS")
|
||||||
|
except WindowNotFoundError:
|
||||||
|
return Widget.RegularWindow(
|
||||||
|
default_width=900,
|
||||||
|
default_height=600,
|
||||||
|
resizable=False,
|
||||||
|
child=settings_widget(),
|
||||||
|
namespace="ignis_SETTINGS",
|
||||||
|
)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user