diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-03-31 23:02:52 +1100 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-03-31 23:02:52 +1100 |
| commit | 8fab7d4b65cd16769d3ef48e44da3f8ed75582ed (patch) | |
| tree | cd711556a1f894f8ba8420fe6b249ae58733ca06 | |
| parent | bar: add error for no wireplumber (diff) | |
| download | caelestia-shell-8fab7d4b65cd16769d3ef48e44da3f8ed75582ed.tar.gz caelestia-shell-8fab7d4b65cd16769d3ef48e44da3f8ed75582ed.tar.bz2 caelestia-shell-8fab7d4b65cd16769d3ef48e44da3f8ed75582ed.zip | |
feat: fake screen rounding
| -rw-r--r-- | app.tsx | 2 | ||||
| -rw-r--r-- | scss/common.scss | 6 | ||||
| -rw-r--r-- | src/modules/bar.tsx | 121 | ||||
| -rw-r--r-- | src/modules/screencorners.tsx | 24 | ||||
| -rw-r--r-- | src/widgets/screencorner.tsx | 49 |
5 files changed, 150 insertions, 52 deletions
@@ -2,6 +2,7 @@ import Bar from "@/modules/bar"; import Launcher from "@/modules/launcher"; import NotifPopups from "@/modules/notifpopups"; import Osds from "@/modules/osds"; +import ScreenCorners from "@/modules/screencorners"; import Session from "@/modules/session"; import SideBar from "@/modules/sidebar"; import Calendar from "@/services/calendar"; @@ -75,6 +76,7 @@ App.start({ <Session />; Monitors.get_default().forEach(m => <SideBar monitor={m} />); Monitors.get_default().forEach(m => <Bar monitor={m} />); + Monitors.get_default().forEach(m => <ScreenCorners monitor={m} />); // Init services timeout(1000, () => { diff --git a/scss/common.scss b/scss/common.scss index 1cf7249..de9f424 100644 --- a/scss/common.scss +++ b/scss/common.scss @@ -6,6 +6,12 @@ label.icon { @include font.icon; } +.screen-corner { + @include lib.rounded(15); + + background-color: scheme.$mantle; +} + .notification { .inner { @include lib.rounded(8); diff --git a/src/modules/bar.tsx b/src/modules/bar.tsx index 995d3a3..29f4f88 100644 --- a/src/modules/bar.tsx +++ b/src/modules/bar.tsx @@ -8,6 +8,7 @@ import { bindCurrentTime, osIcon } from "@/utils/system"; import type { AstalWidget } from "@/utils/types"; import { setupCustomTooltip } from "@/utils/widgets"; import type PopupWindow from "@/widgets/popupwindow"; +import ScreenCorner from "@/widgets/screencorner"; import { execAsync, Variable } from "astal"; import Binding, { bind, kebabify } from "astal/binding"; import { App, Astal, Gtk } from "astal/gtk3"; @@ -560,63 +561,79 @@ const bindWidget = (module: keyof typeof config.modules, Widget: () => JSX.Eleme const bindCompositeWidget = (module: keyof typeof config.modules, binding: Binding<JSX.Element>) => bind(Variable.derive([config.modules[module].enabled, binding], (e, w) => (e ? w : <Dummy />))); -export default ({ monitor }: { monitor: Monitor }) => { +const Bar = ({ monitor }: { monitor: Monitor }) => { const className = Variable.derive( [bind(config.vertical), bind(config.style)], (v, s) => `bar ${v ? "vertical" : " horizontal"} ${s}` ); return ( - <window - namespace="caelestia-bar" - monitor={monitor.id} - anchor={bind(config.vertical).as( - v => - Astal.WindowAnchor.TOP | - Astal.WindowAnchor.LEFT | - (v ? Astal.WindowAnchor.BOTTOM : Astal.WindowAnchor.RIGHT) - )} - exclusivity={Astal.Exclusivity.EXCLUSIVE} - > - <centerbox vertical={bind(config.vertical)} className={bind(className)} onDestroy={() => className.drop()}> - <box vertical={bind(config.vertical)}> - {bindWidget("osIcon", OSIcon)} - {bindWidget("activeWindow", ActiveWindow)} - {bindWidget("mediaPlaying", MediaPlaying)} - <button - expand - onScroll={(_, event) => - event.delta_y > 0 ? (monitor.brightness -= 0.1) : (monitor.brightness += 0.1) - } - /> - </box> - {bindWidget("workspaces", Workspaces)} - <box vertical={bind(config.vertical)}> - <button - expand - onScroll={(_, event) => { - const speaker = AstalWp.get_default()?.audio.defaultSpeaker; - if (!speaker) return console.error("Unable to connect to WirePlumber."); - speaker.mute = false; - if (event.delta_y > 0) speaker.volume -= 0.1; - else speaker.volume += 0.1; - }} - /> - {bindWidget("tray", Tray)} - {bindWidget("statusIcons", StatusIcons)} - {bindWidget("pkgUpdates", PkgUpdates)} - {bindWidget("notifCount", NotifCount)} - {bindCompositeWidget( - "battery", - bind(AstalBattery.get_default(), "isBattery").as(b => (b ? <Battery /> : <Dummy />)) - )} - {bindCompositeWidget( - "dateTime", - bind(config.vertical).as(v => (v ? <DateTimeVertical /> : <DateTime />)) - )} - {bindWidget("power", Power)} - </box> - </centerbox> - </window> + <centerbox vertical={bind(config.vertical)} className={bind(className)} onDestroy={() => className.drop()}> + <box vertical={bind(config.vertical)}> + {bindWidget("osIcon", OSIcon)} + {bindWidget("activeWindow", ActiveWindow)} + {bindWidget("mediaPlaying", MediaPlaying)} + <button + expand + onScroll={(_, event) => + event.delta_y > 0 ? (monitor.brightness -= 0.1) : (monitor.brightness += 0.1) + } + /> + </box> + {bindWidget("workspaces", Workspaces)} + <box vertical={bind(config.vertical)}> + <button + expand + onScroll={(_, event) => { + const speaker = AstalWp.get_default()?.audio.defaultSpeaker; + if (!speaker) return console.error("Unable to connect to WirePlumber."); + speaker.mute = false; + if (event.delta_y > 0) speaker.volume -= 0.1; + else speaker.volume += 0.1; + }} + /> + {bindWidget("tray", Tray)} + {bindWidget("statusIcons", StatusIcons)} + {bindWidget("pkgUpdates", PkgUpdates)} + {bindWidget("notifCount", NotifCount)} + {bindCompositeWidget( + "battery", + bind(AstalBattery.get_default(), "isBattery").as(b => (b ? <Battery /> : <Dummy />)) + )} + {bindCompositeWidget( + "dateTime", + bind(config.vertical).as(v => (v ? <DateTimeVertical /> : <DateTime />)) + )} + {bindWidget("power", Power)} + </box> + </centerbox> ); }; + +export default ({ monitor }: { monitor: Monitor }) => ( + <window + namespace="caelestia-bar" + monitor={monitor.id} + anchor={bind(config.vertical).as( + v => + Astal.WindowAnchor.TOP | + Astal.WindowAnchor.LEFT | + (v ? Astal.WindowAnchor.BOTTOM : Astal.WindowAnchor.RIGHT) + )} + exclusivity={Astal.Exclusivity.EXCLUSIVE} + > + <overlay + passThrough + overlays={[ + <ScreenCorner place="topleft" />, + <ScreenCorner + halign={bind(config.vertical).as(v => (v ? undefined : Gtk.Align.END))} + valign={bind(config.vertical).as(v => (v ? Gtk.Align.END : undefined))} + place={bind(config.vertical).as(v => (v ? "bottomleft" : "topright"))} + />, + ]} + > + <Bar monitor={monitor} /> + </overlay> + </window> +); diff --git a/src/modules/screencorners.tsx b/src/modules/screencorners.tsx new file mode 100644 index 0000000..63e7221 --- /dev/null +++ b/src/modules/screencorners.tsx @@ -0,0 +1,24 @@ +import type { Monitor } from "@/services/monitors"; +import ScreenCorner from "@/widgets/screencorner"; +import { bind } from "astal/binding"; +import { Astal } from "astal/gtk3"; +import { bar } from "config"; + +export default ({ monitor }: { monitor: Monitor }) => ( + <window + namespace="caelestia-screencorners" + monitor={monitor.id} + anchor={bind(bar.vertical).as( + v => + Astal.WindowAnchor.BOTTOM | + Astal.WindowAnchor.RIGHT | + (v ? Astal.WindowAnchor.TOP : Astal.WindowAnchor.LEFT) + )} + > + <box vertical={bind(bar.vertical)}> + <ScreenCorner place={bind(bar.vertical).as(v => (v ? "topright" : "bottomleft"))} /> + <box expand /> + <ScreenCorner place="bottomright" /> + </box> + </window> +); diff --git a/src/widgets/screencorner.tsx b/src/widgets/screencorner.tsx new file mode 100644 index 0000000..a55d782 --- /dev/null +++ b/src/widgets/screencorner.tsx @@ -0,0 +1,49 @@ +import type { Binding } from "astal"; +import { Gtk, type Widget } from "astal/gtk3"; +import type cairo from "cairo"; + +type Place = "topleft" | "topright" | "bottomleft" | "bottomright"; + +export default ({ place, ...rest }: Widget.DrawingAreaProps & { place: Place | Binding<Place> }) => ( + <drawingarea + {...rest} + className="screen-corner" + setup={self => { + self.connect("realize", () => self.get_window()?.set_pass_through(true)); + + const r = self.get_style_context().get_property("border-radius", Gtk.StateFlags.NORMAL) as number; + self.set_size_request(r, r); + self.connect("draw", (_, cr: cairo.Context) => { + const c = self.get_style_context().get_background_color(Gtk.StateFlags.NORMAL); + const r = self.get_style_context().get_property("border-radius", Gtk.StateFlags.NORMAL) as number; + self.set_size_request(r, r); + + switch (typeof place === "string" ? place : place.get()) { + case "topleft": + cr.arc(r, r, r, Math.PI, (3 * Math.PI) / 2); + cr.lineTo(0, 0); + break; + + case "topright": + cr.arc(0, r, r, (3 * Math.PI) / 2, 2 * Math.PI); + cr.lineTo(r, 0); + break; + + case "bottomleft": + cr.arc(r, 0, r, Math.PI / 2, Math.PI); + cr.lineTo(0, r); + break; + + case "bottomright": + cr.arc(0, 0, r, 0, Math.PI / 2); + cr.lineTo(r, r); + break; + } + + cr.closePath(); + cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha); + cr.fill(); + }); + }} + /> +); |