summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-03-31 23:02:52 +1100
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-03-31 23:02:52 +1100
commit8fab7d4b65cd16769d3ef48e44da3f8ed75582ed (patch)
treecd711556a1f894f8ba8420fe6b249ae58733ca06
parentbar: add error for no wireplumber (diff)
downloadcaelestia-shell-8fab7d4b65cd16769d3ef48e44da3f8ed75582ed.tar.gz
caelestia-shell-8fab7d4b65cd16769d3ef48e44da3f8ed75582ed.tar.bz2
caelestia-shell-8fab7d4b65cd16769d3ef48e44da3f8ed75582ed.zip
feat: fake screen rounding
-rw-r--r--app.tsx2
-rw-r--r--scss/common.scss6
-rw-r--r--src/modules/bar.tsx121
-rw-r--r--src/modules/screencorners.tsx24
-rw-r--r--src/widgets/screencorner.tsx49
5 files changed, 150 insertions, 52 deletions
diff --git a/app.tsx b/app.tsx
index 23f4be9..35a522c 100644
--- a/app.tsx
+++ b/app.tsx
@@ -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();
+ });
+ }}
+ />
+);