diff --git a/README.md b/README.md index 6f4b78c..8aac104 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ ags ``` Or you can add it to your Hyprland config (hyprland.conf) to auto-start with: + +### Notifications + +HyprPanel handles notifications through the AGS built-in notification service. If you're already using a notification daemon such as Dunst or Mako, you may have to stop them to prevent conflicts with HyprPanel. ```bash exec-once = ags ``` diff --git a/modules/bar/workspaces/index.ts b/modules/bar/workspaces/index.ts index d817803..fb0987f 100644 --- a/modules/bar/workspaces/index.ts +++ b/modules/bar/workspaces/index.ts @@ -2,7 +2,7 @@ const hyprland = await Service.import("hyprland"); import { WorkspaceRule, WorkspaceMap } from "lib/types/workspace"; import options from "options"; -const { workspaces, monitorSpecific } = options.bar.workspaces; +const { workspaces, monitorSpecific, reverse_scroll } = options.bar.workspaces; function range(length: number, start = 1) { return Array.from({ length }, (_, i) => i + start); @@ -42,6 +42,43 @@ const Workspaces = (monitor = -1, ws = 8) => { } }; + const getCurrentMonitorWorkspaces = () => { + const monitorWorkspaces = getWorkspaceRules(); + const monitorMap = {}; + hyprland.monitors.forEach((m) => (monitorMap[m.id] = m.name)); + + const currentMonitorName = monitorMap[monitor]; + + return monitorWorkspaces[currentMonitorName]; + } + const currentMonitorWorkspaces = Variable(getCurrentMonitorWorkspaces()); + + workspaces.connect("changed", () => { + currentMonitorWorkspaces.value = getCurrentMonitorWorkspaces() + }) + + const goToNextWS = () => { + const curWorkspace = hyprland.active.workspace.id; + const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace); + let nextIndex = indexOfWs + 1; + if (nextIndex >= currentMonitorWorkspaces.value.length) { + nextIndex = 0; + } + + hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[nextIndex]}`) + } + + const goToPrevWS = () => { + const curWorkspace = hyprland.active.workspace.id; + const indexOfWs = currentMonitorWorkspaces.value.indexOf(curWorkspace); + let prevIndex = indexOfWs - 1; + if (prevIndex < 0) { + prevIndex = currentMonitorWorkspaces.value.length - 1; + } + + hyprland.messageAsync(`dispatch workspace ${currentMonitorWorkspaces.value[prevIndex]}`) + } + return { component: Widget.Box({ class_name: "workspaces", @@ -57,61 +94,67 @@ const Workspaces = (monitor = -1, ws = 8) => { return getWorkspacesForMonitor(i, workspaceRules); }) .map((i) => { - return Widget.Label({ - attribute: i, - vpack: "center", - class_name: Utils.merge( - [ - options.bar.workspaces.show_icons.bind("value"), - options.bar.workspaces.icons.available.bind("value"), - options.bar.workspaces.icons.active.bind("value"), - options.bar.workspaces.icons.occupied.bind("value"), - hyprland.active.workspace.bind("id") - ], - (show_icons) => { - if (show_icons) { - return `workspace-icon`; - } - return ""; - }, - ), - label: Utils.merge( - [ - options.bar.workspaces.show_icons.bind("value"), - options.bar.workspaces.icons.available.bind("value"), - options.bar.workspaces.icons.active.bind("value"), - options.bar.workspaces.icons.occupied.bind("value"), - hyprland.active.workspace.bind("id") - ], - (showIcons, available, active, occupied, _) => { - if (showIcons) { - if (hyprland.active.workspace.id === i) { - return active; - } - if ((hyprland.getWorkspace(i)?.windows || 0) > 0) { - return occupied; - } - if ( - monitor !== -1 - ) { - return available; - } - } - return `${i}`; - }, - ), - setup: (self) => { - self.hook(hyprland, () => { - self.toggleClassName( - "active", - hyprland.active.workspace.id === i, - ); - self.toggleClassName( - "occupied", - (hyprland.getWorkspace(i)?.windows || 0) > 0, - ); - }); + return Widget.Button({ + on_primary_click: () => { + hyprland.messageAsync(`dispatch workspace ${i}`) + }, + child: Widget.Label({ + attribute: i, + vpack: "center", + class_name: Utils.merge( + [ + options.bar.workspaces.show_icons.bind("value"), + options.bar.workspaces.icons.available.bind("value"), + options.bar.workspaces.icons.active.bind("value"), + options.bar.workspaces.icons.occupied.bind("value"), + hyprland.active.workspace.bind("id") + ], + (show_icons) => { + if (show_icons) { + return `workspace-icon`; + } + return ""; + }, + ), + label: Utils.merge( + [ + options.bar.workspaces.show_icons.bind("value"), + options.bar.workspaces.icons.available.bind("value"), + options.bar.workspaces.icons.active.bind("value"), + options.bar.workspaces.icons.occupied.bind("value"), + hyprland.active.workspace.bind("id") + ], + (showIcons, available, active, occupied, _) => { + if (showIcons) { + if (hyprland.active.workspace.id === i) { + return active; + } + if ((hyprland.getWorkspace(i)?.windows || 0) > 0) { + return occupied; + } + if ( + monitor !== -1 + ) { + return available; + } + } + return `${i}`; + }, + ), + setup: (self) => { + self.hook(hyprland, () => { + self.toggleClassName( + "active", + hyprland.active.workspace.id === i, + ); + self.toggleClassName( + "occupied", + (hyprland.getWorkspace(i)?.windows || 0) > 0, + ); + }); + }, + }) }); }); }, @@ -130,6 +173,24 @@ const Workspaces = (monitor = -1, ws = 8) => { }), isVisible: true, boxClass: "workspaces", + useBox: true, + props: { + + on_scroll_up: () => { + if (reverse_scroll.value === true) { + goToPrevWS(); + } else { + goToNextWS(); + } + }, + on_scroll_down: () => { + if (reverse_scroll.value === true) { + goToNextWS(); + } else { + goToPrevWS(); + } + }, + } }; }; export { Workspaces }; diff --git a/modules/shared/barItemBox.ts b/modules/shared/barItemBox.ts index caebb74..a5a2ff5 100644 --- a/modules/shared/barItemBox.ts +++ b/modules/shared/barItemBox.ts @@ -1,15 +1,27 @@ export const BarItemBox = (child) => { - const computeVisible = () => { - if (Object.hasOwnProperty.call(child, "isVis")) { - return child.isVis.bind("value"); - } - return child.isVisible; - }; + const computeVisible = () => { + if (Object.hasOwnProperty.call(child, "isVis")) { + return child.isVis.bind("value"); + } + return child.isVisible; + }; - return Widget.Button({ - class_name: `bar_item_box_visible ${Object.hasOwnProperty.call(child, "boxClass") ? child.boxClass : ""}`, - child: child.component, - visible: computeVisible(), - ...child.props, - }); + if (Object.hasOwnProperty.call(child, "useBox") && child.useBox === true) { + return Widget.Box({ + class_name: `bar_item_box_visible ${Object.hasOwnProperty.call(child, "boxClass") ? child.boxClass : ""}`, + child: Widget.EventBox({ + class_name: `bar-event-box`, + child: child.component, + ...child.props, + }), + visible: computeVisible(), + }); + } + + return Widget.Button({ + class_name: `bar_item_box_visible ${Object.hasOwnProperty.call(child, "boxClass") ? child.boxClass : ""}`, + child: child.component, + visible: computeVisible(), + ...child.props, + }); }; diff --git a/options.ts b/options.ts index b081b11..92f4714 100644 --- a/options.ts +++ b/options.ts @@ -578,6 +578,7 @@ const options = mkOptions(OPTIONS, { }, workspaces: opt(10), monitorSpecific: opt(true), + reverse_scroll: opt(false), }, volume: { label: opt(true), diff --git a/scss/style/bar/bar.scss b/scss/style/bar/bar.scss index 324f4e1..191dabc 100644 --- a/scss/style/bar/bar.scss +++ b/scss/style/bar/bar.scss @@ -3,6 +3,7 @@ .bar { background: if($bar-transparent, transparent, $bar-background); + .transparent { background: transparent; } @@ -20,66 +21,87 @@ &.battery { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-battery-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-battery-hover); } } + &.bluetooth { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-bluetooth-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-bluetooth-hover); } } + &.clock { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-clock-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-clock-hover); } } + &.media { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-media-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-media-hover); } } + &.dashboard { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-dashboard-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-dashboard-hover); } } + &.network { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-network-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-network-hover); } } + &.notifications { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-notifications-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-notifications-hover); } } + &.systray { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-systray-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-systray-hover); } } + &.volume { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-volume-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-volume-hover); } } + &.windowtitle { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-windowtitle-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-windowtitle-hover); } } + &.workspaces { background-color: if($bar-buttons-monochrome, $bar-buttons-background, $bar-buttons-workspaces-background); + &:hover { background: if($bar-buttons-monochrome, $bar-buttons-hover, $bar-buttons-workspaces-hover); } diff --git a/scss/style/bar/workspace.scss b/scss/style/bar/workspace.scss index 08a1eda..9d46cf5 100644 --- a/scss/style/bar/workspace.scss +++ b/scss/style/bar/workspace.scss @@ -2,20 +2,23 @@ @import '../../variables'; .workspaces { + button { + margin: 0rem 0.5rem * .5; + + &:not(:first-child) { + margin-left: 0.7em; + } + } + label { font-size: 0.2em; min-width: 4em; min-height: 4em; border-radius: 1.9rem * .6; - margin: 0rem 0.5rem * .5; transition: 300ms * .5; background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available); color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-available); - &:not(:first-child) { - margin-left: 4.5em; - } - &.occupied { background-color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied); color: if($bar-buttons-monochrome, $bar-buttons-icon, $bar-buttons-workspaces-occupied); @@ -35,8 +38,6 @@ min-width: 0em; min-height: 0em; border-radius: 0em; - margin: 0rem 1rem * .5; - transition: 300ms * .5; font-size: 1.1em; } } diff --git a/widget/settings/pages/config/bar/index.ts b/widget/settings/pages/config/bar/index.ts index f8ea914..907927a 100644 --- a/widget/settings/pages/config/bar/index.ts +++ b/widget/settings/pages/config/bar/index.ts @@ -24,6 +24,7 @@ export const BarSettings = () => { Option({ opt: options.bar.workspaces.icons.occupied, title: 'Workspace Occupied', type: 'string' }), Option({ opt: options.bar.workspaces.workspaces, title: 'Total Workspaces', type: 'number' }), Option({ opt: options.bar.workspaces.monitorSpecific, title: 'Monitor Specific', subtitle: 'Only workspaces applicable to the monitor will be displayed', type: 'boolean' }), + Option({ opt: options.bar.workspaces.reverse_scroll, title: 'Invert Scroll', subtitle: 'Scrolling up will go to the previous workspace rather than the next.', type: 'boolean' }), Header('Volume'), Option({ opt: options.bar.volume.label, title: 'Show Volume Percentage', type: 'boolean' }), diff --git a/widget/settings/pages/theme/bar/index.ts b/widget/settings/pages/theme/bar/index.ts index 1ca70af..e47745c 100644 --- a/widget/settings/pages/theme/bar/index.ts +++ b/widget/settings/pages/theme/bar/index.ts @@ -29,7 +29,6 @@ export const BarTheme = () => { Header('Workspaces'), Option({ opt: options.theme.bar.buttons.workspaces.background, title: 'Background', type: 'color' }), - Option({ opt: options.theme.bar.buttons.workspaces.hover, title: 'Hover', type: 'color' }), Option({ opt: options.theme.bar.buttons.workspaces.available, title: 'Workspace Available Color', type: 'color' }), Option({ opt: options.theme.bar.buttons.workspaces.occupied, title: 'Workspace Occupied Color', type: 'color' }), Option({ opt: options.theme.bar.buttons.workspaces.active, title: 'Workspace Active Color', type: 'color' }),