diff --git a/config/ags/apps/settings/apps.ts b/config/ags/apps/settings/apps.ts new file mode 100644 index 00000000..c9f10f61 --- /dev/null +++ b/config/ags/apps/settings/apps.ts @@ -0,0 +1,78 @@ +const apps_script = `python -OOO ${App.configDir}/scripts/apps.py`; +import { MaterialIcon } from "icons"; + +const Row = (app: string, title: string, icon: string) => Widget.EventBox({ + class_name: "row", + on_primary_click: self => { + self.child.children[1]!.activate() + }, + child: Widget.Box({ + class_name: "row", + vpack: "start", + children: [ + MaterialIcon(icon), + Widget.Box({ + vertical: true, + hexpand: true, + vpack: "center", + children: [ + Widget.Label({ + hpack: "start", + class_name: "title", + label: title + }) + ] + }), + Widget.Entry({ + hpack: "end", + css: "border: 2px solid; border-color: transparent;", + attribute: { + "get": (self) => { + Utils.execAsync(`${apps_script} --get ${app}`) + .then((out) => { + self.text = out; + }) + .catch(print) + } + }, + on_accept: self => { + Utils.execAsync(`${apps_script} --${app} ${self.text}`) + .then(() => { + self.css = "border: 2px solid; border-color: #66BB6A;" + Utils.timeout(1000, () => { + self.css = "border: 2px solid; border-color: transparent" + }) + }) + .catch(() => { + self.css = "border: 2px solid; border-color: @error;" + Utils.timeout(1000, () => { + self.css = "border: 2px solid; border-color: transparent" + }) + self.attribute.get(self) + }) + }, + setup: (self) => { + self.attribute.get(self) + } + }) + ] + }) +}) + + +export function Apps() { + const box = Widget.Box({ + vertical: true, + children: [ + Row("browser", "Browser", "web"), + Row("editor", "Editor", "edit"), + Row("filemanager", "File manager", "folder"), + Row("terminal", "Terminal", "terminal") + ], + }) + return Widget.Scrollable({ + hscroll: "never", + child: box, + vexpand: true + }) +} diff --git a/config/ags/apps/settings/main.ts b/config/ags/apps/settings/main.ts index 2b1e4883..4b487f60 100644 --- a/config/ags/apps/settings/main.ts +++ b/config/ags/apps/settings/main.ts @@ -3,7 +3,8 @@ import { Network } from "./network"; import { Bluetooth } from "./bluetooth"; import { Wallpapers } from "./wallpapers"; import { Info } from "./info"; -import { Widget as WidgetType } from "types/widgets/widget"; +import { Apps } from "./apps"; +import { MaterialIcon } from "icons"; const hyprland = await Service.import("hyprland") import Gtk from "gi://Gtk?version=3.0" @@ -42,14 +43,15 @@ function Settings(cur_tab: string) { "network": Page(Network(), "Network"), "bluetooth": Page(Bluetooth(), "Bluetooth"), "wallpaper": Page(Wallpapers(), "Wallpapers"), - "info": Page(Info(), "Info") + "info": Page(Info(), "Info"), + "apps": Page(Apps(), "Apps") } }) const Row = (name: string, label: string, icon: string = "image-missing") => Widget.Button({ on_clicked: () => { current_tab.setValue(name) }, child: Widget.Box({ children: [ - Widget.Icon(icon), + MaterialIcon(icon), Widget.Label(label) ] }), @@ -61,12 +63,14 @@ function Settings(cur_tab: string) { class_name: "sidebar", spacing: 2, children: [ - Row("network", "Network", "network-wireless-signal-excellent-symbolic"), - Row("bluetooth", "Bluetooth", "bluetooth-active-symbolic"), + Row("network", "Network", "signal_wifi_4_bar"), + Row("bluetooth", "Bluetooth", "bluetooth"), Widget.Separator(), - Row("wallpaper", "Wallpapers", "folder-pictures-symbolic"), + Row("wallpaper", "Wallpapers", "image"), Widget.Separator(), - Row("info", "Info", "dialog-information-symbolic"), + Row("apps", "Apps", "grid_view"), + Widget.Separator(), + Row("info", "Info", "info") ] }) return Widget.Box({ diff --git a/config/ags/apps/settings/network.ts b/config/ags/apps/settings/network.ts index 50aace04..a4b47f1a 100644 --- a/config/ags/apps/settings/network.ts +++ b/config/ags/apps/settings/network.ts @@ -1,8 +1,17 @@ import Gtk from "gi://Gtk?version=3.0" import { timeout } from "resource:///com/github/Aylur/ags/utils.js"; import { Variable as VariableType } from "types/variable"; +import { MaterialIcon } from "icons"; const systemtray = await Service.import("systemtray") +const MATERIAL_SYMBOL_SIGNAL_STRENGTH = { + 'network-wireless-signal-excellent-symbolic': "signal_wifi_4_bar", + 'network-wireless-signal-good-symbolic': "network_wifi_3_bar", + 'network-wireless-signal-ok-symbolic': "network_wifi_2_bar", + 'network-wireless-signal-weak-symbolic': "network_wifi_1_bar", + 'network-wireless-signal-none-symbolic': "signal_wifi_0_bar", +} + type AccessPoint = { bssid: string | null; address: string | null; @@ -70,10 +79,10 @@ const WifiNetwork = (access_point: AccessPoint) => { child: Widget.Box({ class_name: "wifi_network_box", children: [ - Widget.Icon({ - icon: access_point.iconName, - size: 20 - }), + MaterialIcon( + MATERIAL_SYMBOL_SIGNAL_STRENGTH[access_point.iconName!], + "20px" + ), // @ts-ignore Widget.Box({ vertical: true, diff --git a/config/ags/assets/apps.json b/config/ags/assets/apps.json new file mode 100644 index 00000000..5219b860 --- /dev/null +++ b/config/ags/assets/apps.json @@ -0,0 +1,6 @@ +{ + "browser": "firefox", + "editor": "nvim", + "filemanager": "thunar", + "terminal": "kitty" +} \ No newline at end of file diff --git a/config/ags/emojis.json b/config/ags/assets/emojis.json similarity index 100% rename from config/ags/emojis.json rename to config/ags/assets/emojis.json diff --git a/config/ags/icons.ts b/config/ags/icons.ts new file mode 100644 index 00000000..88be6da0 --- /dev/null +++ b/config/ags/icons.ts @@ -0,0 +1,12 @@ +import { LabelProps } from "types/widgets/label" + +const _icons_folder = `${App.configDir}/assets/icons` + +export const MaterialIcon = (icon: string, size: string = "24px", props?: LabelProps) => Widget.Label({ + label: icon, + class_name: "icon material_icon", + css: `font-size: ${size};`, + hpack: "center", + vpack: "center", + ...props +}) diff --git a/config/ags/modules/bar.ts b/config/ags/modules/bar.ts index 306d1ad3..b39ae6ca 100644 --- a/config/ags/modules/bar.ts +++ b/config/ags/modules/bar.ts @@ -17,6 +17,7 @@ const mpris = await Service.import("mpris") const bluetooth = await Service.import("bluetooth") const { Box, Button, EventBox } = Widget; import Gtk from "gi://Gtk?version=3.0" +import { MaterialIcon } from "icons.js" function checkKeymap() { @@ -32,6 +33,14 @@ hyprland.connect("keyboard-layout", (hyprland, keyboardname, layoutname) => { keyboard_layout.setValue(layoutname.trim().toLowerCase().substr(0, 2)) }) +const MATERIAL_SYMBOL_SIGNAL_STRENGTH = { + 'network-wireless-signal-excellent-symbolic': "signal_wifi_4_bar", + 'network-wireless-signal-good-symbolic': "network_wifi_3_bar", + 'network-wireless-signal-ok-symbolic': "network_wifi_2_bar", + 'network-wireless-signal-weak-symbolic': "network_wifi_1_bar", + 'network-wireless-signal-none-symbolic': "signal_wifi_0_bar", +} + const time = Variable("", { poll: [1000, 'date "+%I:%M %p"'], }) @@ -246,9 +255,7 @@ function AppLauncher() { on_clicked: () => { App.toggleWindow("applauncher") }, - child: Widget.Label( - "\uf002" - ) + child: MaterialIcon("search") }) return button @@ -256,9 +263,11 @@ function AppLauncher() { + + function Wifi() { return Widget.Button({ - class_name: "bar_wifi icon", + class_name: "bar_wifi", on_primary_click: () => { OpenSettings("network") }, @@ -270,25 +279,24 @@ function Wifi() { Utils.execAsync("nm-connection-editor").catch(print) } }, - child: Widget.Icon({ - icon: "network-wireless-offline-symbolic" - }), + child: MaterialIcon("signal_wifi_off", "16px"), tooltip_text: 'Disabled' }).hook(network, self => { if (network.wifi.enabled) { self.tooltip_text = network.wifi.ssid || 'Unknown'; - self.child.icon = network.wifi.icon_name; + self.child.label = MATERIAL_SYMBOL_SIGNAL_STRENGTH[network.wifi.icon_name] || "signal_wifi_off"; } else { self.tooltip_text = 'Disabled' - self.child.icon = "network-wireless-offline-symbolic"; + self.child.label = "signal_wifi_off"; } }) } + function Bluetooth() { return Widget.Button({ - class_name: "bar_bluetooth icon", + class_name: "bar_bluetooth", on_primary_click: () => { OpenSettings("bluetooth") }, @@ -300,14 +308,12 @@ function Bluetooth() { Utils.execAsync("blueman-manager").catch(print) } }, - child: Widget.Icon({ - icon: "bluetooth-disabled-symbolic" - }), + child: MaterialIcon("bluetooth_disabled", "18px"), }).hook(bluetooth, self => { if (bluetooth.enabled) { - self.child.icon = "bluetooth-active-symbolic"; + self.child.icon = "bluetooth"; } else { - self.child.icon = "bluetooth-disabled-symbolic"; + self.child.icon = "bluetooth_disabled"; } }) } @@ -328,14 +334,11 @@ function MediaPlayer() { class_name: "filled_tonal_button awesome_icon", on_primary_click: () => { App.toggleWindow("media") - // Utils.execAsync(["ags", "-t", "media"]) }, on_secondary_click: () => { Utils.execAsync(["playerctl", "play-pause"]).catch(print) }, - child: Widget.Label( - "" - ), + child: MaterialIcon("play_circle"), visible: false, tooltip_text: "Unknown" }).hook(mpris, self => { @@ -375,7 +378,7 @@ function OpenSideBar() { on_secondary_click: () => { OpenSettings() }, - label: "" + child: MaterialIcon("dock_to_left") }) return button @@ -432,31 +435,27 @@ if (globalWidgets.length === 0) { function volumeIndicator() { return Widget.EventBox({ - onScrollUp: () => { - audio.speaker.volume = Math.min(audio.speaker.volume + 0.05, 1); - }, - onScrollDown: () => { - audio.speaker.volume = Math.max(audio.speaker.volume - 0.05, 0); - }, + onScrollUp: () => audio.speaker.volume += 0.01, + onScrollDown: () => audio.speaker.volume -= 0.01, class_name: "filled_tonal_button volume_box", child: Widget.Button({ on_primary_click: () => Utils.execAsync("pavucontrol").catch(print), on_secondary_click: () => audio.speaker.is_muted = !audio.speaker.is_muted, child: Widget.Box({ children: [ - Widget.Icon().hook(audio.speaker, self => { + MaterialIcon("volume_off", "20px").hook(audio.speaker, self => { const vol = audio.speaker.volume * 100; const icon = [ - [101, 'overamplified'], - [67, 'high'], - [34, 'medium'], - [1, 'low'], - [0, 'muted'], + [101, 'sound_detection_loud_sound'], + [67, 'volume_up'], + [34, 'volume_down'], + [1, 'volume_mute'], + [0, 'volume_off'], ].find(([threshold]) => Number(threshold) <= vol)?.[1]; if (audio.speaker.is_muted) - self.icon = `audio-volume-muted-symbolic`; + self.label = "volume_off"; else - self.icon = `audio-volume-${icon}-symbolic`; + self.label = String(icon!); self.tooltip_text = `Volume ${Math.floor(vol)}%`; }), Widget.Label({ diff --git a/config/ags/modules/emoji.ts b/config/ags/modules/emoji.ts index 0b9341a9..36e52c3a 100644 --- a/config/ags/modules/emoji.ts +++ b/config/ags/modules/emoji.ts @@ -140,7 +140,7 @@ function Emoji({ width = 500, height = 500, spacing = 12 }) { setup: self => self.hook(App, (_, windowName, visible) => { if (windowName !== WINDOW_NAME) return; if (visible && emojisCache.length === 0) { - loadEmojis(`${App.configDir}/emojis.json`) + loadEmojis(`${App.configDir}/assets/emojis.json`) .then(emojis => { emojisCache = emojis; list.children = emojis.map(entry => EmojiItem(entry)); diff --git a/config/ags/modules/media.ts b/config/ags/modules/media.ts index 1a7f0507..aad2a85f 100644 --- a/config/ags/modules/media.ts +++ b/config/ags/modules/media.ts @@ -1,14 +1,15 @@ import { MprisPlayer } from 'types/service/mpris.js'; import popupwindow from './misc/popupwindow.ts'; import Label from 'types/widgets/label.js'; +import { MaterialIcon } from 'icons.ts'; const mpris = await Service.import("mpris") const players = mpris.bind("players") const FALLBACK_ICON = "audio-x-generic-symbolic" -const PLAY_ICON = "media-playback-start-symbolic" -const PAUSE_ICON = "media-playback-pause-symbolic" -const PREV_ICON = "media-skip-backward-symbolic" -const NEXT_ICON = "media-skip-forward-symbolic" +const PLAY_ICON = "play_arrow" +const PAUSE_ICON = "pause" +const PREV_ICON = "skip_previous" +const NEXT_ICON = "skip_next" const WINDOW_NAME = "media" function lengthStr(length: number): string { @@ -96,27 +97,28 @@ function Player(player: MprisPlayer) { class_name: "play-pause", on_clicked: () => player.playPause(), visible: player.bind("can_play"), - child: Widget.Icon({ - icon: player.bind("play_back_status").transform(s => { + child: MaterialIcon( + // @ts-ignore + player.bind("play_back_status").transform(s => { switch (s) { case "Playing": return PAUSE_ICON case "Paused": case "Stopped": return PLAY_ICON } - }), - }), + } + ), "18px"), }) const prev = Widget.Button({ on_clicked: () => player.previous(), visible: player.bind("can_go_prev"), - child: Widget.Icon(PREV_ICON), + child: MaterialIcon(PREV_ICON, "18px"), }) const next = Widget.Button({ on_clicked: () => player.next(), visible: player.bind("can_go_next"), - child: Widget.Icon(NEXT_ICON), + child: MaterialIcon(NEXT_ICON, "18px"), }) return Widget.Box( @@ -169,6 +171,5 @@ export const media = popupwindow({ visible: false, keymode: "exclusive", child: Media(), - //anchor: ["left", "top"], anchor: ["left", "bottom"], }) diff --git a/config/ags/modules/notificationPopups.ts b/config/ags/modules/notificationPopups.ts index 0cb1dd2c..1e51866d 100644 --- a/config/ags/modules/notificationPopups.ts +++ b/config/ags/modules/notificationPopups.ts @@ -5,6 +5,7 @@ import { timeout } from "resource:///com/github/Aylur/ags/utils.js"; import GLib from "gi://GLib"; import Pango from "gi://Pango"; import Label from "types/widgets/label"; +import { MaterialIcon } from "icons"; const notifications = await Service.import("notifications") import Gtk from "gi://Gtk?version=3.0" @@ -73,11 +74,11 @@ export const Notification = (notification: NotificationType, dismiss = true) => }), Widget.Label({ class_name: "notification-time", - label: GLib.DateTime.new_from_unix_local(notification.time).format("%I:%M %p"), + label: GLib.DateTime.new_from_unix_local(notification.time).format("%H:%M"), }), Widget.Button({ - class_name: "standard_icon_button", - child: Widget.Icon("window-close-symbolic"), + class_name: "standard_icon_button notification-close", + child: MaterialIcon("close"), on_clicked: () => { if (dismiss) { notification.dismiss(); diff --git a/config/ags/modules/sidebar/buttons.ts b/config/ags/modules/sidebar/buttons.ts index 28401b7f..eb224450 100644 --- a/config/ags/modules/sidebar/buttons.ts +++ b/config/ags/modules/sidebar/buttons.ts @@ -1,4 +1,7 @@ +import { MaterialIcon } from "icons"; + const { GLib } = imports.gi; + const lock_command = `hyprlock`; const logout_command = `loginctl kill-session $XDG_SESSION_ID`; const shutdown_command = `systemctl poweroff`; @@ -9,10 +12,7 @@ function LockButton({ icon, ...props }) { let clickCount = 0; let button = Widget.Button({ tooltip_text: "Lock", - child: Widget.Label({ - label: icon, - class_name: "awesome_icon" - }), + child: MaterialIcon(icon, "20px"), class_name: "outline_button", ...props, }) @@ -37,10 +37,7 @@ function SuspendButton({ icon, ...props }) { let clickCount = 0; let button = Widget.Button({ tooltip_text: "Suspend", - child: Widget.Label({ - label: icon, - class_name: "awesome_icon" - }), + child: MaterialIcon(icon, "20px"), class_name: "outline_button", ...props, }) @@ -67,10 +64,7 @@ function LogoutButton({ icon, ...props }) { let clickCount = 0; let button = Widget.Button({ tooltip_text: "Logout", - child: Widget.Label({ - label: icon, - class_name: "awesome_icon" - }), + child: MaterialIcon(icon, "20px"), class_name: "outline_button", ...props, }) @@ -95,10 +89,7 @@ function RebootButton({ icon, ...props }) { let clickCount = 0; let button = Widget.Button({ tooltip_text: "Reboot", - child: Widget.Label({ - label: icon, - class_name: "awesome_icon" - }), + child: MaterialIcon(icon, "20px"), class_name: "outline_button", ...props, }) @@ -123,10 +114,7 @@ function ShutdownButton({ icon, ...props }) { let clickCount = 0; let button = Widget.Button({ tooltip_text: "Shutdown", - child: Widget.Label({ - label: icon, - class_name: "awesome_icon" - }), + child: MaterialIcon(icon, "20px"), class_name: "outline_button", ...props, }) @@ -154,23 +142,23 @@ export function Buttons() { spacing: 5, children: [ LockButton({ - icon: "", + icon: "lock", hexpand: true, }), SuspendButton({ - icon: "", + icon: "clear_night", hexpand: true, }), LogoutButton({ - icon: "", + icon: "logout", hexpand: true, }), RebootButton({ - icon: "", + icon: "restart_alt", hexpand: true, }), ShutdownButton({ - icon: "", + icon: "power_settings_new", hexpand: true, }) ] diff --git a/config/ags/modules/sidebar/management.ts b/config/ags/modules/sidebar/management.ts index abe33c8a..b4b542f7 100644 --- a/config/ags/modules/sidebar/management.ts +++ b/config/ags/modules/sidebar/management.ts @@ -5,6 +5,7 @@ const powerProfiles = await Service.import('powerprofiles') import { OpenSettings } from "apps/settings/main.ts"; import { WINDOW_NAME } from "./main.ts" import { bluetooth_enabled, idle_inhibitor, night_light, screen_recorder } from "variables.ts"; +import { MaterialIcon } from "icons.ts"; import Gtk from "gi://Gtk?version=3.0" @@ -16,11 +17,7 @@ function WifiIndicator() { return Widget.Box({ css: "padding-left: 15px; padding-right: 15px; padding-top: 5px; padding-bottom: 5px;", children: [ - Widget.Label({ - label: "\udb82\udd28", - class_name: "awesome_icon", - css: `font-weight: bold; font-size: 20px; margin-right: 0.60em;`, - }), + MaterialIcon("signal_wifi_4_bar", "20px"), Widget.Box({ visible: network.wifi.bind('enabled'), orientation: Gtk.Orientation.VERTICAL, @@ -47,10 +44,8 @@ function WifiIndicator() { Widget.Box({ hpack: "end", hexpand: true, - child: Widget.Label({ - label: "", - class_name: "awesome_icon", - css: `font-weight: normal; font-size: 15px;`, + child: MaterialIcon("chevron_right", "20px", { + class_name: "material_icon icon arrow" }) }) ], @@ -60,9 +55,7 @@ function WifiIndicator() { const WiredIndicator = () => Widget.Box({ css: "padding-left: 15px; padding-right: 15px; padding-top: 5px; padding-bottom: 5px;", children: [ - Widget.Icon({ - icon: network.wired.bind('icon_name'), - }), + MaterialIcon("settings_ethernet", "20px"), Widget.Label({ label: "Internet", }), @@ -83,11 +76,7 @@ function IconAndName({ label, icon, padding = "0.3em", arrow = false }) { let box = Widget.Box({ css: "padding-left: 15px; padding-right: 15px; padding-top: 5px; padding-bottom: 5px;", children: [ - Widget.Label({ - label: icon, - class_name: "awesome_icon", - css: `font-weight: bold; font-size: 20px; margin-right: ${padding};`, - }), + MaterialIcon(icon, "20px"), Widget.Label({ label: label, justification: "center", @@ -98,10 +87,8 @@ function IconAndName({ label, icon, padding = "0.3em", arrow = false }) { const arrow = Widget.Box({ hpack: "end", hexpand: true, - child: Widget.Label({ - label: "", - class_name: "awesome_icon", - css: `font-weight: normal; font-size: 15px;`, + child: MaterialIcon("chevron_right", "20px", { + class_name: "material_icon icon arrow" }) }) // @ts-ignore @@ -133,7 +120,10 @@ function startRecordingTimer(button) { recordingStartTime = Date.now(); recordingInterval = setInterval(() => { let elapsedTime = Date.now() - recordingStartTime; - button.label = formatTime(elapsedTime); + button.child = IconAndName({ + label: formatTime(elapsedTime), + icon: "stop_circle" + }); }, 1000); } @@ -141,7 +131,11 @@ function stopRecordingTimer(button) { clearInterval(recordingInterval); recordingStartTime = null; recordingInterval = null; - button.label = '󰑋 Screen Recorder'; + button.label = 'Screen Recorder'; + button.child = IconAndName({ + label: "Screen Recorder", + icon: "screen_record" + }); } // Widget @@ -177,7 +171,7 @@ function Page1() { ), child: IconAndName({ label: "Bluetooth", - icon: "󰂯", + icon: "bluetooth", arrow: true, }), on_primary_click: () => { @@ -202,7 +196,7 @@ function Page1() { .as(bool => bool ? "management_button active" : "management_button"), child: IconAndName({ label: "Idle inhibitor", - icon: "" + icon: "schedule" }), onClicked: () => { idle_inhibitor.setValue(!idle_inhibitor.value) @@ -223,7 +217,8 @@ function Page1() { }, child: IconAndName({ label: "Do Not Disturb", - icon: "" + icon: notifications.bind("dnd") + .as((bool) => bool ? "do_not_disturb_on" : "do_not_disturb_off") }) }) ] @@ -234,37 +229,42 @@ function Page1() { hexpand: true, children: [ Widget.Button({ - hexpand: true, - class_name: screen_recorder.bind() - .as(bool => bool ? "management_button active" : "management_button"), - on_clicked: (self) => { + hexpand: true, + class_name: screen_recorder.bind() + .as(bool => bool ? "management_button active" : "management_button"), + child: IconAndName({ + label: "Screen Recorder", + icon: "screen_record" + }), + onClicked: (self) => { isScreenRecordingOn().then(isRecording => { if (isRecording) { stopRecordingTimer(self); - } else { - startRecordingTimer(self); - } - self.toggleClassName("active-button", !isRecording); - self.toggleClassName("recording", !isRecording); - - Utils.execAsync(['bash', '-c', 'mkdir -p ~/Videos/Screenrecordings; pkill wf-recorder; if [ $? -ne 0 ]; then wf-recorder -f ~/Videos/Screenrecordings/recording_"$(date +\'%b-%d-%Y-%I:%M:%S-%P\')".mp4 -g "$(slurp)" --pixel-format yuv420p; fi']) - .catch(logError) - .then(() => { - isScreenRecordingOn().then(newIsRecording => { - if (!newIsRecording) { - stopRecordingTimer(self); - } - }) - }); + } else { + startRecordingTimer(self); + } + self.toggleClassName("active-button", !isRecording); + self.toggleClassName("recording", !isRecording); + Utils.execAsync(['bash', '-c', 'mkdir -p ~/Videos/Screenrecordings; pkill wf-recorder; if [ $? -ne 0 ]; then wf-recorder -f ~/Videos/Screenrecordings/recording_"$(date +\'%b-%d-%Y-%I:%M:%S-%P\')".mp4 -g "$(slurp)" --pixel-format yuv420p; fi']) + .catch(logError) + .then(() => { + isScreenRecordingOn().then(newIsRecording => { + if (!newIsRecording) { + stopRecordingTimer(self); + } }); - }, - child: IconAndName({ - label: "Screen Recorder", - icon: "󰑋", - setup: self => { - self.label = '󰑋 Screen Recorder'; - } - }) + }); + }); + }, + setup: (self) => { + isScreenRecordingOn().then(isRecording => { + if (isRecording) { + startRecordingTimer(self); + self.addClassName("active-button"); + self.addClassName("recording"); + } + }); + } }), Widget.Button({ hexpand: true, @@ -272,7 +272,7 @@ function Page1() { .as(mode => mode ? "management_button active" : "management_button"), child: IconAndName({ label: "Night Light", - icon: "" + icon: "nightlight" }), onClicked: () => { night_light.setValue(!night_light.value) @@ -317,7 +317,7 @@ function Page2() { class_name: "management_button", child: IconAndName({ label: "Color picker", - icon: "", + icon: "colorize", arrow: true, }), on_primary_click: () => { @@ -338,11 +338,11 @@ function Page2() { icon: powerProfiles.bind('active_profile').as(profile => { switch (profile) { case 'balanced': - return ''; + return 'balance'; case 'performance': - return ''; + return 'bolt'; default: - return ''; + return 'eco'; } }) }), @@ -377,7 +377,7 @@ function Page2() { class_name: "management_button", child: IconAndName({ label: "Settings", - icon: "", + icon: "settings", arrow: true, }), on_primary_click: () => { diff --git a/config/ags/modules/sidebar/navigation.ts b/config/ags/modules/sidebar/navigation.ts index 18d97f5d..40639efc 100644 --- a/config/ags/modules/sidebar/navigation.ts +++ b/config/ags/modules/sidebar/navigation.ts @@ -1,8 +1,8 @@ - import { NotificationsBox } from "./notifications.ts" import { SystemBox } from "./system.ts" let shown = Variable("Messages"); import Gtk from "gi://Gtk?version=3.0" +import { MaterialIcon } from "icons.ts"; type ButtonType = { @@ -22,13 +22,9 @@ function Button({ page, label, icon }: ButtonType) { children: [ Widget.Box({ orientation: Gtk.Orientation.VERTICAL, - hpack: "end", + hpack: "center", class_name: "container", - child: Widget.Label({ - hpack: "center", - label: icon, - class_name: "awesome_icon icon" - }), + child: MaterialIcon(icon, "20px"), }), Widget.Label({ label: label, @@ -63,17 +59,17 @@ export function Navigation() { Button({ page: "Messages", label: "Messages", - icon: "" + icon: "chat" }), Button({ page: "Notifications", label: "Notifs", - icon: "" + icon: "notifications" }), Button({ page: "System", label: "System", - icon: "" + icon: "info" }) ] }) diff --git a/config/ags/scripts/apps.py b/config/ags/scripts/apps.py new file mode 100644 index 00000000..9486251c --- /dev/null +++ b/config/ags/scripts/apps.py @@ -0,0 +1,111 @@ +import argparse +import re +import os +import json + +parser = argparse.ArgumentParser() +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument('--browser', type=str, help="Change default browser") +group.add_argument('--filemanager', type=str, help="Change file manager") +group.add_argument('--editor', type=str, help="Change default editor") +group.add_argument('--terminal', type=str, help="Change default terminal") +group.add_argument('--get', type=str, help="Get default app", choices=["browser", "editor", "terminal", "filemanager"]) + +args = parser.parse_args() + +config_file = os.path.expanduser("~/dotfiles/hypr/UserConfigs/Apps.conf") +json_config_file = os.path.expanduser("~/dotfiles/ags/assets/apps.json") + +def read_apps_conf(): + with open(config_file) as f: + return f.read().strip() + +def write_apps_conf(content): + with open(config_file, 'w') as f: + f.write(content + '\n') + +def read_json_config(): + with open(json_config_file) as json_file: + return json.load(json_file) + +def write_json_config(config): + with open(json_config_file, 'w') as json_file: + json.dump(config, json_file, indent=4) + +def which(program: str): + def is_executable(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_executable(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_executable(exe_file): + return exe_file + raise ProgramNotFound(value) + +def extract_associations(env_str): + pattern = re.compile(r'^\s*env\s*=\s*(\w+)\s*,\s*(.*?)\s*#\s*!\s*-\s*@(\w+)\s*$', re.MULTILINE) # noqa + associations = {} + matches = pattern.findall(env_str) + for key, _, association in matches: + if association not in associations: + associations[association] = [] + if key not in associations[association]: + associations[association].append(key) + return associations + +def replace_value(env_str, key, new_value): + pattern = re.compile( + r'^(\s*env\s*=\s*' + + re.escape(key) + + r'\s*,\s*)(.*?)\s*(#.*)?$', re.MULTILINE + ) + return re.sub(pattern, r'\1' + new_value + r' \3', env_str) + +class AssociationNotFound(Exception): + def __init__(self, name: str, *args: object) -> None: + error = f"{name} association not found" + super().__init__(error, *args) + +class ProgramNotFound(Exception): + def __init__(self, name: str, *args: object) -> None: + error = f"{name} not found" + super().__init__(error, *args) + +def change_association(key: str, value: str): + env_str = read_apps_conf() + which(value) + associations = extract_associations(env_str) + if key not in associations: + raise AssociationNotFound(key) + + for key in associations[key]: + env_str = replace_value(env_str, key, value) + + write_apps_conf(env_str) + +def update_json_config(key: str, value: str): + config = read_json_config() + config[key] = value + write_json_config(config) + +if args.get: + json_config = read_json_config() + print(json_config[args.get]) +elif args.browser: + change_association("browser", args.browser) + update_json_config("browser", args.browser) +elif args.filemanager: + change_association("filemanager", args.filemanager) + update_json_config("filemanager", args.filemanager) +elif args.editor: + change_association("editor", args.editor) + update_json_config("editor", args.editor) +elif args.terminal: + change_association("terminal", args.terminal) + update_json_config("terminal", args.terminal) diff --git a/config/ags/style-apps.css b/config/ags/style-apps.css index 42a8da05..aa938910 100644 --- a/config/ags/style-apps.css +++ b/config/ags/style-apps.css @@ -67,7 +67,7 @@ color: @onSecondaryContainer; } -.settings .sidebar .row image { +.settings .sidebar .row .icon { margin-right: 12px; color: @onSurfaceVariant; } @@ -111,6 +111,10 @@ background-color: mix(@onSurface, @surface, 0.92) } +.settings .row:active { + background-color: mix(@onSurface, @surface, 0.8) +} + .settings .row.focused { background-color: mix(@onSurface, @surface, 0.8) } @@ -127,6 +131,11 @@ color: @onSurfaceVariant } +.settings .row .icon { + color: @onSurfaceVariant; + margin-right: 16px; +} + .settings .bottom_bar { background-color: @surface; font-weight: normal; @@ -142,7 +151,7 @@ min-width: 30px; min-height: 32px; border-radius: 4px; - background-color: @surfaceContainerHighest; + background-color: @surfaceContainerHigh; } .settings entry label, .settings combobox label { @@ -152,12 +161,12 @@ /* Wifi */ -.settings .wifi_list .row image { +.settings .wifi_list .row .icon { color: @onSurfaceVariant; margin-right: 16px; } -.settings .wifi_list .row.active image { +.settings .wifi_list .row.active .icon { color: @primary; } diff --git a/config/ags/style.css b/config/ags/style.css index d4a5383c..1912d934 100644 --- a/config/ags/style.css +++ b/config/ags/style.css @@ -271,6 +271,10 @@ window.notification-popups box.notifications { color: @primary; } +.notification-close .icon { + font-weight: 300; +} + /* ----------------------------------------------------- * Applications * ----------------------------------------------------- */ @@ -342,25 +346,6 @@ window.notification-popups box.notifications { * Clipboard history * ----------------------------------------------------- */ -.emoji_box { - border-radius: 24px; - background-color: @background; - border: 2px solid; - border-color: @outlineVariant; - padding: 11px; -} - -.emoji_entry { - background-color: @surfaceContainerHigh; - color: @onSurface; - border: none; - outline: none; - border-radius: 100px; - margin-bottom: 16px; - padding: 5px; - padding-left: 25px; -} - .cliphistory_box { border-radius: 24px; background-color: @background; @@ -416,6 +401,28 @@ window.notification-popups box.notifications { font-weight: normal; } +/* ----------------------------------------------------- + * Emoji + * -----------------------------------------------------*/ + +.emoji_box { + border-radius: 24px; + background-color: @background; + border: 2px solid; + border-color: @outlineVariant; + padding: 11px; +} + +.emoji_entry { + background-color: @surfaceContainerHigh; + color: @onSurface; + border: none; + outline: none; + border-radius: 100px; + margin-bottom: 16px; + padding: 5px; + padding-left: 25px; +} /* ----------------------------------------------------- * Media player @@ -486,10 +493,12 @@ window.notification-popups box.notifications { min-height: 1em; min-width: 1em; padding: .3em; + padding-top: 0px; + padding-bottom: 0px; color: @onSecondary; } -.player button image { +.player button .icon { color: @onSecondary; } @@ -515,7 +524,12 @@ window.notification-popups box.notifications { border-radius: 100px; } -.volume_box image { +.volume_box button { + padding-top: 0px; + padding-bottom: 0px; +} + +.volume_box .icon { margin-right: 5px; } @@ -523,6 +537,10 @@ window.notification-popups box.notifications { font-family: "Font Awesome 6 Free"; } +.material_icon { + font-family: "Material Symbols Outlined", "Material Symbols Sharp"; +} + .battery { background-color: @surfaceContainer; border-radius: 100px; @@ -544,7 +562,7 @@ window.notification-popups box.notifications { margin: 0px; } -.bar_applets image { +.bar_applets .icon { color: @onSurface; } @@ -595,7 +613,12 @@ window.notification-popups box.notifications { } .management_button .icon { - color: @onSurface + color: @onSurface; + margin-right: 5px; +} + +.management_button .arrow { + margin-right: 0px; } .management_button:hover { @@ -606,8 +629,9 @@ window.notification-popups box.notifications { color: @onPrimary } -.management_button.active .icon { - color: @onPrimary +.management_button.active .icon{ + color: @onPrimary; + font-weight: normal; } .management_button.active { @@ -682,7 +706,7 @@ window.notification-popups box.notifications { } .system_scale.backlight { - margin-bottom: 25px; + margin-bottom: 28px; } .system_scale.brightness slider { @@ -799,6 +823,7 @@ window.notification-popups box.notifications { .outline_button label, .outline_button .icon, .outline_button .awesome_icon, .outline_button image { color: @primary; + font-weight: 300; } .outline_button:hover { @@ -815,11 +840,16 @@ window.notification-popups box.notifications { background-color: @secondaryContainer; color: @onSecondaryContainer; border-radius: 100px; + padding: 0px; + padding-left: 6px; + padding-right: 6px; } .filled_tonal_button label, .filled_tonal_button .icon, .filled_tonal_button .awesome_icon, .filled_tonal_button image { color: @onSecondaryContainer; + font-weight: 400; + margin: 0px; } .filled_tonal_button:hover { @@ -940,19 +970,19 @@ filled_button.disabled .awesome_icon, .filled_button.disabled image { .navigation_button .container { border-radius: 16px; - padding: 5px; + padding-left: 5px; + padding-right: 5px; min-height: 15px; min-width: 0px; transition-property: min-width, color, background-color, border-color; } .navigation_button .icon { - font-weight: normal; + font-weight: 400; color: @onSurface; } .navigation_button.active .icon { - font-weight: bold; color: @onSecondaryContainer; } @@ -1048,6 +1078,7 @@ switch:checked:hover slider, switch:checked:active slider { background-color: @primaryContainer; } + /* ----------------------------------------------------- * Cheatsheet * ----------------------------------------------------- */ @@ -1069,8 +1100,9 @@ switch:checked:hover slider, switch:checked:active slider { } .cheatsheet .category separator { - margin-top: 2px; - margin-bottom: 2px; + margin-top: 4px; + margin-bottom: 4px; + color: @outline; } .cheatsheet .category .row { diff --git a/setup b/setup index b55c9d33..e0bbca55 100755 --- a/setup +++ b/setup @@ -112,6 +112,11 @@ pkg=( ttf-firacode-nerd noto-fonts gnu-free-fonts + ttf-material-symbols-variable-git + fontconfig + ttf-meslo-nerd + ttf-google-sans + ttf-roboto #hyprland hyprland