react-native-gtkx 0.1.0-alpha.4 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -9
- package/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.js +41 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/widgets.generated.d.ts +63 -0
- package/dist/adw/widgets.generated.js +125 -0
- package/dist/adw/widgets.generated.js.map +1 -0
- package/dist/animated/create-animated.d.ts +2 -0
- package/dist/animated/create-animated.js +2 -0
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/index.d.ts +2 -0
- package/dist/animated/index.js +1 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/value-xy.d.ts +41 -0
- package/dist/animated/value-xy.js +90 -0
- package/dist/animated/value-xy.js.map +1 -0
- package/dist/animated/value.d.ts +9 -0
- package/dist/animated/value.js +30 -2
- package/dist/animated/value.js.map +1 -1
- package/dist/common/content.d.ts +69 -0
- package/dist/common/content.js +84 -0
- package/dist/common/content.js.map +1 -0
- package/dist/common/icon.d.ts +23 -0
- package/dist/common/icon.js +14 -0
- package/dist/common/icon.js.map +1 -0
- package/dist/common/index.d.ts +5 -0
- package/dist/common/index.js +21 -0
- package/dist/common/index.js.map +1 -0
- package/dist/common/list.d.ts +82 -0
- package/dist/common/list.js +166 -0
- package/dist/common/list.js.map +1 -0
- package/dist/common/navigation-stack.d.ts +84 -0
- package/dist/common/navigation-stack.js +313 -0
- package/dist/common/navigation-stack.js.map +1 -0
- package/dist/common/widget.d.ts +62 -0
- package/dist/common/widget.js +209 -0
- package/dist/common/widget.js.map +1 -0
- package/dist/components/animated.d.ts +7 -4
- package/dist/components/animated.js +88 -67
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.d.ts +36 -1
- package/dist/components/app-registry.js +8 -3
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/host-node.d.ts +7 -0
- package/dist/components/host-node.js +26 -1
- package/dist/components/host-node.js.map +1 -1
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/measure.d.ts +13 -0
- package/dist/components/measure.js +51 -0
- package/dist/components/measure.js.map +1 -0
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/press-event.d.ts +28 -0
- package/dist/components/press-event.js +46 -0
- package/dist/components/press-event.js.map +1 -0
- package/dist/components/pressable.d.ts +2 -6
- package/dist/components/pressable.js +119 -20
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/rect-store.d.ts +11 -1
- package/dist/components/rect-store.js +27 -3
- package/dist/components/rect-store.js.map +1 -1
- package/dist/components/scroll-view.d.ts +4 -0
- package/dist/components/scroll-view.js +8 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/svg/animated-support.d.ts +19 -0
- package/dist/components/svg/animated-support.js +64 -0
- package/dist/components/svg/animated-support.js.map +1 -0
- package/dist/components/svg/context.d.ts +8 -0
- package/dist/components/svg/context.js +15 -0
- package/dist/components/svg/context.js.map +1 -0
- package/dist/components/svg/gradients.d.ts +31 -0
- package/dist/components/svg/gradients.js +93 -0
- package/dist/components/svg/gradients.js.map +1 -0
- package/dist/components/svg/group.d.ts +8 -0
- package/dist/components/svg/group.js +30 -0
- package/dist/components/svg/group.js.map +1 -0
- package/dist/components/svg/index.d.ts +6 -0
- package/dist/components/svg/index.js +5 -0
- package/dist/components/svg/index.js.map +1 -0
- package/dist/components/svg/node.d.ts +7 -0
- package/dist/components/svg/node.js +7 -0
- package/dist/components/svg/node.js.map +1 -0
- package/dist/components/svg/paint.d.ts +24 -0
- package/dist/components/svg/paint.js +57 -0
- package/dist/components/svg/paint.js.map +1 -0
- package/dist/components/svg/shapes.d.ts +47 -0
- package/dist/components/svg/shapes.js +51 -0
- package/dist/components/svg/shapes.js.map +1 -0
- package/dist/components/svg/svg.d.ts +14 -0
- package/dist/components/svg/svg.js +48 -0
- package/dist/components/svg/svg.js.map +1 -0
- package/dist/components/svg/use-shape-node.d.ts +12 -0
- package/dist/components/svg/use-shape-node.js +52 -0
- package/dist/components/svg/use-shape-node.js.map +1 -0
- package/dist/components/text-input.js +1 -1
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.js +36 -3
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-layout-child.js +50 -4
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/view.d.ts +9 -4
- package/dist/components/view.js +8 -2
- package/dist/components/view.js.map +1 -1
- package/dist/contracts.d.ts +31 -4
- package/dist/contracts.js.map +1 -1
- package/dist/gtk/index.d.ts +7 -0
- package/dist/gtk/index.js +46 -0
- package/dist/gtk/index.js.map +1 -0
- package/dist/gtk/widgets.generated.d.ts +103 -0
- package/dist/gtk/widgets.generated.js +205 -0
- package/dist/gtk/widgets.generated.js.map +1 -0
- package/dist/gtk/window-actions.d.ts +77 -0
- package/dist/gtk/window-actions.js +193 -0
- package/dist/gtk/window-actions.js.map +1 -0
- package/dist/gtkx/bridge/geometry.d.ts +15 -0
- package/dist/gtkx/bridge/geometry.js +38 -0
- package/dist/gtkx/bridge/geometry.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +11 -4
- package/dist/gtkx/bridge/index.js +47 -6
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/layout-manager.d.ts +2 -1
- package/dist/gtkx/bridge/layout-manager.js +31 -9
- package/dist/gtkx/bridge/layout-manager.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
- package/dist/gtkx/bridge/slot-portal.js +17 -0
- package/dist/gtkx/bridge/slot-portal.js.map +1 -0
- package/dist/gtkx/bridge/svg-node.d.ts +79 -0
- package/dist/gtkx/bridge/svg-node.js +323 -0
- package/dist/gtkx/bridge/svg-node.js.map +1 -0
- package/dist/gtkx/bridge/use-signal.js +19 -9
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.d.ts +2 -0
- package/dist/gtkx/bridge/widgets.generated.js +14 -0
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/bin.d.ts +2 -0
- package/dist/mcp/bin.js +17 -0
- package/dist/mcp/bin.js.map +1 -0
- package/dist/mcp/data/generated.d.ts +1304 -0
- package/dist/mcp/data/generated.js +1179 -0
- package/dist/mcp/data/generated.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/list.d.ts +4 -0
- package/dist/mcp/list.js +56 -0
- package/dist/mcp/list.js.map +1 -0
- package/dist/mcp/resolve.d.ts +55 -0
- package/dist/mcp/resolve.js +151 -0
- package/dist/mcp/resolve.js.map +1 -0
- package/dist/mcp/search.d.ts +13 -0
- package/dist/mcp/search.js +48 -0
- package/dist/mcp/search.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +135 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool.d.ts +21 -0
- package/dist/mcp/tool.js +36 -0
- package/dist/mcp/tool.js.map +1 -0
- package/dist/metro/index.js +10 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/index.d.ts +50 -13
- package/dist/navigation/index.js +183 -148
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/option-warnings.js +9 -1
- package/dist/navigation/option-warnings.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +207 -9
- package/dist/navigation/sidebar.js +294 -10
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/responder/system.d.ts +27 -0
- package/dist/responder/system.js +184 -0
- package/dist/responder/system.js.map +1 -0
- package/dist/responder/touch-history.d.ts +10 -0
- package/dist/responder/touch-history.js +78 -0
- package/dist/responder/touch-history.js.map +1 -0
- package/dist/responder/types.d.ts +63 -0
- package/dist/responder/types.js +29 -0
- package/dist/responder/types.js.map +1 -0
- package/dist/responder/use-responder.d.ts +12 -0
- package/dist/responder/use-responder.js +98 -0
- package/dist/responder/use-responder.js.map +1 -0
- package/dist/runner/host-dev.js +3 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +5 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.d.ts +22 -2
- package/dist/runner/index.js +163 -13
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/assemble.d.ts +29 -0
- package/dist/sea/assemble.js +129 -0
- package/dist/sea/assemble.js.map +1 -0
- package/dist/sea/bundle.d.ts +61 -0
- package/dist/sea/bundle.js +202 -0
- package/dist/sea/bundle.js.map +1 -0
- package/dist/sea/gtkx-config-module.d.ts +18 -0
- package/dist/sea/gtkx-config-module.js +67 -0
- package/dist/sea/gtkx-config-module.js.map +1 -0
- package/dist/sea/native-shim.d.ts +40 -0
- package/dist/sea/native-shim.js +121 -0
- package/dist/sea/native-shim.js.map +1 -0
- package/dist/sea/react-anchor-plugin.d.ts +2 -0
- package/dist/sea/react-anchor-plugin.js +30 -0
- package/dist/sea/react-anchor-plugin.js.map +1 -0
- package/dist/style/box-shadow.d.ts +7 -0
- package/dist/style/box-shadow.js +197 -0
- package/dist/style/box-shadow.js.map +1 -0
- package/dist/style/index.d.ts +3 -0
- package/dist/style/index.js +3 -0
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.d.ts +5 -2
- package/dist/style/split-style.js +33 -5
- package/dist/style/split-style.js.map +1 -1
- package/dist/style/text-decoration.d.ts +15 -0
- package/dist/style/text-decoration.js +26 -0
- package/dist/style/text-decoration.js.map +1 -0
- package/dist/style/transform.d.ts +22 -0
- package/dist/style/transform.js +137 -0
- package/dist/style/transform.js.map +1 -0
- package/dist/style/visual-css.js +40 -3
- package/dist/style/visual-css.js.map +1 -1
- package/dist/svg/geometry.d.ts +19 -0
- package/dist/svg/geometry.js +80 -0
- package/dist/svg/geometry.js.map +1 -0
- package/dist/svg/gradient-geometry.d.ts +15 -0
- package/dist/svg/gradient-geometry.js +15 -0
- package/dist/svg/gradient-geometry.js.map +1 -0
- package/dist/svg/transform.d.ts +22 -0
- package/dist/svg/transform.js +74 -0
- package/dist/svg/transform.js.map +1 -0
- package/dist/svg/view-box.d.ts +34 -0
- package/dist/svg/view-box.js +108 -0
- package/dist/svg/view-box.js.map +1 -0
- package/dist/svg-compat/index.d.ts +1 -0
- package/dist/svg-compat/index.js +16 -0
- package/dist/svg-compat/index.js.map +1 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/vendor/react-native/pan-responder.d.ts +102 -0
- package/dist/vendor/react-native/pan-responder.js +196 -0
- package/dist/vendor/react-native/pan-responder.js.map +1 -0
- package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
- package/dist/vendor/react-native/touch-history-math.js +102 -0
- package/dist/vendor/react-native/touch-history-math.js.map +1 -0
- package/dist/vite/index.d.ts +7 -3
- package/dist/vite/index.js +16 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +52 -0
- package/dist/vitest/index.js +58 -0
- package/dist/vitest/index.js.map +1 -0
- package/dist/vitest/setup.d.ts +4 -0
- package/dist/vitest/setup.js +22 -0
- package/dist/vitest/setup.js.map +1 -0
- package/package.json +52 -13
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Window and application actions declared IN the app tree, not around it.
|
|
3
|
+
//
|
|
4
|
+
// `AppRegistry.runApplication`'s `windowActions`/`windowControllers`/
|
|
5
|
+
// `applicationActions` options build their children as props of the window
|
|
6
|
+
// AppRegistry creates — SIBLINGS of the app's own tree. Nothing inside the
|
|
7
|
+
// app is above them, so no context of the app's reaches them: a `win.new`
|
|
8
|
+
// action could not read a React context store, and `examples/tasks-nav` had
|
|
9
|
+
// to rewrite its store as a module-level external one before Ctrl+N could
|
|
10
|
+
// see any app state at all.
|
|
11
|
+
//
|
|
12
|
+
// These components invert that. They are portals in React's own sense: the
|
|
13
|
+
// children stay where they are written — inside whatever providers,
|
|
14
|
+
// navigators and screens surround them, with their own state and effects —
|
|
15
|
+
// while the registration lands on the window or the application. Three
|
|
16
|
+
// things follow, and all three are the point:
|
|
17
|
+
//
|
|
18
|
+
// - context works, because the declaration IS a descendant of its provider;
|
|
19
|
+
// - registration is dynamic: an action is added when the component that
|
|
20
|
+
// needs it mounts and removed when it unmounts, so a single screen can
|
|
21
|
+
// own its own actions instead of the process owning all of them forever;
|
|
22
|
+
// - it composes: two unrelated subtrees can each declare their own without
|
|
23
|
+
// meeting in one options object.
|
|
24
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useSyncExternalStore, } from "react";
|
|
25
|
+
import { createSlotPortal, GSimpleAction as GtkxSimpleAction, useApplication, useParentWindow, } from "../gtkx/bridge/index";
|
|
26
|
+
const ActionScopeContext = createContext(null);
|
|
27
|
+
// Claims per action map, in mount order. A WeakMap so a closed window's
|
|
28
|
+
// bookkeeping goes away with the window.
|
|
29
|
+
const claimsByScope = new WeakMap();
|
|
30
|
+
const queueFor = (target, name) => {
|
|
31
|
+
let names = claimsByScope.get(target);
|
|
32
|
+
if (!names) {
|
|
33
|
+
names = new Map();
|
|
34
|
+
claimsByScope.set(target, names);
|
|
35
|
+
}
|
|
36
|
+
let queue = names.get(name);
|
|
37
|
+
if (!queue) {
|
|
38
|
+
queue = [];
|
|
39
|
+
names.set(name, queue);
|
|
40
|
+
}
|
|
41
|
+
return queue;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Arbitrates one action name inside one scope, and answers "may I register?".
|
|
45
|
+
*
|
|
46
|
+
* FIRST DECLARATION WINS, and a second one is ignored with a development
|
|
47
|
+
* warning. That is not a coin toss between first and last: `Gio.ActionMap`
|
|
48
|
+
* is name-keyed on BOTH ends — `addAction` replaces a same-named action
|
|
49
|
+
* silently, and `removeAction` takes a NAME, not the action object. So with
|
|
50
|
+
* "last wins" the first unmount of either declaration removes whatever
|
|
51
|
+
* currently answers to that name, leaving the other one mounted but dead.
|
|
52
|
+
* First-wins is the only order in which release always happens before
|
|
53
|
+
* acquire: the loser never registers at all, and when the winner unmounts
|
|
54
|
+
* (removing its own action, correctly) the next claimant is notified and
|
|
55
|
+
* registers afterwards, in a later commit.
|
|
56
|
+
*
|
|
57
|
+
* The claim queue is an external store, so it is read as one: the claim is
|
|
58
|
+
* made when React subscribes and dropped when it unsubscribes, which is also
|
|
59
|
+
* what puts the hand-off after the departing owner's own deregistration
|
|
60
|
+
* rather than racing it.
|
|
61
|
+
*
|
|
62
|
+
* With no scope (a `GSimpleAction` handed to `<GtkApplicationWindow
|
|
63
|
+
* actions={…}>` directly, the pre-existing path) there is nothing to
|
|
64
|
+
* arbitrate and the action always renders.
|
|
65
|
+
*/
|
|
66
|
+
const useIsNameOwner = (scope, name) => {
|
|
67
|
+
const claimRef = useRef(null);
|
|
68
|
+
const subscribe = useCallback((notify) => {
|
|
69
|
+
if (scope === null) {
|
|
70
|
+
return () => undefined;
|
|
71
|
+
}
|
|
72
|
+
const queue = queueFor(scope.target, name);
|
|
73
|
+
const claim = { notify };
|
|
74
|
+
queue.push(claim);
|
|
75
|
+
claimRef.current = claim;
|
|
76
|
+
if (queue.length > 1 && process.env.NODE_ENV !== "production") {
|
|
77
|
+
console.warn(`[react-native-gtkx] the action "${scope.prefix}.${name}" is declared by ${queue.length} mounted components at once. ` +
|
|
78
|
+
`The first one to mount stays registered and this one is ignored — GTK removes an action by NAME, so letting the ` +
|
|
79
|
+
`newest win would let whichever declaration unmounts first take the name down with it. Give them distinct names, ` +
|
|
80
|
+
`or move the declaration to one place both can reach.`);
|
|
81
|
+
}
|
|
82
|
+
return () => {
|
|
83
|
+
const index = queue.indexOf(claim);
|
|
84
|
+
claimRef.current = null;
|
|
85
|
+
if (index === -1) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const wasOwner = index === 0;
|
|
89
|
+
queue.splice(index, 1);
|
|
90
|
+
if (wasOwner) {
|
|
91
|
+
queue[0]?.notify();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}, [scope, name]);
|
|
95
|
+
const getSnapshot = useCallback(() => {
|
|
96
|
+
if (scope === null) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const claim = claimRef.current;
|
|
100
|
+
return claim !== null && queueFor(scope.target, name)[0] === claim;
|
|
101
|
+
}, [scope, name]);
|
|
102
|
+
return useSyncExternalStore(subscribe, getSnapshot);
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* A `Gio.SimpleAction`, declared as an element.
|
|
106
|
+
*
|
|
107
|
+
* Identical to gtkx's own element except for one thing: inside
|
|
108
|
+
* {@link WindowActions} or {@link ApplicationActions} it takes part in name
|
|
109
|
+
* arbitration, so two subtrees declaring the same action name produce a
|
|
110
|
+
* warning and a predictable winner instead of a name that the first unmount
|
|
111
|
+
* silently kills. Outside them it renders exactly as before.
|
|
112
|
+
*/
|
|
113
|
+
export const GSimpleAction = (props) => {
|
|
114
|
+
const scope = useContext(ActionScopeContext);
|
|
115
|
+
const name = typeof props.name === "string" ? props.name : "";
|
|
116
|
+
const isOwner = useIsNameOwner(scope, name);
|
|
117
|
+
return isOwner ? _jsx(GtkxSimpleAction, { ...props }) : null;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Registers its children as actions on the WINDOW — the `win.` prefix a
|
|
121
|
+
* HeaderBar button's `actionName`, a `GMenu` item or an `actionAccels` entry
|
|
122
|
+
* targets.
|
|
123
|
+
*
|
|
124
|
+
* Render it anywhere in the app tree; the children are registered on the
|
|
125
|
+
* window that encloses them and unregistered when this component unmounts.
|
|
126
|
+
*
|
|
127
|
+
* ```tsx
|
|
128
|
+
* const NewTaskAction = () => {
|
|
129
|
+
* const { addTask } = useStore() // an ordinary React context store
|
|
130
|
+
* return (
|
|
131
|
+
* <WindowActions>
|
|
132
|
+
* <GSimpleAction name="new" onActivate={() => addTask()} />
|
|
133
|
+
* </WindowActions>
|
|
134
|
+
* )
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* Inside a `Modal` the enclosing window is the MODAL's window, so the
|
|
139
|
+
* actions belong to it and go away with it — which is usually what a dialog
|
|
140
|
+
* wants, and worth knowing when it is not.
|
|
141
|
+
*/
|
|
142
|
+
export const WindowActions = ({ children }) => {
|
|
143
|
+
const window = useParentWindow();
|
|
144
|
+
const scope = useMemo(() => (window === null ? null : { target: window, prefix: "win" }), [window]);
|
|
145
|
+
// Null for exactly one commit: the window object reaches context through
|
|
146
|
+
// state, so the app's first render happens before it is known.
|
|
147
|
+
if (window === null || scope === null) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
return (_jsx(ActionScopeContext.Provider, { value: scope, children: createSlotPortal(children, window, "actions") }));
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Registers its children as actions on the APPLICATION — the `app.` prefix.
|
|
154
|
+
*
|
|
155
|
+
* The same component as {@link WindowActions} against a different action
|
|
156
|
+
* map, and the two are not interchangeable: a `Gio.Notification`'s action
|
|
157
|
+
* button can only ever activate an application action, and an application
|
|
158
|
+
* action outlives any one window. Which prefix an `actionName=` has to say
|
|
159
|
+
* is decided by which of the two you write, deliberately — a prop that
|
|
160
|
+
* silently moved every child between prefixes would be a good way to spend
|
|
161
|
+
* an hour wondering why a button is insensitive.
|
|
162
|
+
*/
|
|
163
|
+
export const ApplicationActions = ({ children }) => {
|
|
164
|
+
const application = useApplication();
|
|
165
|
+
const scope = useMemo(() => application === null ? null : { target: application, prefix: "app" }, [application]);
|
|
166
|
+
if (application === null || scope === null) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
return (_jsx(ActionScopeContext.Provider, { value: scope, children: createSlotPortal(children, application, "actions") }));
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Attaches its children as event controllers on the WINDOW: a
|
|
173
|
+
* `GtkShortcutController` with `scope={Gtk.ShortcutScope.GLOBAL}` is the
|
|
174
|
+
* reason this exists — window-wide keyboard shortcuts, declared by the
|
|
175
|
+
* screen they belong to and gone when it leaves.
|
|
176
|
+
*
|
|
177
|
+
* Deliberately a different component from {@link WindowActions} rather than
|
|
178
|
+
* one that sorts its children: controllers land on the window as a WIDGET
|
|
179
|
+
* (`addController`/`removeController`, keyed by the controller object)
|
|
180
|
+
* while actions land on it as a `Gio.ActionMap` (keyed by name). Different
|
|
181
|
+
* children, different GObject interfaces, and — as the name arbitration
|
|
182
|
+
* above shows — different duplicate semantics. One component sniffing each
|
|
183
|
+
* child's type to pick a slot would fail silently on a wrong child; two
|
|
184
|
+
* components fail at the type level instead.
|
|
185
|
+
*/
|
|
186
|
+
export const WindowControllers = ({ children }) => {
|
|
187
|
+
const window = useParentWindow();
|
|
188
|
+
if (window === null) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
return createSlotPortal(children, window, "controllers");
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=window-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window-actions.js","sourceRoot":"","sources":["../../src/gtk/window-actions.tsx"],"names":[],"mappings":"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","sourcesContent":["// Window and application actions declared IN the app tree, not around it.\n//\n// `AppRegistry.runApplication`'s `windowActions`/`windowControllers`/\n// `applicationActions` options build their children as props of the window\n// AppRegistry creates — SIBLINGS of the app's own tree. Nothing inside the\n// app is above them, so no context of the app's reaches them: a `win.new`\n// action could not read a React context store, and `examples/tasks-nav` had\n// to rewrite its store as a module-level external one before Ctrl+N could\n// see any app state at all.\n//\n// These components invert that. They are portals in React's own sense: the\n// children stay where they are written — inside whatever providers,\n// navigators and screens surround them, with their own state and effects —\n// while the registration lands on the window or the application. Three\n// things follow, and all three are the point:\n//\n// - context works, because the declaration IS a descendant of its provider;\n// - registration is dynamic: an action is added when the component that\n// needs it mounts and removed when it unmounts, so a single screen can\n// own its own actions instead of the process owning all of them forever;\n// - it composes: two unrelated subtrees can each declare their own without\n// meeting in one options object.\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n type ComponentProps,\n type ReactNode,\n} from \"react\"\nimport {\n createSlotPortal,\n GSimpleAction as GtkxSimpleAction,\n useApplication,\n useParentWindow,\n} from \"../gtkx/bridge/index\"\n\n/**\n * The action map an enclosing {@link WindowActions} / {@link ApplicationActions}\n * registers into, plus the GTK prefix its children answer to. Carried through\n * React context, so a `GSimpleAction` nested arbitrarily deep — inside a\n * component of the app's own, not just as a direct child — still finds it.\n */\ntype ActionScope = {\n /** The `Gio.ActionMap` (a window or the application). Registry key, too. */\n target: object\n /** \"win\" or \"app\": what an `actionName` has to say to reach these. */\n prefix: string\n}\n\nconst ActionScopeContext = createContext<ActionScope | null>(null)\n\n// One live claim on one action name. `notify` is the claimant's own\n// useSyncExternalStore callback, so the registry can tell the next in line\n// that the name has come free.\ntype NameClaim = { notify: () => void }\n\n// Claims per action map, in mount order. A WeakMap so a closed window's\n// bookkeeping goes away with the window.\nconst claimsByScope: WeakMap<object, Map<string, NameClaim[]>> = new WeakMap()\n\nconst queueFor = (target: object, name: string): NameClaim[] => {\n let names = claimsByScope.get(target)\n if (!names) {\n names = new Map()\n claimsByScope.set(target, names)\n }\n let queue = names.get(name)\n if (!queue) {\n queue = []\n names.set(name, queue)\n }\n return queue\n}\n\n/**\n * Arbitrates one action name inside one scope, and answers \"may I register?\".\n *\n * FIRST DECLARATION WINS, and a second one is ignored with a development\n * warning. That is not a coin toss between first and last: `Gio.ActionMap`\n * is name-keyed on BOTH ends — `addAction` replaces a same-named action\n * silently, and `removeAction` takes a NAME, not the action object. So with\n * \"last wins\" the first unmount of either declaration removes whatever\n * currently answers to that name, leaving the other one mounted but dead.\n * First-wins is the only order in which release always happens before\n * acquire: the loser never registers at all, and when the winner unmounts\n * (removing its own action, correctly) the next claimant is notified and\n * registers afterwards, in a later commit.\n *\n * The claim queue is an external store, so it is read as one: the claim is\n * made when React subscribes and dropped when it unsubscribes, which is also\n * what puts the hand-off after the departing owner's own deregistration\n * rather than racing it.\n *\n * With no scope (a `GSimpleAction` handed to `<GtkApplicationWindow\n * actions={…}>` directly, the pre-existing path) there is nothing to\n * arbitrate and the action always renders.\n */\nconst useIsNameOwner = (scope: ActionScope | null, name: string): boolean => {\n const claimRef = useRef<NameClaim | null>(null)\n\n const subscribe = useCallback(\n (notify: () => void) => {\n if (scope === null) {\n return () => undefined\n }\n const queue = queueFor(scope.target, name)\n const claim: NameClaim = { notify }\n queue.push(claim)\n claimRef.current = claim\n\n if (queue.length > 1 && process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[react-native-gtkx] the action \"${scope.prefix}.${name}\" is declared by ${queue.length} mounted components at once. ` +\n `The first one to mount stays registered and this one is ignored — GTK removes an action by NAME, so letting the ` +\n `newest win would let whichever declaration unmounts first take the name down with it. Give them distinct names, ` +\n `or move the declaration to one place both can reach.`,\n )\n }\n\n return () => {\n const index = queue.indexOf(claim)\n claimRef.current = null\n if (index === -1) {\n return\n }\n const wasOwner = index === 0\n queue.splice(index, 1)\n if (wasOwner) {\n queue[0]?.notify()\n }\n }\n },\n [scope, name],\n )\n\n const getSnapshot = useCallback(() => {\n if (scope === null) {\n return true\n }\n const claim = claimRef.current\n return claim !== null && queueFor(scope.target, name)[0] === claim\n }, [scope, name])\n\n return useSyncExternalStore(subscribe, getSnapshot)\n}\n\nexport type GSimpleActionProps = ComponentProps<typeof GtkxSimpleAction>\n\n/**\n * A `Gio.SimpleAction`, declared as an element.\n *\n * Identical to gtkx's own element except for one thing: inside\n * {@link WindowActions} or {@link ApplicationActions} it takes part in name\n * arbitration, so two subtrees declaring the same action name produce a\n * warning and a predictable winner instead of a name that the first unmount\n * silently kills. Outside them it renders exactly as before.\n */\nexport const GSimpleAction = (props: GSimpleActionProps) => {\n const scope = useContext(ActionScopeContext)\n const name = typeof props.name === \"string\" ? props.name : \"\"\n const isOwner = useIsNameOwner(scope, name)\n return isOwner ? <GtkxSimpleAction {...props} /> : null\n}\n\nexport type WindowActionsProps = {\n /** `GSimpleAction` elements, or any components that render them. */\n children?: ReactNode\n}\n\n/**\n * Registers its children as actions on the WINDOW — the `win.` prefix a\n * HeaderBar button's `actionName`, a `GMenu` item or an `actionAccels` entry\n * targets.\n *\n * Render it anywhere in the app tree; the children are registered on the\n * window that encloses them and unregistered when this component unmounts.\n *\n * ```tsx\n * const NewTaskAction = () => {\n * const { addTask } = useStore() // an ordinary React context store\n * return (\n * <WindowActions>\n * <GSimpleAction name=\"new\" onActivate={() => addTask()} />\n * </WindowActions>\n * )\n * }\n * ```\n *\n * Inside a `Modal` the enclosing window is the MODAL's window, so the\n * actions belong to it and go away with it — which is usually what a dialog\n * wants, and worth knowing when it is not.\n */\nexport const WindowActions = ({ children }: WindowActionsProps) => {\n const window = useParentWindow()\n const scope = useMemo(\n () => (window === null ? null : { target: window, prefix: \"win\" }),\n [window],\n )\n // Null for exactly one commit: the window object reaches context through\n // state, so the app's first render happens before it is known.\n if (window === null || scope === null) {\n return null\n }\n return (\n <ActionScopeContext.Provider value={scope}>\n {createSlotPortal(children, window, \"actions\")}\n </ActionScopeContext.Provider>\n )\n}\n\nexport type ApplicationActionsProps = {\n /** `GSimpleAction` elements, or any components that render them. */\n children?: ReactNode\n}\n\n/**\n * Registers its children as actions on the APPLICATION — the `app.` prefix.\n *\n * The same component as {@link WindowActions} against a different action\n * map, and the two are not interchangeable: a `Gio.Notification`'s action\n * button can only ever activate an application action, and an application\n * action outlives any one window. Which prefix an `actionName=` has to say\n * is decided by which of the two you write, deliberately — a prop that\n * silently moved every child between prefixes would be a good way to spend\n * an hour wondering why a button is insensitive.\n */\nexport const ApplicationActions = ({ children }: ApplicationActionsProps) => {\n const application = useApplication()\n const scope = useMemo(\n () =>\n application === null ? null : { target: application, prefix: \"app\" },\n [application],\n )\n if (application === null || scope === null) {\n return null\n }\n return (\n <ActionScopeContext.Provider value={scope}>\n {createSlotPortal(children, application, \"actions\")}\n </ActionScopeContext.Provider>\n )\n}\n\nexport type WindowControllersProps = {\n /** `Gtk.EventController` elements — `GtkShortcutController` above all. */\n children?: ReactNode\n}\n\n/**\n * Attaches its children as event controllers on the WINDOW: a\n * `GtkShortcutController` with `scope={Gtk.ShortcutScope.GLOBAL}` is the\n * reason this exists — window-wide keyboard shortcuts, declared by the\n * screen they belong to and gone when it leaves.\n *\n * Deliberately a different component from {@link WindowActions} rather than\n * one that sorts its children: controllers land on the window as a WIDGET\n * (`addController`/`removeController`, keyed by the controller object)\n * while actions land on it as a `Gio.ActionMap` (keyed by name). Different\n * children, different GObject interfaces, and — as the name arbitration\n * above shows — different duplicate semantics. One component sniffing each\n * child's type to pick a slot would fail silently on a wrong child; two\n * components fail at the type level instead.\n */\nexport const WindowControllers = ({ children }: WindowControllersProps) => {\n const window = useParentWindow()\n if (window === null) {\n return null\n }\n return createSlotPortal(children, window, \"controllers\")\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
export type Point = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Translates a point from `widget`'s coordinates into `target`'s.
|
|
8
|
+
* Returns null when the two are not in the same widget hierarchy.
|
|
9
|
+
*/
|
|
10
|
+
export declare const computePointIn: (widget: Gtk.Widget, target: Gtk.Widget, x: number, y: number) => Point | null;
|
|
11
|
+
/**
|
|
12
|
+
* Translates a point from `widget`'s coordinates into its toplevel's — RN's
|
|
13
|
+
* "page" space. Returns null for a widget that is not in a window yet.
|
|
14
|
+
*/
|
|
15
|
+
export declare const computePointInWindow: (widget: Gtk.Widget, x: number, y: number) => Point | null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Widget geometry in foreign coordinate spaces — what RN's measure()/
|
|
2
|
+
// measureInWindow()/measureLayout() and an event's pageX/pageY need.
|
|
3
|
+
//
|
|
4
|
+
// GTK4 folds a widget's allocated position into its transform relative to
|
|
5
|
+
// its parent, so there is no globally meaningful x/y to read off a widget:
|
|
6
|
+
// the position only exists as an answer to "in whose coordinates?".
|
|
7
|
+
// gtk_widget_compute_point() walks that transform chain, which is also why
|
|
8
|
+
// it comes out right inside a scrolled viewport — GtkViewport allocates its
|
|
9
|
+
// child at -adjustment.value, so the scroll offset IS the child's transform.
|
|
10
|
+
//
|
|
11
|
+
// gtk_widget_translate_coordinates() and gtk_widget_get_allocation() do the
|
|
12
|
+
// same job and are deprecated since GTK 4.12; compute_point/compute_bounds
|
|
13
|
+
// are the replacements. Both can legitimately fail (no common ancestor, an
|
|
14
|
+
// unrealized widget, a singular transform) — every helper here returns null
|
|
15
|
+
// rather than a plausible-looking zero.
|
|
16
|
+
import * as Graphene from "@gtkx/gi/graphene";
|
|
17
|
+
// Graphene.Point's constructor takes plain scalars, so it does not hit the
|
|
18
|
+
// nested-boxed-struct crash that Graphene.Rect's does (RC2-WORKAROUND
|
|
19
|
+
// graphene-rect-nested-boxed-props, docs/gtkx-rc2-notes.md) — but alloc+init
|
|
20
|
+
// is the shape already proven in svg-node.ts, so use it here too.
|
|
21
|
+
const point = (x, y) => Graphene.Point.alloc().init(x, y);
|
|
22
|
+
/**
|
|
23
|
+
* Translates a point from `widget`'s coordinates into `target`'s.
|
|
24
|
+
* Returns null when the two are not in the same widget hierarchy.
|
|
25
|
+
*/
|
|
26
|
+
export const computePointIn = (widget, target, x, y) => {
|
|
27
|
+
const [ok, translated] = widget.computePoint(target, point(x, y));
|
|
28
|
+
return ok ? { x: translated.x, y: translated.y } : null;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Translates a point from `widget`'s coordinates into its toplevel's — RN's
|
|
32
|
+
* "page" space. Returns null for a widget that is not in a window yet.
|
|
33
|
+
*/
|
|
34
|
+
export const computePointInWindow = (widget, x, y) => {
|
|
35
|
+
const root = widget.getRoot();
|
|
36
|
+
return root ? computePointIn(widget, root, x, y) : null;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/geometry.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,qEAAqE;AACrE,EAAE;AACF,0EAA0E;AAC1E,2EAA2E;AAC3E,oEAAoE;AACpE,2EAA2E;AAC3E,4EAA4E;AAC5E,6EAA6E;AAC7E,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,4EAA4E;AAC5E,wCAAwC;AACxC,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAK7C,2EAA2E;AAC3E,sEAAsE;AACtE,6EAA6E;AAC7E,kEAAkE;AAClE,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,CAAS,EAAkB,EAAE,CACrD,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAEnC;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,MAAkB,EAClB,MAAkB,EAClB,CAAS,EACT,CAAS,EACK,EAAE;IAChB,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACjE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACzD,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAAkB,EAClB,CAAS,EACT,CAAS,EACK,EAAE;IAChB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACzD,CAAC,CAAA","sourcesContent":["// Widget geometry in foreign coordinate spaces — what RN's measure()/\n// measureInWindow()/measureLayout() and an event's pageX/pageY need.\n//\n// GTK4 folds a widget's allocated position into its transform relative to\n// its parent, so there is no globally meaningful x/y to read off a widget:\n// the position only exists as an answer to \"in whose coordinates?\".\n// gtk_widget_compute_point() walks that transform chain, which is also why\n// it comes out right inside a scrolled viewport — GtkViewport allocates its\n// child at -adjustment.value, so the scroll offset IS the child's transform.\n//\n// gtk_widget_translate_coordinates() and gtk_widget_get_allocation() do the\n// same job and are deprecated since GTK 4.12; compute_point/compute_bounds\n// are the replacements. Both can legitimately fail (no common ancestor, an\n// unrealized widget, a singular transform) — every helper here returns null\n// rather than a plausible-looking zero.\nimport * as Graphene from \"@gtkx/gi/graphene\"\nimport type * as Gtk from \"@gtkx/gi/gtk\"\n\nexport type Point = { x: number; y: number }\n\n// Graphene.Point's constructor takes plain scalars, so it does not hit the\n// nested-boxed-struct crash that Graphene.Rect's does (RC2-WORKAROUND\n// graphene-rect-nested-boxed-props, docs/gtkx-rc2-notes.md) — but alloc+init\n// is the shape already proven in svg-node.ts, so use it here too.\nconst point = (x: number, y: number): Graphene.Point =>\n Graphene.Point.alloc().init(x, y)\n\n/**\n * Translates a point from `widget`'s coordinates into `target`'s.\n * Returns null when the two are not in the same widget hierarchy.\n */\nexport const computePointIn = (\n widget: Gtk.Widget,\n target: Gtk.Widget,\n x: number,\n y: number,\n): Point | null => {\n const [ok, translated] = widget.computePoint(target, point(x, y))\n return ok ? { x: translated.x, y: translated.y } : null\n}\n\n/**\n * Translates a point from `widget`'s coordinates into its toplevel's — RN's\n * \"page\" space. Returns null for a widget that is not in a window yet.\n */\nexport const computePointInWindow = (\n widget: Gtk.Widget,\n x: number,\n y: number,\n): Point | null => {\n const root = widget.getRoot()\n return root ? computePointIn(widget, root, x, y) : null\n}\n"]}
|
|
@@ -2,17 +2,24 @@ import * as Adw from "@gtkx/gi/adw";
|
|
|
2
2
|
import * as Gdk from "@gtkx/gi/gdk";
|
|
3
3
|
import * as Gio from "@gtkx/gi/gio";
|
|
4
4
|
import * as GLib from "@gtkx/gi/glib";
|
|
5
|
+
import * as GObject from "@gtkx/gi/gobject";
|
|
5
6
|
import * as Graphene from "@gtkx/gi/graphene";
|
|
6
7
|
import * as Gsk from "@gtkx/gi/gsk";
|
|
7
8
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
8
9
|
import * as Pango from "@gtkx/gi/pango";
|
|
9
|
-
export { Adw, Gdk, Gio, GLib, Graphene, Gsk, Gtk, Pango };
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
10
|
+
export { Adw, Gdk, Gio, GLib, GObject, Graphene, Gsk, Gtk, Pango };
|
|
11
|
+
export { GtkApplication, GtkGestureClick } from "@gtkx/jsx/gtk";
|
|
12
|
+
export { GMenu, GSimpleAction } from "@gtkx/jsx/gio";
|
|
13
|
+
export { AdwBreakpoint, AdwShortcutsItem, AdwShortcutsSection, AdwToggle, } from "@gtkx/jsx/adw";
|
|
14
|
+
export { GtkAdjustment, GtkDragSource, GtkDropTarget, GtkShortcut, GtkShortcutController, GtkTextBuffer, } from "@gtkx/jsx/gtk";
|
|
15
|
+
export * from "./widgets.generated";
|
|
16
|
+
export { createPortal, createRoot, quit, rootElement, useApplication, useBindSetting, useParentWindow, useProperty, useSetting, type Root, type RootElement, type SettingsSchema, type SettingsSchemaKeys, type SettingValue, } from "@gtkx/react";
|
|
13
17
|
export { useSignal } from "./use-signal";
|
|
14
18
|
export { css, cx, injectGlobal } from "@gtkx/css";
|
|
15
19
|
export { createTextProbe, measureWidget, toNumber } from "./measure";
|
|
20
|
+
export { computePointIn, computePointInWindow, type Point } from "./geometry";
|
|
21
|
+
export { createSlotPortal } from "./slot-portal";
|
|
16
22
|
export { getViewBoxComponent, setBoxPassthrough } from "./view-box";
|
|
23
|
+
export { getSvgNodeComponent, getSvgNodeDescriptor, queueSvgRedraw, resolveSvgColor, resolveSvgPaint, setSvgNodeDescriptor, type SvgDefsDescriptor, type SvgGradientUnits, type SvgGroupDescriptor, type SvgLinearGradientDescriptor, type SvgNodeDescriptor, type SvgPaintSpec, type SvgRadialGradientDescriptor, type SvgShapeDescriptor, type SvgStopDescriptor, type SvgSvgDescriptor, } from "./svg-node";
|
|
17
24
|
export { allocateChild, attachRnLayout, detachRnLayout, queueAllocate, queueResize, type RnLayoutHooks, type RnLayoutOrientation, } from "./layout-manager";
|
|
18
25
|
export { colorScheme, styleManager } from "./theme";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The ONLY module allowed to import @gtkx/* (enforced by eslint no-restricted-imports).
|
|
2
2
|
// gtkx is an RC dependency: when its API moves, this bridge absorbs the change.
|
|
3
3
|
//
|
|
4
|
-
// Caveats baked into this surface (catalogued in docs/gtkx-
|
|
4
|
+
// Caveats baked into this surface (catalogued in docs/gtkx-rc3-notes.md):
|
|
5
5
|
// - 64-bit FFI values arrive as BigInt → normalize with toNumber() at this boundary
|
|
6
6
|
// import-then-export (not `export * as`): the latter is the one syntax the
|
|
7
7
|
// stock @react-native/babel-preset cannot transform — this form keeps
|
|
@@ -10,20 +10,61 @@ import * as Adw from "@gtkx/gi/adw";
|
|
|
10
10
|
import * as Gdk from "@gtkx/gi/gdk";
|
|
11
11
|
import * as Gio from "@gtkx/gi/gio";
|
|
12
12
|
import * as GLib from "@gtkx/gi/glib";
|
|
13
|
+
import * as GObject from "@gtkx/gi/gobject";
|
|
13
14
|
import * as Graphene from "@gtkx/gi/graphene";
|
|
14
15
|
import * as Gsk from "@gtkx/gi/gsk";
|
|
15
16
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
16
17
|
import * as Pango from "@gtkx/gi/pango";
|
|
17
|
-
export { Adw, Gdk, Gio, GLib, Graphene, Gsk, Gtk, Pango };
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
export { Adw, Gdk, Gio, GLib, GObject, Graphene, Gsk, Gtk, Pango };
|
|
19
|
+
// GtkApplication and GtkGestureClick are not Gtk.Widget subclasses (an
|
|
20
|
+
// application object and an event controller, respectively), so the widget
|
|
21
|
+
// surface generator never sees them — kept here by hand, same as before it
|
|
22
|
+
// existed.
|
|
23
|
+
export { GtkApplication, GtkGestureClick } from "@gtkx/jsx/gtk";
|
|
24
|
+
// Auxiliary gtkx JSX elements that are not Gtk.Widget/Adw.Widget subclasses
|
|
25
|
+
// either, same reason as the pair above — scripts/generate-widget-surface.ts
|
|
26
|
+
// only classifies widgets, so these fall into its "notAWidget" bucket
|
|
27
|
+
// (see scripts/widget-surface/classification.json) and are otherwise
|
|
28
|
+
// unreachable through react-native-gtkx/gtk or /adw. Every one of these is a
|
|
29
|
+
// real, necessary building block for a real app: actions and menus (Gio),
|
|
30
|
+
// a responsive breakpoint (Adw), a text buffer and an adjustment (the model
|
|
31
|
+
// objects GtkTextView/GtkSpinRow etc. bind to), keyboard shortcuts, and the
|
|
32
|
+
// two drag-and-drop controllers.
|
|
33
|
+
//
|
|
34
|
+
// Adw.Breakpoint specifically: independently verified
|
|
35
|
+
// (`Adw.Breakpoint.prototype instanceof Gtk.Widget === false`, prototype
|
|
36
|
+
// chain bottoms out at plain GObject.Object) — it draws nothing and has no
|
|
37
|
+
// size, it is a declaration (a condition plus a set of property setters)
|
|
38
|
+
// attached to a Window/ApplicationWindow/Dialog/BreakpointBin. Running it
|
|
39
|
+
// through wrapReactNative would give it a Yoga node for a widget that
|
|
40
|
+
// occupies no space: a layout bug, not a convenience. Adw.BreakpointBin (a
|
|
41
|
+
// real widget, the container that scopes breakpoints to a subtree) is
|
|
42
|
+
// wrapped normally in ./widgets.generated instead, and is what
|
|
43
|
+
// createSidebarNavigator's own collapse pairs AdwBreakpoint with (see
|
|
44
|
+
// docs/platform-layer.md, "Two ways to react to size").
|
|
45
|
+
export { GMenu, GSimpleAction } from "@gtkx/jsx/gio";
|
|
46
|
+
export { AdwBreakpoint, AdwShortcutsItem, AdwShortcutsSection, AdwToggle, } from "@gtkx/jsx/adw";
|
|
47
|
+
export { GtkAdjustment, GtkDragSource, GtkDropTarget, GtkShortcut, GtkShortcutController, GtkTextBuffer, } from "@gtkx/jsx/gtk";
|
|
48
|
+
// The full widget surface — every GTK/Adwaita class gtkx binds that derives
|
|
49
|
+
// Gtk.Widget, raw. src/gtk/widgets.generated and src/adw/widgets.generated
|
|
50
|
+
// wrap most of them with wrapReactNative; this file is where @gtkx/jsx is
|
|
51
|
+
// still allowed to be imported directly (see the eslint carve-out for
|
|
52
|
+
// src/gtkx/bridge/**), so they pull the raw widgets from here rather than
|
|
53
|
+
// from @gtkx/jsx themselves.
|
|
54
|
+
export * from "./widgets.generated";
|
|
55
|
+
export { createPortal, createRoot, quit, rootElement, useApplication, useBindSetting, useParentWindow, useProperty, useSetting, } from "@gtkx/react";
|
|
21
56
|
// useSignal comes from ./use-signal, not @gtkx/react — see the workaround note
|
|
22
|
-
// there (rc.
|
|
57
|
+
// there (rc.3 delivers a stale handler).
|
|
23
58
|
export { useSignal } from "./use-signal";
|
|
24
59
|
export { css, cx, injectGlobal } from "@gtkx/css";
|
|
25
60
|
export { createTextProbe, measureWidget, toNumber } from "./measure";
|
|
61
|
+
export { computePointIn, computePointInWindow } from "./geometry";
|
|
62
|
+
// A portal into a NAMED slot of a remote object (the window's action map,
|
|
63
|
+
// its controllers) rather than into its default child slot — see the file
|
|
64
|
+
// for why gtkx's own createPortal is not enough on its own.
|
|
65
|
+
export { createSlotPortal } from "./slot-portal";
|
|
26
66
|
export { getViewBoxComponent, setBoxPassthrough } from "./view-box";
|
|
67
|
+
export { getSvgNodeComponent, getSvgNodeDescriptor, queueSvgRedraw, resolveSvgColor, resolveSvgPaint, setSvgNodeDescriptor, } from "./svg-node";
|
|
27
68
|
export { allocateChild, attachRnLayout, detachRnLayout, queueAllocate, queueResize, } from "./layout-manager";
|
|
28
69
|
export { colorScheme, styleManager } from "./theme";
|
|
29
70
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,gFAAgF;AAChF,EAAE;AACF,0EAA0E;AAC1E,oFAAoF;AAEpF,2EAA2E;AAC3E,sEAAsE;AACtE,qDAAqD;AACrD,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAEvC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,gFAAgF;AAChF,EAAE;AACF,0EAA0E;AAC1E,oFAAoF;AAEpF,2EAA2E;AAC3E,sEAAsE;AACtE,qDAAqD;AACrD,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAEvC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AAElE,uEAAuE;AACvE,2EAA2E;AAC3E,2EAA2E;AAC3E,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/D,4EAA4E;AAC5E,6EAA6E;AAC7E,sEAAsE;AACtE,qEAAqE;AACrE,6EAA6E;AAC7E,0EAA0E;AAC1E,4EAA4E;AAC5E,4EAA4E;AAC5E,iCAAiC;AACjC,EAAE;AACF,sDAAsD;AACtD,yEAAyE;AACzE,2EAA2E;AAC3E,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,2EAA2E;AAC3E,sEAAsE;AACtE,+DAA+D;AAC/D,sEAAsE;AACtE,wDAAwD;AACxD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,SAAS,GACV,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,WAAW,EACX,qBAAqB,EACrB,aAAa,GACd,MAAM,eAAe,CAAA;AAEtB,4EAA4E;AAC5E,2EAA2E;AAC3E,0EAA0E;AAC1E,sEAAsE;AACtE,0EAA0E;AAC1E,6BAA6B;AAC7B,cAAc,qBAAqB,CAAA;AAEnC,OAAO,EACL,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,WAAW,EACX,cAAc,EACd,cAAc,EACd,eAAe,EACf,WAAW,EACX,UAAU,GAMX,MAAM,aAAa,CAAA;AAEpB,+EAA+E;AAC/E,yCAAyC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAc,MAAM,YAAY,CAAA;AAC7E,0EAA0E;AAC1E,0EAA0E;AAC1E,4DAA4D;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,eAAe,EACf,oBAAoB,GAWrB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,aAAa,EACb,cAAc,EACd,cAAc,EACd,aAAa,EACb,WAAW,GAGZ,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA","sourcesContent":["// The ONLY module allowed to import @gtkx/* (enforced by eslint no-restricted-imports).\n// gtkx is an RC dependency: when its API moves, this bridge absorbs the change.\n//\n// Caveats baked into this surface (catalogued in docs/gtkx-rc3-notes.md):\n// - 64-bit FFI values arrive as BigInt → normalize with toNumber() at this boundary\n\n// import-then-export (not `export * as`): the latter is the one syntax the\n// stock @react-native/babel-preset cannot transform — this form keeps\n// consumer apps on their unmodified RN Babel config.\nimport * as Adw from \"@gtkx/gi/adw\"\nimport * as Gdk from \"@gtkx/gi/gdk\"\nimport * as Gio from \"@gtkx/gi/gio\"\nimport * as GLib from \"@gtkx/gi/glib\"\nimport * as GObject from \"@gtkx/gi/gobject\"\nimport * as Graphene from \"@gtkx/gi/graphene\"\nimport * as Gsk from \"@gtkx/gi/gsk\"\nimport * as Gtk from \"@gtkx/gi/gtk\"\nimport * as Pango from \"@gtkx/gi/pango\"\n\nexport { Adw, Gdk, Gio, GLib, GObject, Graphene, Gsk, Gtk, Pango }\n\n// GtkApplication and GtkGestureClick are not Gtk.Widget subclasses (an\n// application object and an event controller, respectively), so the widget\n// surface generator never sees them — kept here by hand, same as before it\n// existed.\nexport { GtkApplication, GtkGestureClick } from \"@gtkx/jsx/gtk\"\n\n// Auxiliary gtkx JSX elements that are not Gtk.Widget/Adw.Widget subclasses\n// either, same reason as the pair above — scripts/generate-widget-surface.ts\n// only classifies widgets, so these fall into its \"notAWidget\" bucket\n// (see scripts/widget-surface/classification.json) and are otherwise\n// unreachable through react-native-gtkx/gtk or /adw. Every one of these is a\n// real, necessary building block for a real app: actions and menus (Gio),\n// a responsive breakpoint (Adw), a text buffer and an adjustment (the model\n// objects GtkTextView/GtkSpinRow etc. bind to), keyboard shortcuts, and the\n// two drag-and-drop controllers.\n//\n// Adw.Breakpoint specifically: independently verified\n// (`Adw.Breakpoint.prototype instanceof Gtk.Widget === false`, prototype\n// chain bottoms out at plain GObject.Object) — it draws nothing and has no\n// size, it is a declaration (a condition plus a set of property setters)\n// attached to a Window/ApplicationWindow/Dialog/BreakpointBin. Running it\n// through wrapReactNative would give it a Yoga node for a widget that\n// occupies no space: a layout bug, not a convenience. Adw.BreakpointBin (a\n// real widget, the container that scopes breakpoints to a subtree) is\n// wrapped normally in ./widgets.generated instead, and is what\n// createSidebarNavigator's own collapse pairs AdwBreakpoint with (see\n// docs/platform-layer.md, \"Two ways to react to size\").\nexport { GMenu, GSimpleAction } from \"@gtkx/jsx/gio\"\nexport {\n AdwBreakpoint,\n AdwShortcutsItem,\n AdwShortcutsSection,\n AdwToggle,\n} from \"@gtkx/jsx/adw\"\nexport {\n GtkAdjustment,\n GtkDragSource,\n GtkDropTarget,\n GtkShortcut,\n GtkShortcutController,\n GtkTextBuffer,\n} from \"@gtkx/jsx/gtk\"\n\n// The full widget surface — every GTK/Adwaita class gtkx binds that derives\n// Gtk.Widget, raw. src/gtk/widgets.generated and src/adw/widgets.generated\n// wrap most of them with wrapReactNative; this file is where @gtkx/jsx is\n// still allowed to be imported directly (see the eslint carve-out for\n// src/gtkx/bridge/**), so they pull the raw widgets from here rather than\n// from @gtkx/jsx themselves.\nexport * from \"./widgets.generated\"\n\nexport {\n createPortal,\n createRoot,\n quit,\n rootElement,\n useApplication,\n useBindSetting,\n useParentWindow,\n useProperty,\n useSetting,\n type Root,\n type RootElement,\n type SettingsSchema,\n type SettingsSchemaKeys,\n type SettingValue,\n} from \"@gtkx/react\"\n\n// useSignal comes from ./use-signal, not @gtkx/react — see the workaround note\n// there (rc.3 delivers a stale handler).\nexport { useSignal } from \"./use-signal\"\n\nexport { css, cx, injectGlobal } from \"@gtkx/css\"\n\nexport { createTextProbe, measureWidget, toNumber } from \"./measure\"\nexport { computePointIn, computePointInWindow, type Point } from \"./geometry\"\n// A portal into a NAMED slot of a remote object (the window's action map,\n// its controllers) rather than into its default child slot — see the file\n// for why gtkx's own createPortal is not enough on its own.\nexport { createSlotPortal } from \"./slot-portal\"\nexport { getViewBoxComponent, setBoxPassthrough } from \"./view-box\"\nexport {\n getSvgNodeComponent,\n getSvgNodeDescriptor,\n queueSvgRedraw,\n resolveSvgColor,\n resolveSvgPaint,\n setSvgNodeDescriptor,\n type SvgDefsDescriptor,\n type SvgGradientUnits,\n type SvgGroupDescriptor,\n type SvgLinearGradientDescriptor,\n type SvgNodeDescriptor,\n type SvgPaintSpec,\n type SvgRadialGradientDescriptor,\n type SvgShapeDescriptor,\n type SvgStopDescriptor,\n type SvgSvgDescriptor,\n} from \"./svg-node\"\nexport {\n allocateChild,\n attachRnLayout,\n detachRnLayout,\n queueAllocate,\n queueResize,\n type RnLayoutHooks,\n type RnLayoutOrientation,\n} from \"./layout-manager\"\nexport { colorScheme, styleManager } from \"./theme\"\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import type { Transform2D } from "../../contracts";
|
|
2
3
|
export type RnLayoutOrientation = "horizontal" | "vertical";
|
|
3
4
|
export type RnLayoutHooks = {
|
|
4
5
|
measure(orientation: RnLayoutOrientation, forSize: number): number;
|
|
@@ -6,6 +7,6 @@ export type RnLayoutHooks = {
|
|
|
6
7
|
};
|
|
7
8
|
export declare const attachRnLayout: (widget: Gtk.Widget, hooks: RnLayoutHooks) => void;
|
|
8
9
|
export declare const detachRnLayout: (widget: Gtk.Widget) => void;
|
|
9
|
-
export declare const allocateChild: (child: Gtk.Widget, x: number, y: number, width: number, height: number) => void;
|
|
10
|
+
export declare const allocateChild: (child: Gtk.Widget, x: number, y: number, width: number, height: number, matrix?: Transform2D | null) => void;
|
|
10
11
|
export declare const queueAllocate: (widget: Gtk.Widget) => void;
|
|
11
12
|
export declare const queueResize: (widget: Gtk.Widget) => void;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// minimum ratchet, overflow children inflating ancestors, and widget minimums
|
|
8
8
|
// pushing rects.
|
|
9
9
|
import * as Gdk from "@gtkx/gi/gdk";
|
|
10
|
+
import * as Gsk from "@gtkx/gi/gsk";
|
|
10
11
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
11
12
|
import { registerClass } from "@gtkx/runtime";
|
|
12
13
|
// The class must exist before any instance is created, but registration needs
|
|
@@ -66,15 +67,36 @@ export const detachRnLayout = (widget) => {
|
|
|
66
67
|
hooksByManager.delete(manager);
|
|
67
68
|
widget.setLayoutManager(null);
|
|
68
69
|
};
|
|
69
|
-
// Places one child at an exact rect
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
// Places one child at an exact rect, optionally under a visual transform.
|
|
71
|
+
// Only meaningful from inside an allocate() hook — GTK forbids
|
|
72
|
+
// size-allocating outside the layout phase.
|
|
73
|
+
//
|
|
74
|
+
// The transform path is gtk_widget_allocate(), which is what
|
|
75
|
+
// gtk_widget_size_allocate() itself calls with a plain translate — the child
|
|
76
|
+
// keeps the width/height Yoga gave it and only its placement matrix changes,
|
|
77
|
+
// which is exactly RN's "transforms are visual only". GTK4 inverts that
|
|
78
|
+
// matrix in gtk_widget_pick(), so input follows the rotated/scaled shape for
|
|
79
|
+
// free (measured: docs/research/transforms.md).
|
|
80
|
+
export const allocateChild = (child, x, y, width, height, matrix = null) => {
|
|
81
|
+
const left = Math.round(x);
|
|
82
|
+
const top = Math.round(y);
|
|
83
|
+
const w = Math.max(0, Math.round(width));
|
|
84
|
+
const h = Math.max(0, Math.round(height));
|
|
85
|
+
if (!matrix) {
|
|
86
|
+
child.sizeAllocate(new Gdk.Rectangle({ x: left, y: top, width: w, height: h }), -1);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// RN's transform origin is the centre of the view, so the matrix GTK gets
|
|
90
|
+
// is T(left + cx, top + cy) · matrix · T(-cx, -cy) — folded into the six
|
|
91
|
+
// components here rather than chained through GskTransform, which would
|
|
92
|
+
// cost five FFI hops instead of one.
|
|
93
|
+
const cx = w / 2;
|
|
94
|
+
const cy = h / 2;
|
|
95
|
+
const transform = Gsk.Transform.new().matrix2d(matrix.xx, matrix.yx, matrix.xy, matrix.yy, left + cx - (matrix.xx * cx + matrix.xy * cy) + matrix.dx, top + cy - (matrix.yx * cx + matrix.yy * cy) + matrix.dy);
|
|
96
|
+
// gtk_widget_allocate consumes the transform (transfer full) and gtkx
|
|
97
|
+
// hands ownership over with it — never unref it here (that aborts on
|
|
98
|
+
// GLib's rc-box magic assertion).
|
|
99
|
+
child.allocate(w, h, -1, transform);
|
|
78
100
|
};
|
|
79
101
|
// Engine flush → new rects for the same container size: schedule allocation
|
|
80
102
|
// only. A measure-affecting change must use queueResize instead.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout-manager.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/layout-manager.ts"],"names":[],"mappings":"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","sourcesContent":["// RnGtkxLayout — a GObject subclass of GtkLayoutManager registered entirely\n// from JS (docs/research/layout-manager.md, \"B0\"). The manager obeys ONLY the\n// layout engine: measure() reports the engine-provided size (minimum ==\n// natural, children are never queried) and allocate() delegates to the engine,\n// which places every child at exactly its computed rect via allocateChild().\n// This is what removes the GTK-vs-RN layout conflicts at the root: window\n// minimum ratchet, overflow children inflating ancestors, and widget minimums\n// pushing rects.\nimport * as Gdk from \"@gtkx/gi/gdk\"\nimport * as Gtk from \"@gtkx/gi/gtk\"\nimport { registerClass } from \"@gtkx/runtime\"\n\nexport type RnLayoutOrientation = \"horizontal\" | \"vertical\"\n\nexport type RnLayoutHooks = {\n // Engine-provided size for the given axis; minimum == natural == this value.\n measure(orientation: RnLayoutOrientation, forSize: number): number\n // Called during GTK's allocation phase with the container's final size.\n // The engine must size-allocate every child (allocateChild) synchronously.\n allocate(width: number, height: number): void\n}\n\n// The class must exist before any instance is created, but registration needs\n// GObject to be initialized — both hold after the bridge is imported, so the\n// class is registered lazily on first attach (also keeps the module safe to\n// import in environments without a display).\nlet RnGtkxLayoutClass: (new () => Gtk.LayoutManager) | null = null\n\nconst hooksByManager = new WeakMap<Gtk.LayoutManager, RnLayoutHooks>()\n\nconst ensureRegistered = (): new () => Gtk.LayoutManager => {\n if (RnGtkxLayoutClass) {\n return RnGtkxLayoutClass\n }\n\n class RnGtkxLayout extends Gtk.LayoutManager {\n override measure(\n _widget: Gtk.Widget,\n orientation: Gtk.Orientation,\n forSize: number,\n ): [number, number, number, number] {\n const hooks = hooksByManager.get(this)\n if (!hooks) {\n return [0, 0, -1, -1]\n }\n const size = hooks.measure(\n orientation === Gtk.Orientation.HORIZONTAL ? \"horizontal\" : \"vertical\",\n forSize,\n )\n return [size, size, -1, -1]\n }\n\n // Trailing vfunc params (baseline; widget) are dropped — JS tolerates the\n // arity mismatch and the linter rejects unused trailing args.\n override allocate(\n _widget: Gtk.Widget,\n width: number,\n height: number,\n ): void {\n hooksByManager.get(this)?.allocate(width, height)\n }\n\n override getRequestMode(): Gtk.SizeRequestMode {\n return Gtk.SizeRequestMode.CONSTANT_SIZE\n }\n }\n\n // Explicit typeName: bundlers minify class names and registerClass derives\n // the GType name from klass.name by default (FINDINGS §1).\n registerClass(RnGtkxLayout, { typeName: \"RnGtkxLayout\" })\n RnGtkxLayoutClass = RnGtkxLayout\n return RnGtkxLayout\n}\n\n// Replaces the widget's layout manager with an RnGtkxLayout driven by `hooks`.\n// The container must attach its children in a layout-child-free way (GtkBox\n// append / set_parent) — GtkFixed.put demands a GtkFixedLayoutChild from the\n// current manager and is incompatible (FINDINGS §2).\nexport const attachRnLayout = (\n widget: Gtk.Widget,\n hooks: RnLayoutHooks,\n): void => {\n const Klass = ensureRegistered()\n if (hooksByManager.has(widget.getLayoutManager() as Gtk.LayoutManager)) {\n throw new Error(\"attachRnLayout: widget already has an RnGtkxLayout\")\n }\n const manager = new Klass()\n hooksByManager.set(manager, hooks)\n widget.setLayoutManager(manager)\n}\n\n// Drops the hooks and hands layout back to a fresh manager of the widget's\n// own class default. Safe to call on widgets that were never attached.\nexport const detachRnLayout = (widget: Gtk.Widget): void => {\n const manager = widget.getLayoutManager() as Gtk.LayoutManager | null\n if (!manager || !hooksByManager.has(manager)) {\n return\n }\n hooksByManager.delete(manager)\n widget.setLayoutManager(null as unknown as Gtk.LayoutManager)\n}\n\n// Places one child at an exact rect. Only meaningful from inside an allocate()\n// hook — GTK forbids size-allocating outside the layout phase.\nexport const allocateChild = (\n child: Gtk.Widget,\n x: number,\n y: number,\n width: number,\n height: number,\n): void => {\n child.sizeAllocate(\n new Gdk.Rectangle({\n x: Math.round(x),\n y: Math.round(y),\n width: Math.max(0, Math.round(width)),\n height: Math.max(0, Math.round(height)),\n }),\n -1,\n )\n}\n\n// Engine flush → new rects for the same container size: schedule allocation\n// only. A measure-affecting change must use queueResize instead.\nexport const queueAllocate = (widget: Gtk.Widget): void => {\n widget.queueAllocate()\n}\n\nexport const queueResize = (widget: Gtk.Widget): void => {\n widget.queueResize()\n}\n"]}
|
|
1
|
+
{"version":3,"file":"layout-manager.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/layout-manager.ts"],"names":[],"mappings":"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","sourcesContent":["// RnGtkxLayout — a GObject subclass of GtkLayoutManager registered entirely\n// from JS (docs/research/layout-manager.md, \"B0\"). The manager obeys ONLY the\n// layout engine: measure() reports the engine-provided size (minimum ==\n// natural, children are never queried) and allocate() delegates to the engine,\n// which places every child at exactly its computed rect via allocateChild().\n// This is what removes the GTK-vs-RN layout conflicts at the root: window\n// minimum ratchet, overflow children inflating ancestors, and widget minimums\n// pushing rects.\nimport * as Gdk from \"@gtkx/gi/gdk\"\nimport * as Gsk from \"@gtkx/gi/gsk\"\nimport * as Gtk from \"@gtkx/gi/gtk\"\nimport { registerClass } from \"@gtkx/runtime\"\nimport type { Transform2D } from \"../../contracts\"\n\nexport type RnLayoutOrientation = \"horizontal\" | \"vertical\"\n\nexport type RnLayoutHooks = {\n // Engine-provided size for the given axis; minimum == natural == this value.\n measure(orientation: RnLayoutOrientation, forSize: number): number\n // Called during GTK's allocation phase with the container's final size.\n // The engine must size-allocate every child (allocateChild) synchronously.\n allocate(width: number, height: number): void\n}\n\n// The class must exist before any instance is created, but registration needs\n// GObject to be initialized — both hold after the bridge is imported, so the\n// class is registered lazily on first attach (also keeps the module safe to\n// import in environments without a display).\nlet RnGtkxLayoutClass: (new () => Gtk.LayoutManager) | null = null\n\nconst hooksByManager = new WeakMap<Gtk.LayoutManager, RnLayoutHooks>()\n\nconst ensureRegistered = (): new () => Gtk.LayoutManager => {\n if (RnGtkxLayoutClass) {\n return RnGtkxLayoutClass\n }\n\n class RnGtkxLayout extends Gtk.LayoutManager {\n override measure(\n _widget: Gtk.Widget,\n orientation: Gtk.Orientation,\n forSize: number,\n ): [number, number, number, number] {\n const hooks = hooksByManager.get(this)\n if (!hooks) {\n return [0, 0, -1, -1]\n }\n const size = hooks.measure(\n orientation === Gtk.Orientation.HORIZONTAL ? \"horizontal\" : \"vertical\",\n forSize,\n )\n return [size, size, -1, -1]\n }\n\n // Trailing vfunc params (baseline; widget) are dropped — JS tolerates the\n // arity mismatch and the linter rejects unused trailing args.\n override allocate(\n _widget: Gtk.Widget,\n width: number,\n height: number,\n ): void {\n hooksByManager.get(this)?.allocate(width, height)\n }\n\n override getRequestMode(): Gtk.SizeRequestMode {\n return Gtk.SizeRequestMode.CONSTANT_SIZE\n }\n }\n\n // Explicit typeName: bundlers minify class names and registerClass derives\n // the GType name from klass.name by default (FINDINGS §1).\n registerClass(RnGtkxLayout, { typeName: \"RnGtkxLayout\" })\n RnGtkxLayoutClass = RnGtkxLayout\n return RnGtkxLayout\n}\n\n// Replaces the widget's layout manager with an RnGtkxLayout driven by `hooks`.\n// The container must attach its children in a layout-child-free way (GtkBox\n// append / set_parent) — GtkFixed.put demands a GtkFixedLayoutChild from the\n// current manager and is incompatible (FINDINGS §2).\nexport const attachRnLayout = (\n widget: Gtk.Widget,\n hooks: RnLayoutHooks,\n): void => {\n const Klass = ensureRegistered()\n if (hooksByManager.has(widget.getLayoutManager() as Gtk.LayoutManager)) {\n throw new Error(\"attachRnLayout: widget already has an RnGtkxLayout\")\n }\n const manager = new Klass()\n hooksByManager.set(manager, hooks)\n widget.setLayoutManager(manager)\n}\n\n// Drops the hooks and hands layout back to a fresh manager of the widget's\n// own class default. Safe to call on widgets that were never attached.\nexport const detachRnLayout = (widget: Gtk.Widget): void => {\n const manager = widget.getLayoutManager() as Gtk.LayoutManager | null\n if (!manager || !hooksByManager.has(manager)) {\n return\n }\n hooksByManager.delete(manager)\n widget.setLayoutManager(null as unknown as Gtk.LayoutManager)\n}\n\n// Places one child at an exact rect, optionally under a visual transform.\n// Only meaningful from inside an allocate() hook — GTK forbids\n// size-allocating outside the layout phase.\n//\n// The transform path is gtk_widget_allocate(), which is what\n// gtk_widget_size_allocate() itself calls with a plain translate — the child\n// keeps the width/height Yoga gave it and only its placement matrix changes,\n// which is exactly RN's \"transforms are visual only\". GTK4 inverts that\n// matrix in gtk_widget_pick(), so input follows the rotated/scaled shape for\n// free (measured: docs/research/transforms.md).\nexport const allocateChild = (\n child: Gtk.Widget,\n x: number,\n y: number,\n width: number,\n height: number,\n matrix: Transform2D | null = null,\n): void => {\n const left = Math.round(x)\n const top = Math.round(y)\n const w = Math.max(0, Math.round(width))\n const h = Math.max(0, Math.round(height))\n\n if (!matrix) {\n child.sizeAllocate(\n new Gdk.Rectangle({ x: left, y: top, width: w, height: h }),\n -1,\n )\n return\n }\n\n // RN's transform origin is the centre of the view, so the matrix GTK gets\n // is T(left + cx, top + cy) · matrix · T(-cx, -cy) — folded into the six\n // components here rather than chained through GskTransform, which would\n // cost five FFI hops instead of one.\n const cx = w / 2\n const cy = h / 2\n const transform = Gsk.Transform.new().matrix2d(\n matrix.xx,\n matrix.yx,\n matrix.xy,\n matrix.yy,\n left + cx - (matrix.xx * cx + matrix.xy * cy) + matrix.dx,\n top + cy - (matrix.yx * cx + matrix.yy * cy) + matrix.dy,\n )\n // gtk_widget_allocate consumes the transform (transfer full) and gtkx\n // hands ownership over with it — never unref it here (that aborts on\n // GLib's rc-box magic assertion).\n child.allocate(w, h, -1, transform)\n}\n\n// Engine flush → new rects for the same container size: schedule allocation\n// only. A measure-affecting change must use queueResize instead.\nexport const queueAllocate = (widget: Gtk.Widget): void => {\n widget.queueAllocate()\n}\n\nexport const queueResize = (widget: Gtk.Widget): void => {\n widget.queueResize()\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type * as GObject from "@gtkx/gi/gobject";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* A React portal that renders `children` into `target`'s named `slot`
|
|
5
|
+
* instead of into the tree position where it is written — the declarative
|
|
6
|
+
* equivalent of passing `<Target slot={children} />`, for a target the
|
|
7
|
+
* declaring component does not render.
|
|
8
|
+
*
|
|
9
|
+
* The children stay part of the React tree where the portal element sits,
|
|
10
|
+
* so context, state and effects behave exactly as they would in place.
|
|
11
|
+
*/
|
|
12
|
+
export declare const createSlotPortal: (children: ReactNode, target: GObject.Object, slot: string, key?: string) => ReactNode;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createPortal } from "@gtkx/react";
|
|
2
|
+
import { createElement } from "react";
|
|
3
|
+
// Typed as a component rather than an intrinsic tag: "gtkx:prop" is not in
|
|
4
|
+
// the generated JSX.IntrinsicElements map (it is not a GObject type), so
|
|
5
|
+
// createElement would reject the bare string.
|
|
6
|
+
const PROP_ELEMENT = "gtkx:prop";
|
|
7
|
+
/**
|
|
8
|
+
* A React portal that renders `children` into `target`'s named `slot`
|
|
9
|
+
* instead of into the tree position where it is written — the declarative
|
|
10
|
+
* equivalent of passing `<Target slot={children} />`, for a target the
|
|
11
|
+
* declaring component does not render.
|
|
12
|
+
*
|
|
13
|
+
* The children stay part of the React tree where the portal element sits,
|
|
14
|
+
* so context, state and effects behave exactly as they would in place.
|
|
15
|
+
*/
|
|
16
|
+
export const createSlotPortal = (children, target, slot, key) => createPortal(createElement(PROP_ELEMENT, { propName: slot }, children), target, key);
|
|
17
|
+
//# sourceMappingURL=slot-portal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot-portal.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/slot-portal.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,aAAa,EAA0C,MAAM,OAAO,CAAA;AAE7E,2EAA2E;AAC3E,yEAAyE;AACzE,8CAA8C;AAC9C,MAAM,YAAY,GAAG,WAGnB,CAAA;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,QAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,GAAY,EACD,EAAE,CACb,YAAY,CACV,aAAa,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC,EACzD,MAAM,EACN,GAAG,CACJ,CAAA","sourcesContent":["// Rendering children into a NAMED SLOT of an object that is not their parent.\n//\n// gtkx's own `createPortal(children, container)` targets the container's\n// DEFAULT slot — \"children\", the one a widget takes another widget through.\n// Everything else an object can be given declaratively is a named slot\n// reached in JSX by an element-valued PROP: a window's `Gio.ActionMap`\n// (`actions`), the event controllers on a widget (`controllers`), an\n// `AdwApplicationWindow`'s `breakpoints`. Those slots are what the\n// reconciler routes an element-valued prop into — it wraps the prop's value\n// in an internal element that carries the slot name, and that element is\n// what actually places children into the named slot.\n//\n// So a portal whose single child is that internal element lands its children\n// in a named slot of a remote object, which is what `WindowActions` and\n// `WindowControllers` need: declare in the app tree, attach to the window.\n//\n// RC3-WORKAROUND(prop-portal): the internal element's name is spelled out\n// here because @gtkx/react exports it from neither its public entry point\n// nor its `/internal` subpath — only from a deep module path, which this\n// package does not reach into. The bridge is the one module allowed to know\n// a gtkx internal, so the knowledge is confined to this file: if the name\n// ever changes, exactly one line moves. See docs/gtkx-rc3-notes.md.\nimport type * as GObject from \"@gtkx/gi/gobject\"\nimport { createPortal } from \"@gtkx/react\"\nimport { createElement, type FunctionComponent, type ReactNode } from \"react\"\n\n// Typed as a component rather than an intrinsic tag: \"gtkx:prop\" is not in\n// the generated JSX.IntrinsicElements map (it is not a GObject type), so\n// createElement would reject the bare string.\nconst PROP_ELEMENT = \"gtkx:prop\" as unknown as FunctionComponent<{\n propName: string\n children?: ReactNode\n}>\n\n/**\n * A React portal that renders `children` into `target`'s named `slot`\n * instead of into the tree position where it is written — the declarative\n * equivalent of passing `<Target slot={children} />`, for a target the\n * declaring component does not render.\n *\n * The children stay part of the React tree where the portal element sits,\n * so context, state and effects behave exactly as they would in place.\n */\nexport const createSlotPortal = (\n children: ReactNode,\n target: GObject.Object,\n slot: string,\n key?: string,\n): ReactNode =>\n createPortal(\n createElement(PROP_ELEMENT, { propName: slot }, children),\n target,\n key,\n )\n"]}
|