diff options
Diffstat (limited to 'examples/gtk3/js/popover')
-rw-r--r-- | examples/gtk3/js/popover/Popover.tsx | 90 | ||||
-rw-r--r-- | examples/gtk3/js/popover/Popover2.tsx | 66 | ||||
-rw-r--r-- | examples/gtk3/js/popover/app.tsx | 72 |
3 files changed, 228 insertions, 0 deletions
diff --git a/examples/gtk3/js/popover/Popover.tsx b/examples/gtk3/js/popover/Popover.tsx new file mode 100644 index 0000000..38ea01e --- /dev/null +++ b/examples/gtk3/js/popover/Popover.tsx @@ -0,0 +1,90 @@ +import { Astal, Gdk, Gtk, Widget } from "astal/gtk3" + +const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor + +type PopoverProps = Pick< + Widget.WindowProps, + | "name" + | "namespace" + | "className" + | "visible" + | "child" + | "marginBottom" + | "marginTop" + | "marginLeft" + | "marginRight" + | "halign" + | "valign" +> & { + onClose?(self: Widget.Window): void +} + +/** + * Full screen window widget where you can space the child widget + * using margins and alignment properties. + * + * NOTE: Child widgets will assume they can span across the full window width + * this means that setting `wrap` or `ellipsize` on labels for example will not work + * without explicitly setting its `max_width_chars` property. + * For a workaround see Popover2.tsx + */ +export default function Popover({ + child, + marginBottom, + marginTop, + marginLeft, + marginRight, + halign = Gtk.Align.CENTER, + valign = Gtk.Align.CENTER, + onClose, + ...props +}: PopoverProps) { + return ( + <window + {...props} + css="background-color: transparent" + keymode={Astal.Keymode.EXCLUSIVE} + anchor={TOP | BOTTOM | LEFT | RIGHT} + exclusivity={Astal.Exclusivity.IGNORE} + onNotifyVisible={(self) => { + if (!self.visible) onClose?.(self) + }} + // close when click occurs otside of child + onButtonPressEvent={(self, event) => { + const [, _x, _y] = event.get_coords() + const { x, y, width, height } = self + .get_child()! + .get_allocation() + + const xOut = _x < x || _x > x + width + const yOut = _y < y || _y > y + height + + // clicked outside + if (xOut || yOut) { + self.visible = false + } + }} + // close when hitting Escape + onKeyPressEvent={(self, event: Gdk.Event) => { + if (event.get_keyval()[1] === Gdk.KEY_Escape) { + self.visible = false + } + }} + > + <box + // make sure click event does not bubble up + onButtonPressEvent={() => true} + // child can be positioned with `halign` `valign` and margins + expand + halign={halign} + valign={valign} + marginBottom={marginBottom} + marginTop={marginTop} + marginStart={marginLeft} + marginEnd={marginRight} + > + {child} + </box> + </window> + ) +} diff --git a/examples/gtk3/js/popover/Popover2.tsx b/examples/gtk3/js/popover/Popover2.tsx new file mode 100644 index 0000000..e058079 --- /dev/null +++ b/examples/gtk3/js/popover/Popover2.tsx @@ -0,0 +1,66 @@ +import { Astal, Gdk, Widget } from "astal/gtk3" +import Variable from "astal/variable" + +type Popover2Props = Pick< + Widget.WindowProps, + | "name" + | "namespace" + | "className" + | "visible" + | "child" +> & { + onClose?(self: Widget.Window): void +} + +/** + * Full screen window where the child is positioned to center. + * + * NOTE: Workaround for the label wrap issue by padding the window + * with eventboxes and only anchoring to TOP | BOTTOM. + */ +export default function Popover2({ + child, + onClose, + ...props +}: Popover2Props) { + let win: Widget.Window + + const width = Variable(1000) + const hide = () => (win.visible = false) + + return ( + <window + {...props} + setup={self => win = self} + css="background-color: transparent" + keymode={Astal.Keymode.EXCLUSIVE} + anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.BOTTOM} + exclusivity={Astal.Exclusivity.IGNORE} + onNotifyVisible={(self) => { + // instead of anchoring to all sides we set the width explicitly + // otherwise label wrapping won't work correctly without setting their width + if (self.visible) { + width.set(self.get_current_monitor().workarea.width) + } else { + onClose?.(self) + } + }} + // close when hitting Escape + onKeyPressEvent={(self, event: Gdk.Event) => { + if (event.get_keyval()[1] === Gdk.KEY_Escape) { + self.visible = false + } + }} + > + <box> + <eventbox widthRequest={width(w => w / 2)} expand onClick={hide} /> + <box hexpand={false} vertical> + <eventbox expand onClick={hide} /> + {child} + <eventbox expand onClick={hide} /> + </box> + <eventbox widthRequest={width(w => w / 2)} expand onClick={hide} /> + </box> + </window> + ) +} diff --git a/examples/gtk3/js/popover/app.tsx b/examples/gtk3/js/popover/app.tsx new file mode 100644 index 0000000..5386b66 --- /dev/null +++ b/examples/gtk3/js/popover/app.tsx @@ -0,0 +1,72 @@ +import { App, Astal, Gtk } from "astal/gtk3" +import { Variable } from "astal" +import Popover from "./Popover" +import Popover2 from "./Popover2" +const { TOP, RIGHT, LEFT } = Astal.WindowAnchor + +const lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean quis semper risus." + +App.start({ + instanceName: "popup-example", + css: ` + .popup { + background-color: @theme_bg_color; + box-shadow: 2px 3px 7px 0 rgba(0,0,0,0.4); + border-radius: 12px; + padding: 12px; + } + `, + main() { + const visible1 = Variable(false); + const visible2 = Variable(false); + + const _popover1 = <Popover + className="Popup" + onClose={() => visible1.set(false)} + visible={visible1()} + marginTop={36} + marginRight={60} + valign={Gtk.Align.START} + halign={Gtk.Align.END} + > + <box className="popup" vertical> + {/* maxWidthChars is needed to make wrap work */} + <label label={lorem} wrap maxWidthChars={8} /> + <button onClicked={() => visible1.set(false)}> + Click me to close the popup + </button> + </box> + </Popover> + + + const _popover2 = <Popover2 + className="Popup" + onClose={() => visible2.set(false)} + visible={visible2()} + > + <box className="popup" vertical> + {/* maxWidthChars is needed, wrap will work as intended */} + <label label={lorem} wrap /> + <button onClicked={() => visible2.set(false)}> + Click me to close the popup + </button> + </box> + </Popover2> + + return ( + <window + anchor={TOP | LEFT | RIGHT} + exclusivity={Astal.Exclusivity.EXCLUSIVE} + > + <box halign={Gtk.Align.END}> + <button onClicked={() => visible2.set(true)} halign={Gtk.Align.END}> + Click to open popover2 + </button> + <button onClicked={() => visible1.set(true)} halign={Gtk.Align.END}> + Click to open popover + </button> + </box> + </window> + ) + }, +}) |