react-native-gtkx 0.3.0-alpha.1 → 0.4.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.
Files changed (151) hide show
  1. package/README.md +1 -1
  2. package/dist/adw/index.d.ts +3 -2
  3. package/dist/adw/index.js +21 -4
  4. package/dist/adw/index.js.map +1 -1
  5. package/dist/adw/widgets.generated.d.ts +1 -1
  6. package/dist/adw/widgets.generated.js +2 -2
  7. package/dist/adw/widgets.generated.js.map +1 -1
  8. package/dist/animated/create-animated.d.ts +2 -0
  9. package/dist/animated/create-animated.js +2 -0
  10. package/dist/animated/create-animated.js.map +1 -1
  11. package/dist/animated/event.d.ts +15 -0
  12. package/dist/animated/event.js +80 -0
  13. package/dist/animated/event.js.map +1 -0
  14. package/dist/animated/index.d.ts +4 -0
  15. package/dist/animated/index.js +8 -0
  16. package/dist/animated/index.js.map +1 -1
  17. package/dist/animated/spring.d.ts +10 -0
  18. package/dist/animated/spring.js +20 -6
  19. package/dist/animated/spring.js.map +1 -1
  20. package/dist/animated/timing.d.ts +10 -0
  21. package/dist/animated/timing.js +21 -13
  22. package/dist/animated/timing.js.map +1 -1
  23. package/dist/apis/host.gtkx.js +70 -11
  24. package/dist/apis/host.gtkx.js.map +1 -1
  25. package/dist/common/navigation-stack.d.ts +6 -3
  26. package/dist/common/navigation-stack.js +8 -1
  27. package/dist/common/navigation-stack.js.map +1 -1
  28. package/dist/common/widget.js +2 -11
  29. package/dist/common/widget.js.map +1 -1
  30. package/dist/components/animated.d.ts +1 -0
  31. package/dist/components/animated.js +28 -4
  32. package/dist/components/animated.js.map +1 -1
  33. package/dist/components/app-registry.js +32 -8
  34. package/dist/components/app-registry.js.map +1 -1
  35. package/dist/components/request-animation-frame.d.ts +33 -0
  36. package/dist/components/request-animation-frame.js +83 -0
  37. package/dist/components/request-animation-frame.js.map +1 -0
  38. package/dist/components/scroll-view.js +60 -2
  39. package/dist/components/scroll-view.js.map +1 -1
  40. package/dist/components/virtualized-list.js +1 -4
  41. package/dist/components/virtualized-list.js.map +1 -1
  42. package/dist/dnd/autoscroll.d.ts +44 -0
  43. package/dist/dnd/autoscroll.js +176 -0
  44. package/dist/dnd/autoscroll.js.map +1 -0
  45. package/dist/dnd/grid-order.d.ts +45 -0
  46. package/dist/dnd/grid-order.js +151 -0
  47. package/dist/dnd/grid-order.js.map +1 -0
  48. package/dist/dnd/grid.d.ts +44 -0
  49. package/dist/dnd/grid.js +397 -0
  50. package/dist/dnd/grid.js.map +1 -0
  51. package/dist/dnd/gtk-controllers.d.ts +16 -2
  52. package/dist/dnd/gtk-controllers.js +23 -3
  53. package/dist/dnd/gtk-controllers.js.map +1 -1
  54. package/dist/dnd/index.d.ts +4 -2
  55. package/dist/dnd/index.js +9 -2
  56. package/dist/dnd/index.js.map +1 -1
  57. package/dist/dnd/order-state.d.ts +14 -0
  58. package/dist/dnd/order-state.js +55 -0
  59. package/dist/dnd/order-state.js.map +1 -0
  60. package/dist/dnd/order.d.ts +19 -0
  61. package/dist/dnd/order.js +19 -0
  62. package/dist/dnd/order.js.map +1 -1
  63. package/dist/dnd/sortable.d.ts +29 -4
  64. package/dist/dnd/sortable.js +308 -115
  65. package/dist/dnd/sortable.js.map +1 -1
  66. package/dist/dnd/types.d.ts +236 -14
  67. package/dist/dnd/types.js +30 -0
  68. package/dist/dnd/types.js.map +1 -1
  69. package/dist/gesture-handler-compat/attach-context.d.ts +29 -0
  70. package/dist/gesture-handler-compat/attach-context.js +90 -0
  71. package/dist/gesture-handler-compat/attach-context.js.map +1 -0
  72. package/dist/gesture-handler-compat/detector-runtime.d.ts +14 -3
  73. package/dist/gesture-handler-compat/detector-runtime.js +15 -5
  74. package/dist/gesture-handler-compat/detector-runtime.js.map +1 -1
  75. package/dist/gesture-handler-compat/detector.js +57 -2
  76. package/dist/gesture-handler-compat/detector.js.map +1 -1
  77. package/dist/gesture-handler-compat/gesture-state-manager.d.ts +5 -0
  78. package/dist/gesture-handler-compat/gesture-state-manager.js +77 -0
  79. package/dist/gesture-handler-compat/gesture-state-manager.js.map +1 -0
  80. package/dist/gesture-handler-compat/index.d.ts +2 -2
  81. package/dist/gesture-handler-compat/index.js +38 -33
  82. package/dist/gesture-handler-compat/index.js.map +1 -1
  83. package/dist/gesture-handler-compat/recognizer.d.ts +12 -1
  84. package/dist/gesture-handler-compat/recognizer.js +23 -9
  85. package/dist/gesture-handler-compat/recognizer.js.map +1 -1
  86. package/dist/gesture-handler-compat/tag-registry.d.ts +14 -0
  87. package/dist/gesture-handler-compat/tag-registry.js +19 -0
  88. package/dist/gesture-handler-compat/tag-registry.js.map +1 -0
  89. package/dist/globals/index.d.ts +47 -0
  90. package/dist/globals/index.js +181 -0
  91. package/dist/globals/index.js.map +1 -0
  92. package/dist/globals/install.d.ts +1 -0
  93. package/dist/globals/install.js +14 -0
  94. package/dist/globals/install.js.map +1 -0
  95. package/dist/gtkx/bridge/adw-namespace.d.ts +2 -0
  96. package/dist/gtkx/bridge/adw-namespace.js +26 -0
  97. package/dist/gtkx/bridge/adw-namespace.js.map +1 -0
  98. package/dist/gtkx/bridge/adw.d.ts +23 -0
  99. package/dist/gtkx/bridge/adw.js +93 -0
  100. package/dist/gtkx/bridge/adw.js.map +1 -0
  101. package/dist/gtkx/bridge/color-scheme-parse.d.ts +11 -0
  102. package/dist/gtkx/bridge/color-scheme-parse.js +42 -0
  103. package/dist/gtkx/bridge/color-scheme-parse.js.map +1 -0
  104. package/dist/gtkx/bridge/color-scheme-portal.d.ts +24 -0
  105. package/dist/gtkx/bridge/color-scheme-portal.js +172 -0
  106. package/dist/gtkx/bridge/color-scheme-portal.js.map +1 -0
  107. package/dist/gtkx/bridge/core.d.ts +25 -0
  108. package/dist/gtkx/bridge/core.js +69 -0
  109. package/dist/gtkx/bridge/core.js.map +1 -0
  110. package/dist/gtkx/bridge/drag-layer.d.ts +27 -0
  111. package/dist/gtkx/bridge/drag-layer.js +177 -0
  112. package/dist/gtkx/bridge/drag-layer.js.map +1 -0
  113. package/dist/gtkx/bridge/index.d.ts +1 -27
  114. package/dist/gtkx/bridge/index.js +12 -71
  115. package/dist/gtkx/bridge/index.js.map +1 -1
  116. package/dist/gtkx/bridge/widgets.generated.adw.d.ts +1 -0
  117. package/dist/gtkx/bridge/widgets.generated.adw.js +16 -0
  118. package/dist/gtkx/bridge/widgets.generated.adw.js.map +1 -0
  119. package/dist/gtkx/bridge/widgets.generated.d.ts +0 -1
  120. package/dist/gtkx/bridge/widgets.generated.js +3 -3
  121. package/dist/gtkx/bridge/widgets.generated.js.map +1 -1
  122. package/dist/index.js +20 -0
  123. package/dist/index.js.map +1 -1
  124. package/dist/mcp/data/generated.d.ts +41 -20
  125. package/dist/mcp/data/generated.js +46 -20
  126. package/dist/mcp/data/generated.js.map +1 -1
  127. package/dist/metro/index.d.ts +1 -0
  128. package/dist/metro/index.js +12 -0
  129. package/dist/metro/index.js.map +1 -1
  130. package/dist/navigation/sidebar.js +2 -45
  131. package/dist/navigation/sidebar.js.map +1 -1
  132. package/dist/reanimated-compat/animatable-value.d.ts +158 -0
  133. package/dist/reanimated-compat/animatable-value.js +316 -0
  134. package/dist/reanimated-compat/animatable-value.js.map +1 -0
  135. package/dist/reanimated-compat/animation.d.ts +34 -13
  136. package/dist/reanimated-compat/animation.js +97 -25
  137. package/dist/reanimated-compat/animation.js.map +1 -1
  138. package/dist/reanimated-compat/mutable.js +6 -6
  139. package/dist/reanimated-compat/mutable.js.map +1 -1
  140. package/dist/reanimated-compat/updater-animations.js +32 -12
  141. package/dist/reanimated-compat/updater-animations.js.map +1 -1
  142. package/dist/runner/host-dev.js +5 -1
  143. package/dist/runner/host-dev.js.map +1 -1
  144. package/dist/runner/host.js +9 -1
  145. package/dist/runner/host.js.map +1 -1
  146. package/dist/vite/index.js +130 -0
  147. package/dist/vite/index.js.map +1 -1
  148. package/package.json +1 -1
  149. package/dist/gtkx/bridge/theme.d.ts +0 -4
  150. package/dist/gtkx/bridge/theme.js +0 -5
  151. package/dist/gtkx/bridge/theme.js.map +0 -1
@@ -0,0 +1,172 @@
1
+ // The plain-GTK profile's color-scheme source — see
2
+ // .claude/epics/adw-optional/004.md. Two mechanisms, same as the task:
3
+ //
4
+ // 1. org.freedesktop.appearance's "color-scheme" key on the XDG desktop
5
+ // portal (org.freedesktop.portal.Settings, over D-Bus) — the
6
+ // cross-desktop standard GNOME, KDE and others implement, with live
7
+ // updates via its SettingChanged signal. This is what makes the plain
8
+ // profile report the real system scheme on KDE too, not just GNOME.
9
+ // 2. Gtk.Settings' gtk-application-prefer-dark-theme when the portal never
10
+ // answers (no xdg-desktop-portal running at all, or it timed out) — the
11
+ // only other lever plain GTK has, and also the ONLY thing
12
+ // setPlainColorScheme can honestly write (see its own doc below).
13
+ //
14
+ // The parsing half (unwrapping the portal's double-boxed variant, mapping
15
+ // its uint32 onto "light"/"dark") lives in ./color-scheme-parse instead,
16
+ // which has no @gtkx/gi imports at all — this file is the GTK/D-Bus glue
17
+ // around it, and is NOT unit-testable on its own (constructing a real
18
+ // Gio.DBusProxy needs a real GTK/GLib runtime); see that file's own doc and
19
+ // tests/unit/gtkx/color-scheme-parse.test.ts for what actually is.
20
+ //
21
+ // Deliberately hand-rolled rather than reading GTK's own newer
22
+ // Gtk.Settings:gtk-interface-color-scheme (added in GTK 4.16, and itself
23
+ // backed by this exact same portal internally): that property does not
24
+ // exist before 4.16, silently raising this profile's real minimum past the
25
+ // GTK >= 4.10 baseline Alert's Gtk.AlertDialog fallback already commits to,
26
+ // and — because it is GTK's own opaque internal client — gives us nothing
27
+ // to unit-test if the portal itself can never be reached in this repo's
28
+ // headless test session.
29
+ import * as Gio from "@gtkx/gi/gio";
30
+ import * as GLib from "@gtkx/gi/glib";
31
+ import * as Gtk from "@gtkx/gi/gtk";
32
+ import { parsePortalReadReply, parseSettingChangedValue, } from "./color-scheme-parse";
33
+ const PORTAL_BUS_NAME = "org.freedesktop.portal.Desktop";
34
+ const PORTAL_OBJECT_PATH = "/org/freedesktop/portal/desktop";
35
+ const PORTAL_SETTINGS_INTERFACE = "org.freedesktop.portal.Settings";
36
+ const APPEARANCE_NAMESPACE = "org.freedesktop.appearance";
37
+ const COLOR_SCHEME_KEY = "color-scheme";
38
+ // Bounded so a session with no portal at all (this repo's own headless-sway
39
+ // GTK test session included — see color-scheme-portal.gtk.test.tsx in
40
+ // spike/plain-gtk) fails fast instead of riding D-Bus's own ~25s default
41
+ // method-call timeout. Generous next to a real portal's actual answer time
42
+ // (single-digit milliseconds locally in every manual check during this task).
43
+ const PORTAL_TIMEOUT_MSEC = 2000;
44
+ const settingsDefault = () => Gtk.Settings.getDefault();
45
+ const effectiveScheme = () => settingsDefault()?.gtkApplicationPreferDarkTheme ? "dark" : "light";
46
+ const applyToGtkSettings = (scheme) => {
47
+ const settings = settingsDefault();
48
+ if (settings) {
49
+ // Writing fires notify::gtk-application-prefer-dark-theme (a no-op,
50
+ // GObject-property-setter style, when the value does not actually
51
+ // change) — the ONE signal onPlainColorSchemeChange listens on, so this
52
+ // is also how both setPlainColorScheme and an incoming portal update
53
+ // reach subscribers, with no separate notify path to keep in sync.
54
+ settings.gtkApplicationPreferDarkTheme = scheme === "dark";
55
+ }
56
+ };
57
+ // null = "follow the system" (this profile's equivalent of Adw's
58
+ // ColorScheme.DEFAULT); "light"/"dark" = an explicit local override (this
59
+ // profile's equivalent of Adw's FORCE_LIGHT/FORCE_DARK) — same tri-state
60
+ // shape as AdwStyleManager's own, so getPlainColorScheme/
61
+ // setPlainColorScheme stay consistent with each other exactly the way
62
+ // AdwStyleManager already is (see host.gtkx.ts's setColorScheme/
63
+ // colorScheme(), which both go through the one manager instance).
64
+ let forced = null;
65
+ // The last value this process actually learned FROM the portal, kept even
66
+ // while forced !== null so un-forcing (setPlainColorScheme(null)) can
67
+ // re-apply it immediately rather than waiting for the next SettingChanged.
68
+ let lastPortalScheme = null;
69
+ const applySystemUpdate = (scheme) => {
70
+ lastPortalScheme = scheme;
71
+ if (forced === null) {
72
+ applyToGtkSettings(scheme);
73
+ }
74
+ };
75
+ let probeStarted = false;
76
+ // One-time, synchronous, memoized — mirrors gtkx/bridge/adw.ts's own
77
+ // probe-once cached shape. Synchronous (not a dynamic import/async probe
78
+ // like adw.ts's) because getPlainColorScheme must answer synchronously on
79
+ // every call per the Host contract (AppearanceHost.getColorScheme is not a
80
+ // Promise) — this is the one place that pays the D-Bus round trip, once,
81
+ // bounded by PORTAL_TIMEOUT_MSEC, the first time appearance is actually
82
+ // touched (not at module load, so an app that never reads/writes
83
+ // Appearance never pays for it at all).
84
+ const ensurePortalProbed = () => {
85
+ if (probeStarted) {
86
+ return;
87
+ }
88
+ probeStarted = true;
89
+ try {
90
+ const proxy = Gio.DBusProxy.newForBusSync(Gio.BusType.SESSION,
91
+ // Skips the constructor's own synchronous property-cache GetAll call
92
+ // (the Settings portal interface has none we care about) — without
93
+ // this, construction itself can block on bus activation before ever
94
+ // reaching the bounded callSync below.
95
+ Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, null, PORTAL_BUS_NAME, PORTAL_OBJECT_PATH, PORTAL_SETTINGS_INTERFACE, null);
96
+ const reply = proxy.callSync("Read", GLib.Variant.newTuple([
97
+ GLib.Variant.newString(APPEARANCE_NAMESPACE),
98
+ GLib.Variant.newString(COLOR_SCHEME_KEY),
99
+ ]), Gio.DBusCallFlags.NONE, PORTAL_TIMEOUT_MSEC, null);
100
+ applySystemUpdate(parsePortalReadReply(reply.getChildValue(0)));
101
+ proxy.on("g-signal", (_sender, signalName, parameters) => {
102
+ if (signalName !== "SettingChanged") {
103
+ return;
104
+ }
105
+ const namespace = parameters.getChildValue(0).getString()[0];
106
+ const key = parameters.getChildValue(1).getString()[0];
107
+ if (namespace !== APPEARANCE_NAMESPACE || key !== COLOR_SCHEME_KEY) {
108
+ return;
109
+ }
110
+ applySystemUpdate(parseSettingChangedValue(parameters.getChildValue(2)));
111
+ });
112
+ }
113
+ catch {
114
+ // No session bus, no portal owning org.freedesktop.portal.Desktop, or
115
+ // it didn't answer inside PORTAL_TIMEOUT_MSEC — Gtk.Settings' own
116
+ // current value (its GTK/theme default, absent anything else to set it)
117
+ // is the only honest source left. Not retried: matches adwAvailable()'s
118
+ // own probe-once verdict, and a portal that is not there at process
119
+ // start is not expected to appear mid-session either.
120
+ }
121
+ };
122
+ /** Always "light" | "dark" — see the module doc for how it is sourced. */
123
+ export const getPlainColorScheme = () => {
124
+ ensurePortalProbed();
125
+ return effectiveScheme();
126
+ };
127
+ /**
128
+ * The plain profile's setColorScheme. What Adw's setColorScheme forces is
129
+ * already only ever an app-local override — AdwStyleManager is per-process,
130
+ * never a system-wide write — so writing Gtk.Settings'
131
+ * gtk-application-prefer-dark-theme here (also strictly app-local: it never
132
+ * reaches outside this process either) is not a lesser feature, just the
133
+ * plain-GTK lever that does the same job. There is no honest way to make
134
+ * this profile change the DESKTOP's own preference — no portal write API
135
+ * exists (the portal is read + notify only, by design) — so "local-only" is
136
+ * the whole story, not a fallback for a system-wide write that almost
137
+ * worked.
138
+ *
139
+ * null ("follow the system") re-applies the last value actually observed
140
+ * from the portal, if any. With no portal reachable at all there is no
141
+ * other system source to revert to, so it is a deliberate no-op — staying
142
+ * at whatever the toggle already reads rather than guessing a default.
143
+ */
144
+ export const setPlainColorScheme = (scheme) => {
145
+ ensurePortalProbed();
146
+ forced = scheme;
147
+ applyToGtkSettings(scheme ?? lastPortalScheme ?? effectiveScheme());
148
+ };
149
+ let settingsListenerAttached = false;
150
+ const listeners = new Set();
151
+ const attachSettingsListener = () => {
152
+ if (settingsListenerAttached) {
153
+ return;
154
+ }
155
+ const settings = settingsDefault();
156
+ if (!settings) {
157
+ return;
158
+ }
159
+ settingsListenerAttached = true;
160
+ settings.on("notify::gtk-application-prefer-dark-theme", () => {
161
+ for (const listener of listeners) {
162
+ listener();
163
+ }
164
+ });
165
+ };
166
+ export const onPlainColorSchemeChange = (notify) => {
167
+ ensurePortalProbed();
168
+ attachSettingsListener();
169
+ listeners.add(notify);
170
+ return { remove: () => listeners.delete(notify) };
171
+ };
172
+ //# sourceMappingURL=color-scheme-portal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-scheme-portal.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/color-scheme-portal.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,uEAAuE;AACvE,EAAE;AACF,wEAAwE;AACxE,gEAAgE;AAChE,uEAAuE;AACvE,yEAAyE;AACzE,uEAAuE;AACvE,2EAA2E;AAC3E,2EAA2E;AAC3E,6DAA6D;AAC7D,qEAAqE;AACrE,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,yEAAyE;AACzE,sEAAsE;AACtE,4EAA4E;AAC5E,mEAAmE;AACnE,EAAE;AACF,+DAA+D;AAC/D,yEAAyE;AACzE,uEAAuE;AACvE,2EAA2E;AAC3E,4EAA4E;AAC5E,0EAA0E;AAC1E,wEAAwE;AACxE,yBAAyB;AACzB,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AACnC,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAA;AAEnC,OAAO,EACL,oBAAoB,EACpB,wBAAwB,GAEzB,MAAM,sBAAsB,CAAA;AAI7B,MAAM,eAAe,GAAG,gCAAgC,CAAA;AACxD,MAAM,kBAAkB,GAAG,iCAAiC,CAAA;AAC5D,MAAM,yBAAyB,GAAG,iCAAiC,CAAA;AACnE,MAAM,oBAAoB,GAAG,4BAA4B,CAAA;AACzD,MAAM,gBAAgB,GAAG,cAAc,CAAA;AAEvC,4EAA4E;AAC5E,sEAAsE;AACtE,yEAAyE;AACzE,2EAA2E;AAC3E,8EAA8E;AAC9E,MAAM,mBAAmB,GAAG,IAAI,CAAA;AAEhC,MAAM,eAAe,GAAG,GAAwB,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAA;AAE5E,MAAM,eAAe,GAAG,GAAgB,EAAE,CACxC,eAAe,EAAE,EAAE,6BAA6B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;AAErE,MAAM,kBAAkB,GAAG,CAAC,MAAmB,EAAQ,EAAE;IACvD,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,IAAI,QAAQ,EAAE,CAAC;QACb,oEAAoE;QACpE,kEAAkE;QAClE,wEAAwE;QACxE,qEAAqE;QACrE,mEAAmE;QACnE,QAAQ,CAAC,6BAA6B,GAAG,MAAM,KAAK,MAAM,CAAA;IAC5D,CAAC;AACH,CAAC,CAAA;AAED,iEAAiE;AACjE,0EAA0E;AAC1E,yEAAyE;AACzE,0DAA0D;AAC1D,sEAAsE;AACtE,iEAAiE;AACjE,kEAAkE;AAClE,IAAI,MAAM,GAAuB,IAAI,CAAA;AACrC,0EAA0E;AAC1E,sEAAsE;AACtE,2EAA2E;AAC3E,IAAI,gBAAgB,GAAuB,IAAI,CAAA;AAE/C,MAAM,iBAAiB,GAAG,CAAC,MAAmB,EAAQ,EAAE;IACtD,gBAAgB,GAAG,MAAM,CAAA;IACzB,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC,CAAA;AAED,IAAI,YAAY,GAAG,KAAK,CAAA;AAExB,qEAAqE;AACrE,yEAAyE;AACzE,0EAA0E;AAC1E,2EAA2E;AAC3E,yEAAyE;AACzE,wEAAwE;AACxE,iEAAiE;AACjE,wCAAwC;AACxC,MAAM,kBAAkB,GAAG,GAAS,EAAE;IACpC,IAAI,YAAY,EAAE,CAAC;QACjB,OAAM;IACR,CAAC;IACD,YAAY,GAAG,IAAI,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,aAAa,CACvC,GAAG,CAAC,OAAO,CAAC,OAAO;QACnB,qEAAqE;QACrE,mEAAmE;QACnE,oEAAoE;QACpE,uCAAuC;QACvC,GAAG,CAAC,cAAc,CAAC,sBAAsB,EACzC,IAAI,EACJ,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,IAAI,CACL,CAAA;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAC1B,MAAM,EACN,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;YACpB,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,oBAAoB,CAAC;YAC5C,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,gBAAgB,CAAC;SACzC,CAAC,EACF,GAAG,CAAC,aAAa,CAAC,IAAI,EACtB,mBAAmB,EACnB,IAAI,CACL,CAAA;QACD,iBAAiB,CAAC,oBAAoB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC/D,KAAK,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE;YACvD,IAAI,UAAU,KAAK,gBAAgB,EAAE,CAAC;gBACpC,OAAM;YACR,CAAC;YACD,MAAM,SAAS,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAA;YAC5D,MAAM,GAAG,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAA;YACtD,IAAI,SAAS,KAAK,oBAAoB,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;gBACnE,OAAM;YACR,CAAC;YACD,iBAAiB,CAAC,wBAAwB,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1E,CAAC,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,kEAAkE;QAClE,wEAAwE;QACxE,wEAAwE;QACxE,oEAAoE;QACpE,sDAAsD;IACxD,CAAC;AACH,CAAC,CAAA;AAED,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAgB,EAAE;IACnD,kBAAkB,EAAE,CAAA;IACpB,OAAO,eAAe,EAAE,CAAA;AAC1B,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA0B,EAAQ,EAAE;IACtE,kBAAkB,EAAE,CAAA;IACpB,MAAM,GAAG,MAAM,CAAA;IACf,kBAAkB,CAAC,MAAM,IAAI,gBAAgB,IAAI,eAAe,EAAE,CAAC,CAAA;AACrE,CAAC,CAAA;AAED,IAAI,wBAAwB,GAAG,KAAK,CAAA;AACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;AAEvC,MAAM,sBAAsB,GAAG,GAAS,EAAE;IACxC,IAAI,wBAAwB,EAAE,CAAC;QAC7B,OAAM;IACR,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAM;IACR,CAAC;IACD,wBAAwB,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QAC5D,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,QAAQ,EAAE,CAAA;QACZ,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,MAAkB,EACE,EAAE;IACtB,kBAAkB,EAAE,CAAA;IACpB,sBAAsB,EAAE,CAAA;IACxB,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAA;AACnD,CAAC,CAAA","sourcesContent":["// The plain-GTK profile's color-scheme source — see\n// .claude/epics/adw-optional/004.md. Two mechanisms, same as the task:\n//\n// 1. org.freedesktop.appearance's \"color-scheme\" key on the XDG desktop\n// portal (org.freedesktop.portal.Settings, over D-Bus) — the\n// cross-desktop standard GNOME, KDE and others implement, with live\n// updates via its SettingChanged signal. This is what makes the plain\n// profile report the real system scheme on KDE too, not just GNOME.\n// 2. Gtk.Settings' gtk-application-prefer-dark-theme when the portal never\n// answers (no xdg-desktop-portal running at all, or it timed out) — the\n// only other lever plain GTK has, and also the ONLY thing\n// setPlainColorScheme can honestly write (see its own doc below).\n//\n// The parsing half (unwrapping the portal's double-boxed variant, mapping\n// its uint32 onto \"light\"/\"dark\") lives in ./color-scheme-parse instead,\n// which has no @gtkx/gi imports at all — this file is the GTK/D-Bus glue\n// around it, and is NOT unit-testable on its own (constructing a real\n// Gio.DBusProxy needs a real GTK/GLib runtime); see that file's own doc and\n// tests/unit/gtkx/color-scheme-parse.test.ts for what actually is.\n//\n// Deliberately hand-rolled rather than reading GTK's own newer\n// Gtk.Settings:gtk-interface-color-scheme (added in GTK 4.16, and itself\n// backed by this exact same portal internally): that property does not\n// exist before 4.16, silently raising this profile's real minimum past the\n// GTK >= 4.10 baseline Alert's Gtk.AlertDialog fallback already commits to,\n// and — because it is GTK's own opaque internal client — gives us nothing\n// to unit-test if the portal itself can never be reached in this repo's\n// headless test session.\nimport * as Gio from \"@gtkx/gi/gio\"\nimport * as GLib from \"@gtkx/gi/glib\"\nimport * as Gtk from \"@gtkx/gi/gtk\"\nimport type { SubscriptionHandle } from \"../../contracts\"\nimport {\n parsePortalReadReply,\n parseSettingChangedValue,\n type ColorScheme,\n} from \"./color-scheme-parse\"\n\nexport type { ColorScheme }\n\nconst PORTAL_BUS_NAME = \"org.freedesktop.portal.Desktop\"\nconst PORTAL_OBJECT_PATH = \"/org/freedesktop/portal/desktop\"\nconst PORTAL_SETTINGS_INTERFACE = \"org.freedesktop.portal.Settings\"\nconst APPEARANCE_NAMESPACE = \"org.freedesktop.appearance\"\nconst COLOR_SCHEME_KEY = \"color-scheme\"\n\n// Bounded so a session with no portal at all (this repo's own headless-sway\n// GTK test session included — see color-scheme-portal.gtk.test.tsx in\n// spike/plain-gtk) fails fast instead of riding D-Bus's own ~25s default\n// method-call timeout. Generous next to a real portal's actual answer time\n// (single-digit milliseconds locally in every manual check during this task).\nconst PORTAL_TIMEOUT_MSEC = 2000\n\nconst settingsDefault = (): Gtk.Settings | null => Gtk.Settings.getDefault()\n\nconst effectiveScheme = (): ColorScheme =>\n settingsDefault()?.gtkApplicationPreferDarkTheme ? \"dark\" : \"light\"\n\nconst applyToGtkSettings = (scheme: ColorScheme): void => {\n const settings = settingsDefault()\n if (settings) {\n // Writing fires notify::gtk-application-prefer-dark-theme (a no-op,\n // GObject-property-setter style, when the value does not actually\n // change) — the ONE signal onPlainColorSchemeChange listens on, so this\n // is also how both setPlainColorScheme and an incoming portal update\n // reach subscribers, with no separate notify path to keep in sync.\n settings.gtkApplicationPreferDarkTheme = scheme === \"dark\"\n }\n}\n\n// null = \"follow the system\" (this profile's equivalent of Adw's\n// ColorScheme.DEFAULT); \"light\"/\"dark\" = an explicit local override (this\n// profile's equivalent of Adw's FORCE_LIGHT/FORCE_DARK) — same tri-state\n// shape as AdwStyleManager's own, so getPlainColorScheme/\n// setPlainColorScheme stay consistent with each other exactly the way\n// AdwStyleManager already is (see host.gtkx.ts's setColorScheme/\n// colorScheme(), which both go through the one manager instance).\nlet forced: ColorScheme | null = null\n// The last value this process actually learned FROM the portal, kept even\n// while forced !== null so un-forcing (setPlainColorScheme(null)) can\n// re-apply it immediately rather than waiting for the next SettingChanged.\nlet lastPortalScheme: ColorScheme | null = null\n\nconst applySystemUpdate = (scheme: ColorScheme): void => {\n lastPortalScheme = scheme\n if (forced === null) {\n applyToGtkSettings(scheme)\n }\n}\n\nlet probeStarted = false\n\n// One-time, synchronous, memoized — mirrors gtkx/bridge/adw.ts's own\n// probe-once cached shape. Synchronous (not a dynamic import/async probe\n// like adw.ts's) because getPlainColorScheme must answer synchronously on\n// every call per the Host contract (AppearanceHost.getColorScheme is not a\n// Promise) — this is the one place that pays the D-Bus round trip, once,\n// bounded by PORTAL_TIMEOUT_MSEC, the first time appearance is actually\n// touched (not at module load, so an app that never reads/writes\n// Appearance never pays for it at all).\nconst ensurePortalProbed = (): void => {\n if (probeStarted) {\n return\n }\n probeStarted = true\n try {\n const proxy = Gio.DBusProxy.newForBusSync(\n Gio.BusType.SESSION,\n // Skips the constructor's own synchronous property-cache GetAll call\n // (the Settings portal interface has none we care about) — without\n // this, construction itself can block on bus activation before ever\n // reaching the bounded callSync below.\n Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,\n null,\n PORTAL_BUS_NAME,\n PORTAL_OBJECT_PATH,\n PORTAL_SETTINGS_INTERFACE,\n null,\n )\n const reply = proxy.callSync(\n \"Read\",\n GLib.Variant.newTuple([\n GLib.Variant.newString(APPEARANCE_NAMESPACE),\n GLib.Variant.newString(COLOR_SCHEME_KEY),\n ]),\n Gio.DBusCallFlags.NONE,\n PORTAL_TIMEOUT_MSEC,\n null,\n )\n applySystemUpdate(parsePortalReadReply(reply.getChildValue(0)))\n proxy.on(\"g-signal\", (_sender, signalName, parameters) => {\n if (signalName !== \"SettingChanged\") {\n return\n }\n const namespace = parameters.getChildValue(0).getString()[0]\n const key = parameters.getChildValue(1).getString()[0]\n if (namespace !== APPEARANCE_NAMESPACE || key !== COLOR_SCHEME_KEY) {\n return\n }\n applySystemUpdate(parseSettingChangedValue(parameters.getChildValue(2)))\n })\n } catch {\n // No session bus, no portal owning org.freedesktop.portal.Desktop, or\n // it didn't answer inside PORTAL_TIMEOUT_MSEC — Gtk.Settings' own\n // current value (its GTK/theme default, absent anything else to set it)\n // is the only honest source left. Not retried: matches adwAvailable()'s\n // own probe-once verdict, and a portal that is not there at process\n // start is not expected to appear mid-session either.\n }\n}\n\n/** Always \"light\" | \"dark\" — see the module doc for how it is sourced. */\nexport const getPlainColorScheme = (): ColorScheme => {\n ensurePortalProbed()\n return effectiveScheme()\n}\n\n/**\n * The plain profile's setColorScheme. What Adw's setColorScheme forces is\n * already only ever an app-local override — AdwStyleManager is per-process,\n * never a system-wide write — so writing Gtk.Settings'\n * gtk-application-prefer-dark-theme here (also strictly app-local: it never\n * reaches outside this process either) is not a lesser feature, just the\n * plain-GTK lever that does the same job. There is no honest way to make\n * this profile change the DESKTOP's own preference — no portal write API\n * exists (the portal is read + notify only, by design) — so \"local-only\" is\n * the whole story, not a fallback for a system-wide write that almost\n * worked.\n *\n * null (\"follow the system\") re-applies the last value actually observed\n * from the portal, if any. With no portal reachable at all there is no\n * other system source to revert to, so it is a deliberate no-op — staying\n * at whatever the toggle already reads rather than guessing a default.\n */\nexport const setPlainColorScheme = (scheme: ColorScheme | null): void => {\n ensurePortalProbed()\n forced = scheme\n applyToGtkSettings(scheme ?? lastPortalScheme ?? effectiveScheme())\n}\n\nlet settingsListenerAttached = false\nconst listeners = new Set<() => void>()\n\nconst attachSettingsListener = (): void => {\n if (settingsListenerAttached) {\n return\n }\n const settings = settingsDefault()\n if (!settings) {\n return\n }\n settingsListenerAttached = true\n settings.on(\"notify::gtk-application-prefer-dark-theme\", () => {\n for (const listener of listeners) {\n listener()\n }\n })\n}\n\nexport const onPlainColorSchemeChange = (\n notify: () => void,\n): SubscriptionHandle => {\n ensurePortalProbed()\n attachSettingsListener()\n listeners.add(notify)\n return { remove: () => listeners.delete(notify) }\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import * as Gdk from "@gtkx/gi/gdk";
2
+ import * as Gio from "@gtkx/gi/gio";
3
+ import * as GLib from "@gtkx/gi/glib";
4
+ import * as GObject from "@gtkx/gi/gobject";
5
+ import * as Graphene from "@gtkx/gi/graphene";
6
+ import * as Gsk from "@gtkx/gi/gsk";
7
+ import * as Gtk from "@gtkx/gi/gtk";
8
+ import * as Pango from "@gtkx/gi/pango";
9
+ export { Gdk, Gio, GLib, GObject, Graphene, Gsk, Gtk, Pango };
10
+ export { GtkApplication, GtkGestureClick } from "@gtkx/jsx/gtk";
11
+ export { GMenu, GSimpleAction } from "@gtkx/jsx/gio";
12
+ export { GtkAdjustment, GtkDragSource, GtkDropControllerMotion, GtkDropTarget, GtkShortcut, GtkShortcutController, GtkTextBuffer, } from "@gtkx/jsx/gtk";
13
+ export * from "./widgets.generated";
14
+ export { createPortal, createRoot, quit, rootElement, useApplication, useBindSetting, useParentWindow, useProperty, useSetting, type Root, type RootElement, } from "@gtkx/react";
15
+ export type { SettingsSchema, SettingsSchemaKeys, SettingValue, } from "@gtkx/react/internal";
16
+ export { useSignal } from "./use-signal";
17
+ export { css, cx, injectGlobal } from "@gtkx/css";
18
+ export { createWidgetCss, type WidgetCss } from "./widget-css";
19
+ export { createTextProbe, measureWidget, toNumber } from "./measure";
20
+ export { computePointIn, computePointInWindow, type Point } from "./geometry";
21
+ export { beginDragLayer, type DragLayerHandle } from "./drag-layer";
22
+ export { createSlotPortal } from "./slot-portal";
23
+ export { cacheChildOrder, getViewBoxComponent, invalidateZOrder, setBoxOnly, setBoxPassthrough, setHitSlop, setPaintOnlyLeaf, setPointerTarget, setZIndex, type HitSlop, } from "./view-box";
24
+ 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";
25
+ export { allocateChild, attachRnLayout, detachRnLayout, queueAllocate, queueResize, type RnLayoutHooks, type RnLayoutOrientation, } from "./layout-manager";
@@ -0,0 +1,69 @@
1
+ // The core door onto gtkx: everything the RN core (components, APIs,
2
+ // gesture-handler, reanimated, dnd) needs, and NOTHING from @gtkx/gi/adw or
3
+ // @gtkx/jsx/adw — see .claude/epics/adw-optional/001.md. This is what makes
4
+ // `libraries: ["Gtk-4.0"]` alone buildable: an app that never declares
5
+ // "Adw-1" has no "./adw" entry in either package's codegen store at all, so
6
+ // a static import of either specifier fails the whole build regardless of
7
+ // whether it is ever reached at runtime. ./adw.ts is the one place that
8
+ // still reaches for them, behind a probe (adwAvailable()); its absence
9
+ // surfaces as a named throw, not a build failure.
10
+ //
11
+ // Caveats baked into this surface (catalogued in docs/gtkx-rc4-notes.md):
12
+ // - 64-bit FFI values arrive as BigInt → normalize with toNumber() at this boundary
13
+ // import-then-export (not `export * as`): the latter is the one syntax the
14
+ // stock @react-native/babel-preset cannot transform — this form keeps
15
+ // consumer apps on their unmodified RN Babel config.
16
+ import * as Gdk from "@gtkx/gi/gdk";
17
+ import * as Gio from "@gtkx/gi/gio";
18
+ import * as GLib from "@gtkx/gi/glib";
19
+ import * as GObject from "@gtkx/gi/gobject";
20
+ import * as Graphene from "@gtkx/gi/graphene";
21
+ import * as Gsk from "@gtkx/gi/gsk";
22
+ import * as Gtk from "@gtkx/gi/gtk";
23
+ import * as Pango from "@gtkx/gi/pango";
24
+ export { Gdk, Gio, GLib, GObject, Graphene, Gsk, Gtk, Pango };
25
+ // GtkApplication and GtkGestureClick are not Gtk.Widget subclasses (an
26
+ // application object and an event controller, respectively), so the widget
27
+ // surface generator never sees them — kept here by hand, same as before it
28
+ // existed.
29
+ export { GtkApplication, GtkGestureClick } from "@gtkx/jsx/gtk";
30
+ // Auxiliary gtkx JSX elements that are not Gtk.Widget subclasses either,
31
+ // same reason as the pair above — scripts/generate-widget-surface.ts only
32
+ // classifies widgets, so these fall into its "notAWidget" bucket (see
33
+ // scripts/widget-surface/classification.json) and are otherwise
34
+ // unreachable through react-native-gtkx/gtk. Every one of these is a real,
35
+ // necessary building block for a real app: actions and menus (Gio), a text
36
+ // buffer and an adjustment (the model objects GtkTextView/GtkSpinRow etc.
37
+ // bind to), keyboard shortcuts, and the two drag-and-drop controllers.
38
+ // Adwaita's own auxiliary elements (AdwBreakpoint, AdwToggle, the
39
+ // AdwShortcutsDialog leaves) live in ./adw instead.
40
+ export { GMenu, GSimpleAction } from "@gtkx/jsx/gio";
41
+ export { GtkAdjustment, GtkDragSource, GtkDropControllerMotion, GtkDropTarget, GtkShortcut, GtkShortcutController, GtkTextBuffer, } from "@gtkx/jsx/gtk";
42
+ // The full GTK widget surface — every GTK class gtkx binds that derives
43
+ // Gtk.Widget, raw. src/gtk/widgets.generated wraps most of them with
44
+ // wrapReactNative; this file is where @gtkx/jsx is still allowed to be
45
+ // imported directly (see the eslint carve-out for src/gtkx/bridge/**), so it
46
+ // pulls the raw widgets from here rather than from @gtkx/jsx itself.
47
+ // Adwaita's raw widgets are the sibling generated file, widgets.generated.adw.ts,
48
+ // exported only from ./adw — see that file's header for why the two cannot
49
+ // share one module.
50
+ export * from "./widgets.generated";
51
+ export { createPortal, createRoot, quit, rootElement, useApplication, useBindSetting, useParentWindow, useProperty, useSetting, } from "@gtkx/react";
52
+ // useSignal comes from ./use-signal, not @gtkx/react — see the workaround note
53
+ // there (rc.3 delivers a stale handler).
54
+ export { useSignal } from "./use-signal";
55
+ export { css, cx, injectGlobal } from "@gtkx/css";
56
+ // The imperative counterpart of `css`: a private provider on one widget,
57
+ // for values that change per frame and must not enter the shared stylesheet.
58
+ export { createWidgetCss } from "./widget-css";
59
+ export { createTextProbe, measureWidget, toNumber } from "./measure";
60
+ export { computePointIn, computePointInWindow } from "./geometry";
61
+ export { beginDragLayer } from "./drag-layer";
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";
66
+ export { cacheChildOrder, getViewBoxComponent, invalidateZOrder, setBoxOnly, setBoxPassthrough, setHitSlop, setPaintOnlyLeaf, setPointerTarget, setZIndex, } from "./view-box";
67
+ export { getSvgNodeComponent, getSvgNodeDescriptor, queueSvgRedraw, resolveSvgColor, resolveSvgPaint, setSvgNodeDescriptor, } from "./svg-node";
68
+ export { allocateChild, attachRnLayout, detachRnLayout, queueAllocate, queueResize, } from "./layout-manager";
69
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/core.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,4EAA4E;AAC5E,4EAA4E;AAC5E,uEAAuE;AACvE,4EAA4E;AAC5E,0EAA0E;AAC1E,wEAAwE;AACxE,uEAAuE;AACvE,kDAAkD;AAClD,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,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,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AAE7D,uEAAuE;AACvE,2EAA2E;AAC3E,2EAA2E;AAC3E,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/D,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,gEAAgE;AAChE,2EAA2E;AAC3E,2EAA2E;AAC3E,0EAA0E;AAC1E,uEAAuE;AACvE,kEAAkE;AAClE,oDAAoD;AACpD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EACL,aAAa,EACb,aAAa,EACb,uBAAuB,EACvB,aAAa,EACb,WAAW,EACX,qBAAqB,EACrB,aAAa,GACd,MAAM,eAAe,CAAA;AAEtB,wEAAwE;AACxE,qEAAqE;AACrE,uEAAuE;AACvE,6EAA6E;AAC7E,qEAAqE;AACrE,kFAAkF;AAClF,2EAA2E;AAC3E,oBAAoB;AACpB,cAAc,qBAAqB,CAAA;AAEnC,OAAO,EACL,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,WAAW,EACX,cAAc,EACd,cAAc,EACd,eAAe,EACf,WAAW,EACX,UAAU,GAGX,MAAM,aAAa,CAAA;AAcpB,+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,yEAAyE;AACzE,6EAA6E;AAC7E,OAAO,EAAE,eAAe,EAAkB,MAAM,cAAc,CAAA;AAE9D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAc,MAAM,YAAY,CAAA;AAC7E,OAAO,EAAE,cAAc,EAAwB,MAAM,cAAc,CAAA;AACnE,0EAA0E;AAC1E,0EAA0E;AAC1E,4DAA4D;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,GAEV,MAAM,YAAY,CAAA;AACnB,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","sourcesContent":["// The core door onto gtkx: everything the RN core (components, APIs,\n// gesture-handler, reanimated, dnd) needs, and NOTHING from @gtkx/gi/adw or\n// @gtkx/jsx/adw — see .claude/epics/adw-optional/001.md. This is what makes\n// `libraries: [\"Gtk-4.0\"]` alone buildable: an app that never declares\n// \"Adw-1\" has no \"./adw\" entry in either package's codegen store at all, so\n// a static import of either specifier fails the whole build regardless of\n// whether it is ever reached at runtime. ./adw.ts is the one place that\n// still reaches for them, behind a probe (adwAvailable()); its absence\n// surfaces as a named throw, not a build failure.\n//\n// Caveats baked into this surface (catalogued in docs/gtkx-rc4-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 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 { 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 subclasses either,\n// same reason as the pair above — scripts/generate-widget-surface.ts only\n// classifies widgets, so these fall into its \"notAWidget\" bucket (see\n// scripts/widget-surface/classification.json) and are otherwise\n// unreachable through react-native-gtkx/gtk. Every one of these is a real,\n// necessary building block for a real app: actions and menus (Gio), a text\n// buffer and an adjustment (the model objects GtkTextView/GtkSpinRow etc.\n// bind to), keyboard shortcuts, and the two drag-and-drop controllers.\n// Adwaita's own auxiliary elements (AdwBreakpoint, AdwToggle, the\n// AdwShortcutsDialog leaves) live in ./adw instead.\nexport { GMenu, GSimpleAction } from \"@gtkx/jsx/gio\"\nexport {\n GtkAdjustment,\n GtkDragSource,\n GtkDropControllerMotion,\n GtkDropTarget,\n GtkShortcut,\n GtkShortcutController,\n GtkTextBuffer,\n} from \"@gtkx/jsx/gtk\"\n\n// The full GTK widget surface — every GTK class gtkx binds that derives\n// Gtk.Widget, raw. src/gtk/widgets.generated wraps most of them with\n// wrapReactNative; this file is where @gtkx/jsx is still allowed to be\n// imported directly (see the eslint carve-out for src/gtkx/bridge/**), so it\n// pulls the raw widgets from here rather than from @gtkx/jsx itself.\n// Adwaita's raw widgets are the sibling generated file, widgets.generated.adw.ts,\n// exported only from ./adw — see that file's header for why the two cannot\n// share one module.\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} from \"@gtkx/react\"\n\n// rc.4 moved the settings-schema types off the public entry point into\n// /internal, with no replacement on `@gtkx/react` — `useSetting`/\n// `useBindSetting` are still public and still typed by them, so a consumer\n// that wants to name the type of a setting has no supported import for it.\n// We re-export them from here, which is the whole point of the bridge; the\n// ask to put them back is filed in docs/upstream-gtkx.md.\nexport type {\n SettingsSchema,\n SettingsSchemaKeys,\n SettingValue,\n} from \"@gtkx/react/internal\"\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\n// The imperative counterpart of `css`: a private provider on one widget,\n// for values that change per frame and must not enter the shared stylesheet.\nexport { createWidgetCss, type WidgetCss } from \"./widget-css\"\n\nexport { createTextProbe, measureWidget, toNumber } from \"./measure\"\nexport { computePointIn, computePointInWindow, type Point } from \"./geometry\"\nexport { beginDragLayer, type DragLayerHandle } from \"./drag-layer\"\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 {\n cacheChildOrder,\n getViewBoxComponent,\n invalidateZOrder,\n setBoxOnly,\n setBoxPassthrough,\n setHitSlop,\n setPaintOnlyLeaf,\n setPointerTarget,\n setZIndex,\n type HitSlop,\n} 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\"\n"]}
@@ -0,0 +1,27 @@
1
+ import * as Gtk from "@gtkx/gi/gtk";
2
+ export type DragLayerHandle = {
3
+ /** Called on every `GtkDropControllerMotion` "motion", already in the
4
+ * OVERLAY's own coordinates — no per-call translation, because the
5
+ * picture is that overlay's own child. Zero React involvement: a widget
6
+ * property write per axis, nothing else. */
7
+ move: (x: number, y: number) => void;
8
+ /** The pointer (re)entered this window mid-drag: reveal the ghost and
9
+ * place it before the next motion arrives. */
10
+ show: (x: number, y: number) => void;
11
+ /** The pointer left this window: nothing further tracks it here, so
12
+ * hide rather than leave it stuck at the edge. */
13
+ hide: () => void;
14
+ /** The drag ended (dropped or cancelled): remove the ghost and restore
15
+ * the original's opacity. */
16
+ end: () => void;
17
+ };
18
+ /**
19
+ * Starts the window-level ghost for a drag that just began on `widget`,
20
+ * grabbed at `(grabX, grabY)` in `widget`'s own coordinates — the same pair
21
+ * `GtkDragSource.onPrepare` already reports and already feeds `setIcon()`.
22
+ *
23
+ * Returns `null` when `widget` has no window yet (nothing to escape) or
24
+ * shares no transform with it (unrealized) — the same failure mode
25
+ * `computePointIn` documents for `measure()`.
26
+ */
27
+ export declare const beginDragLayer: (widget: Gtk.Widget, grabX: number, grabY: number) => DragLayerHandle | null;
@@ -0,0 +1,177 @@
1
+ // A window-level drag layer: a non-interactive `Gtk.Picture` positioned by
2
+ // pixel margins inside a `Gtk.Overlay` wrapped once around each window's real
3
+ // content, showing a live `Gtk.WidgetPaintable` of whatever is being dragged.
4
+ //
5
+ // Why this exists NEXT TO `GtkDragSource`'s own icon (gtk-controllers.tsx
6
+ // already sets one via `setIcon`): that icon is a compositor-owned surface —
7
+ // real, and already unclipped by this process's own widget tree, but also
8
+ // outside it, so nothing in this process can read its geometry or its
9
+ // pixels. A dragged item escaping an `overflow: hidden` ancestor needs a
10
+ // widget IN OUR OWN TREE to prove that with — geometry a test can measure,
11
+ // pixels a screenshot can read. This is that widget: a second, deliberately
12
+ // redundant representation, added at window level (an `Overlay` child, so no
13
+ // ancestor's clip reaches it) for as long as the drag is over this window,
14
+ // and removed the moment the drag ends. `setIcon`'s own icon is untouched —
15
+ // a drag that leaves this window for another still shows it.
16
+ //
17
+ // REPARENTING THE DRAGGED WIDGET ITSELF was tried first and refused: moving
18
+ // the real node into a different container re-triggers ITS OWN size
19
+ // negotiation under a parent with different constraints (measured: a
20
+ // 100×100 card came out 800×600), and an unmount mid-flight strands the
21
+ // widget outside the tree React still thinks it owns. A `WidgetPaintable`
22
+ // sidesteps both — it is a live VIEW of the widget, not the widget, so the
23
+ // original never moves and never leaves the component tree React manages.
24
+ //
25
+ // Ghosting the original is `Gtk.Widget.setOpacity()`, restored to whatever it
26
+ // was before (not hardcoded to 1), so this composes with an app's own
27
+ // animated or static opacity rather than fighting it. Because
28
+ // `Gtk.WidgetPaintable` renders the widget it observes LIVE, `setIcon`'s own
29
+ // compositor icon and this overlay copy dim along with the original — the
30
+ // three are one underlying render, not three independent ones. Documented in
31
+ // docs/api.md rather than fought: decoupling them would need a frozen
32
+ // texture snapshot taken before the fade, not a live paintable, and a frozen
33
+ // snapshot is not what the spike validated or what this measures.
34
+ import * as Gtk from "@gtkx/gi/gtk";
35
+ import { computePointIn } from "./geometry";
36
+ // Dim, not hidden: RN dnd libraries commonly leave the dragged row at
37
+ // reduced rather than zero opacity (e.g. `react-native-draggable-flatlist`'s
38
+ // `activeOpacity`), and a fully transparent original would take the ghost's
39
+ // own coupled render with it, per the note above.
40
+ const GHOST_OPACITY = 0.35;
41
+ const overlaysByRoot = new WeakMap();
42
+ const asAdwApplicationWindow = (root) => {
43
+ const candidate = root;
44
+ return typeof candidate.setContent === "function" &&
45
+ typeof candidate.getContent === "function"
46
+ ? candidate
47
+ : null;
48
+ };
49
+ const setWindowContent = (root, content) => {
50
+ const adwWindow = asAdwApplicationWindow(root);
51
+ if (adwWindow) {
52
+ adwWindow.setContent(content);
53
+ }
54
+ else {
55
+ ;
56
+ root.setChild(content);
57
+ }
58
+ };
59
+ /**
60
+ * Detaches the window's current content and hands it back, rather than
61
+ * leaving it parented. `Gtk.Overlay.setChild()` asserts its argument is
62
+ * unparented (or already its own child, or null) — it will not steal a
63
+ * widget from another parent — so the content has to be unparented BEFORE
64
+ * the overlay can adopt it, not after.
65
+ */
66
+ const detachWindowContent = (root) => {
67
+ const adwWindow = asAdwApplicationWindow(root);
68
+ const current = adwWindow
69
+ ? adwWindow.getContent()
70
+ : root.getChild();
71
+ if (current) {
72
+ setWindowContent(root, null);
73
+ }
74
+ return current;
75
+ };
76
+ /**
77
+ * The overlay for `root`, wrapping its real content the first time any drag
78
+ * asks for it and reused for the window's whole lifetime after that — a
79
+ * one-time structural change, not a per-drag one, because reparenting a
80
+ * whole app's content is not something to repeat every time a finger moves.
81
+ */
82
+ const getOverlayEntry = (root) => {
83
+ const existing = overlaysByRoot.get(root);
84
+ if (existing) {
85
+ return existing;
86
+ }
87
+ const current = detachWindowContent(root);
88
+ const overlay = Gtk.Overlay.new();
89
+ overlay.setChild(current);
90
+ setWindowContent(root, overlay);
91
+ const entry = { overlay, active: null };
92
+ overlaysByRoot.set(root, entry);
93
+ // One motion controller per WINDOW rather than per drag or per provider:
94
+ // GDK stops delivering to the drag source after `drag-begin` (the
95
+ // compositor owns the pointer from there), and `GtkDropControllerMotion`
96
+ // is the widget-level replacement `dnd/context.tsx` already relies on, at
97
+ // provider scope, for the same reason. This is that same technique at
98
+ // window scope, so the ghost tracks the pointer regardless of which
99
+ // `DropProvider` — if any — the drag started under.
100
+ const motion = Gtk.DropControllerMotion.new();
101
+ motion.connect("motion", (x, y) => {
102
+ entry.active?.move(x, y);
103
+ });
104
+ motion.connect("enter", (x, y) => {
105
+ entry.active?.show(x, y);
106
+ });
107
+ motion.connect("leave", () => {
108
+ entry.active?.hide();
109
+ });
110
+ overlay.addController(motion);
111
+ return entry;
112
+ };
113
+ /**
114
+ * Starts the window-level ghost for a drag that just began on `widget`,
115
+ * grabbed at `(grabX, grabY)` in `widget`'s own coordinates — the same pair
116
+ * `GtkDragSource.onPrepare` already reports and already feeds `setIcon()`.
117
+ *
118
+ * Returns `null` when `widget` has no window yet (nothing to escape) or
119
+ * shares no transform with it (unrealized) — the same failure mode
120
+ * `computePointIn` documents for `measure()`.
121
+ */
122
+ export const beginDragLayer = (widget, grabX, grabY) => {
123
+ const root = widget.getRoot();
124
+ if (!root) {
125
+ return null;
126
+ }
127
+ const entry = getOverlayEntry(root);
128
+ const origin = computePointIn(widget, entry.overlay, 0, 0);
129
+ const grab = computePointIn(widget, entry.overlay, grabX, grabY);
130
+ if (!origin || !grab) {
131
+ return null;
132
+ }
133
+ // Constant for the drag's lifetime: how far the pointer sits from the
134
+ // widget's own top-left, in the overlay's coordinate space. Every later
135
+ // motion subtracts it back off — the same arithmetic `DropProvider`'s
136
+ // `onDragging` already does for `tx`/`ty`, one level up.
137
+ const offsetX = grab.x - origin.x;
138
+ const offsetY = grab.y - origin.y;
139
+ const picture = Gtk.Picture.newForPaintable(Gtk.WidgetPaintable.new(widget));
140
+ picture.setCanTarget(false);
141
+ picture.setCanFocus(false);
142
+ picture.setHalign(Gtk.Align.START);
143
+ picture.setValign(Gtk.Align.START);
144
+ picture.setMarginStart(Math.round(origin.x));
145
+ picture.setMarginTop(Math.round(origin.y));
146
+ entry.overlay.addOverlay(picture);
147
+ // Neither should ever be true for a floating ghost, but both default open
148
+ // on a plain overlay child — set explicitly rather than trusted, since
149
+ // either one being true would let the ghost resize the window it is
150
+ // supposed to float over (measure) or vanish at its own edge (clip) the
151
+ // moment it demonstrates the fix this exists for.
152
+ entry.overlay.setMeasureOverlay(picture, false);
153
+ entry.overlay.setClipOverlay(picture, false);
154
+ const previousOpacity = widget.getOpacity();
155
+ widget.setOpacity(GHOST_OPACITY);
156
+ const handle = {
157
+ move: (x, y) => {
158
+ picture.setMarginStart(Math.round(x - offsetX));
159
+ picture.setMarginTop(Math.round(y - offsetY));
160
+ },
161
+ show: (x, y) => {
162
+ picture.setVisible(true);
163
+ handle.move(x, y);
164
+ },
165
+ hide: () => picture.setVisible(false),
166
+ end: () => {
167
+ entry.overlay.removeOverlay(picture);
168
+ widget.setOpacity(previousOpacity);
169
+ if (entry.active === handle) {
170
+ entry.active = null;
171
+ }
172
+ },
173
+ };
174
+ entry.active = handle;
175
+ return handle;
176
+ };
177
+ //# sourceMappingURL=drag-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drag-layer.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/drag-layer.ts"],"names":[],"mappings":"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","sourcesContent":["// A window-level drag layer: a non-interactive `Gtk.Picture` positioned by\n// pixel margins inside a `Gtk.Overlay` wrapped once around each window's real\n// content, showing a live `Gtk.WidgetPaintable` of whatever is being dragged.\n//\n// Why this exists NEXT TO `GtkDragSource`'s own icon (gtk-controllers.tsx\n// already sets one via `setIcon`): that icon is a compositor-owned surface —\n// real, and already unclipped by this process's own widget tree, but also\n// outside it, so nothing in this process can read its geometry or its\n// pixels. A dragged item escaping an `overflow: hidden` ancestor needs a\n// widget IN OUR OWN TREE to prove that with — geometry a test can measure,\n// pixels a screenshot can read. This is that widget: a second, deliberately\n// redundant representation, added at window level (an `Overlay` child, so no\n// ancestor's clip reaches it) for as long as the drag is over this window,\n// and removed the moment the drag ends. `setIcon`'s own icon is untouched —\n// a drag that leaves this window for another still shows it.\n//\n// REPARENTING THE DRAGGED WIDGET ITSELF was tried first and refused: moving\n// the real node into a different container re-triggers ITS OWN size\n// negotiation under a parent with different constraints (measured: a\n// 100×100 card came out 800×600), and an unmount mid-flight strands the\n// widget outside the tree React still thinks it owns. A `WidgetPaintable`\n// sidesteps both — it is a live VIEW of the widget, not the widget, so the\n// original never moves and never leaves the component tree React manages.\n//\n// Ghosting the original is `Gtk.Widget.setOpacity()`, restored to whatever it\n// was before (not hardcoded to 1), so this composes with an app's own\n// animated or static opacity rather than fighting it. Because\n// `Gtk.WidgetPaintable` renders the widget it observes LIVE, `setIcon`'s own\n// compositor icon and this overlay copy dim along with the original — the\n// three are one underlying render, not three independent ones. Documented in\n// docs/api.md rather than fought: decoupling them would need a frozen\n// texture snapshot taken before the fade, not a live paintable, and a frozen\n// snapshot is not what the spike validated or what this measures.\nimport * as Gtk from \"@gtkx/gi/gtk\"\nimport { computePointIn } from \"./geometry\"\n\n// Dim, not hidden: RN dnd libraries commonly leave the dragged row at\n// reduced rather than zero opacity (e.g. `react-native-draggable-flatlist`'s\n// `activeOpacity`), and a fully transparent original would take the ghost's\n// own coupled render with it, per the note above.\nconst GHOST_OPACITY = 0.35\n\nexport type DragLayerHandle = {\n /** Called on every `GtkDropControllerMotion` \"motion\", already in the\n * OVERLAY's own coordinates — no per-call translation, because the\n * picture is that overlay's own child. Zero React involvement: a widget\n * property write per axis, nothing else. */\n move: (x: number, y: number) => void\n /** The pointer (re)entered this window mid-drag: reveal the ghost and\n * place it before the next motion arrives. */\n show: (x: number, y: number) => void\n /** The pointer left this window: nothing further tracks it here, so\n * hide rather than leave it stuck at the edge. */\n hide: () => void\n /** The drag ended (dropped or cancelled): remove the ghost and restore\n * the original's opacity. */\n end: () => void\n}\n\ntype OverlayEntry = {\n overlay: Gtk.Overlay\n /** At most one drag is ever in flight per window — GDK serialises drag\n * sessions on a seat — so one slot, not a set. */\n active: DragLayerHandle | null\n}\n\nconst overlaysByRoot = new WeakMap<Gtk.Root, OverlayEntry>()\n\n// `Adw.ApplicationWindow` names its own slot \"content\" and manages Adwaita's\n// toolbar/toast chrome INSIDE the `Gtk.Window` child slot it inherits —\n// writing that slot directly would replace Adwaita's internal structure\n// instead of the app's content. Anything else `AppRegistry.tsx` builds\n// (`Gtk.ApplicationWindow`) is a plain `Gtk.Window`, whose own `child` slot\n// IS the app's content, so `getChild`/`setChild` is right for it.\n//\n// Told apart by DUCK TYPING (does it have setContent/getContent?) rather\n// than `instanceof Adw.ApplicationWindow`: this file backs the RN core's\n// drag-and-drop (react-native-gtkx/dnd), which must build without Adw-1 at\n// all (see .claude/epics/adw-optional/001.md) — `@gtkx/gi/adw` is not\n// something this module can import even just to compare against it. A\n// plain Gtk.Window/GtkApplicationWindow never has these methods, so the\n// check is exactly as precise as the instanceof it replaces.\ntype AdwApplicationWindowLike = {\n setContent: (content: Gtk.Widget | null) => void\n getContent: () => Gtk.Widget | null\n}\n\nconst asAdwApplicationWindow = (\n root: Gtk.Root,\n): AdwApplicationWindowLike | null => {\n const candidate = root as unknown as Partial<AdwApplicationWindowLike>\n return typeof candidate.setContent === \"function\" &&\n typeof candidate.getContent === \"function\"\n ? (candidate as AdwApplicationWindowLike)\n : null\n}\n\nconst setWindowContent = (root: Gtk.Root, content: Gtk.Widget | null): void => {\n const adwWindow = asAdwApplicationWindow(root)\n if (adwWindow) {\n adwWindow.setContent(content)\n } else {\n ;(root as unknown as Gtk.Window).setChild(content)\n }\n}\n\n/**\n * Detaches the window's current content and hands it back, rather than\n * leaving it parented. `Gtk.Overlay.setChild()` asserts its argument is\n * unparented (or already its own child, or null) — it will not steal a\n * widget from another parent — so the content has to be unparented BEFORE\n * the overlay can adopt it, not after.\n */\nconst detachWindowContent = (root: Gtk.Root): Gtk.Widget | null => {\n const adwWindow = asAdwApplicationWindow(root)\n const current = adwWindow\n ? adwWindow.getContent()\n : (root as unknown as Gtk.Window).getChild()\n if (current) {\n setWindowContent(root, null)\n }\n return current\n}\n\n/**\n * The overlay for `root`, wrapping its real content the first time any drag\n * asks for it and reused for the window's whole lifetime after that — a\n * one-time structural change, not a per-drag one, because reparenting a\n * whole app's content is not something to repeat every time a finger moves.\n */\nconst getOverlayEntry = (root: Gtk.Root): OverlayEntry => {\n const existing = overlaysByRoot.get(root)\n if (existing) {\n return existing\n }\n\n const current = detachWindowContent(root)\n const overlay = Gtk.Overlay.new()\n overlay.setChild(current)\n setWindowContent(root, overlay)\n\n const entry: OverlayEntry = { overlay, active: null }\n overlaysByRoot.set(root, entry)\n\n // One motion controller per WINDOW rather than per drag or per provider:\n // GDK stops delivering to the drag source after `drag-begin` (the\n // compositor owns the pointer from there), and `GtkDropControllerMotion`\n // is the widget-level replacement `dnd/context.tsx` already relies on, at\n // provider scope, for the same reason. This is that same technique at\n // window scope, so the ghost tracks the pointer regardless of which\n // `DropProvider` — if any — the drag started under.\n const motion = Gtk.DropControllerMotion.new()\n motion.connect(\"motion\", (x: number, y: number) => {\n entry.active?.move(x, y)\n })\n motion.connect(\"enter\", (x: number, y: number) => {\n entry.active?.show(x, y)\n })\n motion.connect(\"leave\", () => {\n entry.active?.hide()\n })\n overlay.addController(motion)\n\n return entry\n}\n\n/**\n * Starts the window-level ghost for a drag that just began on `widget`,\n * grabbed at `(grabX, grabY)` in `widget`'s own coordinates — the same pair\n * `GtkDragSource.onPrepare` already reports and already feeds `setIcon()`.\n *\n * Returns `null` when `widget` has no window yet (nothing to escape) or\n * shares no transform with it (unrealized) — the same failure mode\n * `computePointIn` documents for `measure()`.\n */\nexport const beginDragLayer = (\n widget: Gtk.Widget,\n grabX: number,\n grabY: number,\n): DragLayerHandle | null => {\n const root = widget.getRoot()\n if (!root) {\n return null\n }\n const entry = getOverlayEntry(root)\n\n const origin = computePointIn(widget, entry.overlay, 0, 0)\n const grab = computePointIn(widget, entry.overlay, grabX, grabY)\n if (!origin || !grab) {\n return null\n }\n // Constant for the drag's lifetime: how far the pointer sits from the\n // widget's own top-left, in the overlay's coordinate space. Every later\n // motion subtracts it back off — the same arithmetic `DropProvider`'s\n // `onDragging` already does for `tx`/`ty`, one level up.\n const offsetX = grab.x - origin.x\n const offsetY = grab.y - origin.y\n\n const picture = Gtk.Picture.newForPaintable(Gtk.WidgetPaintable.new(widget))\n picture.setCanTarget(false)\n picture.setCanFocus(false)\n picture.setHalign(Gtk.Align.START)\n picture.setValign(Gtk.Align.START)\n picture.setMarginStart(Math.round(origin.x))\n picture.setMarginTop(Math.round(origin.y))\n\n entry.overlay.addOverlay(picture)\n // Neither should ever be true for a floating ghost, but both default open\n // on a plain overlay child — set explicitly rather than trusted, since\n // either one being true would let the ghost resize the window it is\n // supposed to float over (measure) or vanish at its own edge (clip) the\n // moment it demonstrates the fix this exists for.\n entry.overlay.setMeasureOverlay(picture, false)\n entry.overlay.setClipOverlay(picture, false)\n\n const previousOpacity = widget.getOpacity()\n widget.setOpacity(GHOST_OPACITY)\n\n const handle: DragLayerHandle = {\n move: (x, y) => {\n picture.setMarginStart(Math.round(x - offsetX))\n picture.setMarginTop(Math.round(y - offsetY))\n },\n show: (x, y) => {\n picture.setVisible(true)\n handle.move(x, y)\n },\n hide: () => picture.setVisible(false),\n end: () => {\n entry.overlay.removeOverlay(picture)\n widget.setOpacity(previousOpacity)\n if (entry.active === handle) {\n entry.active = null\n }\n },\n }\n entry.active = handle\n return handle\n}\n"]}
@@ -1,27 +1 @@
1
- import * as Adw from "@gtkx/gi/adw";
2
- import * as Gdk from "@gtkx/gi/gdk";
3
- import * as Gio from "@gtkx/gi/gio";
4
- import * as GLib from "@gtkx/gi/glib";
5
- import * as GObject from "@gtkx/gi/gobject";
6
- import * as Graphene from "@gtkx/gi/graphene";
7
- import * as Gsk from "@gtkx/gi/gsk";
8
- import * as Gtk from "@gtkx/gi/gtk";
9
- import * as Pango from "@gtkx/gi/pango";
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, GtkDropControllerMotion, 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, } from "@gtkx/react";
17
- export type { SettingsSchema, SettingsSchemaKeys, SettingValue, } from "@gtkx/react/internal";
18
- export { useSignal } from "./use-signal";
19
- export { css, cx, injectGlobal } from "@gtkx/css";
20
- export { createWidgetCss, type WidgetCss } from "./widget-css";
21
- export { createTextProbe, measureWidget, toNumber } from "./measure";
22
- export { computePointIn, computePointInWindow, type Point } from "./geometry";
23
- export { createSlotPortal } from "./slot-portal";
24
- export { cacheChildOrder, getViewBoxComponent, invalidateZOrder, setBoxOnly, setBoxPassthrough, setHitSlop, setPaintOnlyLeaf, setPointerTarget, setZIndex, type HitSlop, } from "./view-box";
25
- 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";
26
- export { allocateChild, attachRnLayout, detachRnLayout, queueAllocate, queueResize, type RnLayoutHooks, type RnLayoutOrientation, } from "./layout-manager";
27
- export { colorScheme, styleManager } from "./theme";
1
+ export * from "./core";