diff options
Diffstat (limited to '')
| -rw-r--r-- | app.ts | 12 | ||||
| -rw-r--r-- | style.scss | 48 | ||||
| -rw-r--r-- | widget/Bar.tsx | 124 | 
3 files changed, 136 insertions, 48 deletions
| @@ -2,9 +2,13 @@ import { App } from "astal/gtk4"  import style from "./style.scss"  import Bar from "./widget/Bar" +const windows = [ +  Bar, +] +  App.start({ -    css: style, -    main() { -        App.get_monitors().map(Bar) -    }, +  css: style, +  main() { +    windows.forEach(window => App.get_monitors().map(window)) +  },  }) @@ -1,20 +1,42 @@  // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss -$fg-color: #{"@theme_fg_color"}; -$bg-color: #{"@theme_bg_color"}; +// $fg-color: #{"@theme_fg_color"}; +// $bg-color: #{"@theme_bg_color"};  window.Bar { -    background: transparent; -    color: $fg-color; -    font-weight: bold; - -    >centerbox { -        background: $bg-color; -        border-radius: 10px; -        margin: 8px; +  border: none; +  box-shadow: none; +  background-color: #000; +  color: #fff; + +  >box { +    padding: 4px 0; +  } +} + +box.Workspaces { +  padding-left: 3px; + +  >button { +    min-width: 10px; +    min-height: 10px; +    border-radius: 5px; + +    border: none; +    margin: 2px 4px; +    padding: 0; +    background: #ccc; + +    transition: min-width 2s ease-in-out; +    // transition-property: min-width; +    // transition-duration: 2s; +    // transition-timing-function: ease-out; + +    &.active { +      min-width: 25px;      } -    button { -        border-radius: 8px; -        margin: 2px; +    &.focused { +      background: #AD49E1;      } +  }  } diff --git a/widget/Bar.tsx b/widget/Bar.tsx index c2db8c5..02aa3ba 100644 --- a/widget/Bar.tsx +++ b/widget/Bar.tsx @@ -1,36 +1,98 @@  import { App, Astal, Gtk, Gdk } from "astal/gtk4" -import { Variable } from "astal" +import { exec, GLib, Variable } from "astal" + +type NiriWorkspace = { +  id: number, +  idx: number, +  name: string | null, +  output: string, +  is_active: boolean, +  is_focused: boolean, +  active_window_id: number | null, +}; + +function getWorkspaces(): NiriWorkspace[] { +  // NOTE: this works only in non-systemd environment on NixOS +  // TODO: try to use Niri socket if it is documented +  return JSON.parse(exec("niri msg -j workspaces")); +} + +function getWorkspacesByOutput(output: string): NiriWorkspace[] { +  return getWorkspaces().filter(workspace => workspace.output == output).sort((a, b) => a.idx - b.idx); +} + +function focusWorkspace(idx: number) { +  // NOTE: this works only in non-systemd environment on NixOS +  // TODO: try to use Niri socket if it is documented +  exec(`niri msg action focus-workspace ${idx}`); +} + +type WorkspaceButtonArguments = { +  idx: number, +  isActive: boolean, +  isFocused: boolean, +}; + +function WorkspaceButton(args: WorkspaceButtonArguments) { +  const classes: string[] = []; +  args.isActive && classes.push("active"); +  args.isFocused && classes.push("focused"); +  return <button cssClasses={classes} onClicked={() => focusWorkspace(args.idx)} /> +} + +type WorkspacesArguments = { +  connector: string, +}; + +function Workspaces(args: WorkspacesArguments) { +  // NOTE: it is pretty inefficient and not so much responsive +  // TODO: it would be better to use Niri socket in the future +  const workspaces: Variable<NiriWorkspace[]> = Variable(getWorkspacesByOutput(args.connector)) +    .poll(1000, () => getWorkspacesByOutput(args.connector)); + +  // BUG: on workspace change there's no CSS transition +  // I guess it is due to rerender here +  return <box cssClasses={["Workspaces"]}> +    {workspaces(v => v.map(workspace => <WorkspaceButton idx={workspace.idx} isActive={workspace.is_active} isFocused={workspace.is_focused} />))} +  </box> +} + + +type TimeArguments = { +  format: string, +}; + +function Time(args: TimeArguments) { +  const time = Variable<GLib.DateTime>(GLib.DateTime.new_now_local()).poll(1000, () => GLib.DateTime.new_now_local()) + +  return <box> +    {time(v => v.format(args.format))} +  </box> +} -const time = Variable("").poll(1000, "date")  export default function Bar(gdkmonitor: Gdk.Monitor) { -    const { TOP, LEFT, RIGHT } = Astal.WindowAnchor - -    return <window -        visible -        cssClasses={["Bar"]} -        gdkmonitor={gdkmonitor} -        exclusivity={Astal.Exclusivity.EXCLUSIVE} -        anchor={TOP | LEFT | RIGHT} -        application={App}> -        <centerbox cssName="centerbox"> -            <button -                onClicked="echo hello" -                hexpand -                halign={Gtk.Align.CENTER} -            > -                Welcome to AGS! -            </button> -            <box /> -            <menubutton -                hexpand -                halign={Gtk.Align.CENTER} -            > -                <label label={time()} /> -                <popover> -                    <Gtk.Calendar /> -                </popover> -            </menubutton> -        </centerbox> -    </window> +  const { TOP, LEFT, RIGHT } = Astal.WindowAnchor + +  return <window +    visible +    name={"Bar"} +    cssClasses={["Bar"]} +    gdkmonitor={gdkmonitor} +    exclusivity={Astal.Exclusivity.EXCLUSIVE} +    anchor={TOP | LEFT | RIGHT} +    application={App}> +    <centerbox cssClasses={["bar-container"]}> +      <box halign={Gtk.Align.START}> +        <Workspaces connector={gdkmonitor.get_connector()!} /> +      </box> + +      <box halign={Gtk.Align.CENTER}> +      </box> + +      <box halign={Gtk.Align.END}> +        <Time format="%I:%M:%S %p %Z" /> +      </box> +    </centerbox> +  </window>  } | 
