@gjsify/example-dom-three-geometry-teapot 0.27.1 → 0.29.0
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/dist/gjs.js +18 -18
- package/dist/gjs.node.mjs +20 -20
- package/package.json +9 -8
- package/src/assets/pisa/nx.png +0 -0
- package/src/assets/pisa/ny.png +0 -0
- package/src/assets/pisa/nz.png +0 -0
- package/src/assets/pisa/px.png +0 -0
- package/src/assets/pisa/py.png +0 -0
- package/src/assets/pisa/pz.png +0 -0
- package/src/assets/uv_grid_opengl.jpg +0 -0
- package/src/browser/browser-main.ts +4 -0
- package/src/browser/browser.ts +284 -0
- package/src/browser/index.html +11 -0
- package/src/browser/webgl.css +53 -0
- package/src/gjs/gjs.ts +27 -0
- package/src/gjs/teapot-window.blp +111 -0
- package/src/gjs/teapot-window.ts +145 -0
- package/src/three-demo.ts +242 -0
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/example-dom-three-geometry-teapot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Three.js geometry teapot example ported from refs/three/examples/webgl_geometry_teapot.html with Adwaita controls",
|
|
5
5
|
"main": "dist/gjs.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
|
-
"dist"
|
|
8
|
+
"dist",
|
|
9
|
+
"src"
|
|
9
10
|
],
|
|
10
11
|
"exports": {
|
|
11
12
|
"./browser": "./src/browser/browser.ts",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"build:assets": "gjsify copy src/browser/index.html src/browser/webgl.css src/assets dist/"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@gjsify/webgl": "^0.
|
|
30
|
+
"@gjsify/webgl": "^0.29.0"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@girs/adw-1": "^4.1.0",
|
|
@@ -34,12 +35,12 @@
|
|
|
34
35
|
"@girs/gjs": "^4.1.0",
|
|
35
36
|
"@girs/gobject-2.0": "^4.1.0",
|
|
36
37
|
"@girs/gtk-4.0": "^4.1.0",
|
|
37
|
-
"@gjsify/adwaita-icons": "^0.
|
|
38
|
-
"@gjsify/adwaita-web": "^0.
|
|
39
|
-
"@gjsify/cli": "^0.
|
|
40
|
-
"@gjsify/devtools": "^0.
|
|
38
|
+
"@gjsify/adwaita-icons": "^0.29.0",
|
|
39
|
+
"@gjsify/adwaita-web": "^0.29.0",
|
|
40
|
+
"@gjsify/cli": "^0.29.0",
|
|
41
|
+
"@gjsify/devtools": "^0.29.0",
|
|
41
42
|
"@gjsify/node-gi": "file:../../../packages/node-gi/node-gi",
|
|
42
|
-
"@gjsify/vite-plugin-blueprint": "^0.
|
|
43
|
+
"@gjsify/vite-plugin-blueprint": "^0.29.0",
|
|
43
44
|
"@types/node": "^25.9.2",
|
|
44
45
|
"@types/three": "^0.185.0",
|
|
45
46
|
"http-server": "^14.1.1",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// Browser UI for three-geometry-teapot example.
|
|
2
|
+
// Mirrors the GJS/Adwaita UI from gjs/teapot-window.ts using @gjsify/adwaita-web.
|
|
3
|
+
|
|
4
|
+
import '@gjsify/adwaita-web'; // registers the custom elements + self-injects the stylesheet
|
|
5
|
+
import type { AdwOverlaySplitView, AdwHeaderBar } from '@gjsify/adwaita-web';
|
|
6
|
+
import { mediaPlaybackPauseSymbolic, mediaPlaybackStartSymbolic } from '@gjsify/adwaita-icons/actions';
|
|
7
|
+
import {
|
|
8
|
+
start,
|
|
9
|
+
TESS_VALUES,
|
|
10
|
+
SHADING_VALUES,
|
|
11
|
+
DEFAULT_TESS_INDEX,
|
|
12
|
+
DEFAULT_SHADING_INDEX,
|
|
13
|
+
type TeapotDemo,
|
|
14
|
+
} from '../three-demo.js';
|
|
15
|
+
|
|
16
|
+
export interface MountOptions {
|
|
17
|
+
/** Base path for loading texture assets (forwarded to three-demo). */
|
|
18
|
+
assetBase?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Handle returned by `mount()` so hosts (e.g. the website slideshow) can pause and resume rendering. */
|
|
22
|
+
export interface ShowcaseHandle {
|
|
23
|
+
pause(): void;
|
|
24
|
+
resume(): void;
|
|
25
|
+
readonly isPaused: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Parse a trusted literal SVG string into an SVGElement. */
|
|
29
|
+
function parseSvg(svgSource: string): SVGElement {
|
|
30
|
+
const doc = new DOMParser().parseFromString(svgSource, 'image/svg+xml');
|
|
31
|
+
return doc.documentElement as unknown as SVGElement;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Replace a button's icon with a freshly-parsed copy of the given SVG source. */
|
|
35
|
+
function setButtonIcon(btn: HTMLButtonElement, svgSource: string): void {
|
|
36
|
+
btn.replaceChildren(parseSvg(svgSource));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create the Adwaita teapot UI and mount it into the given container.
|
|
41
|
+
* The container receives an adw-window with sidebar controls and a WebGL canvas.
|
|
42
|
+
*/
|
|
43
|
+
export function mount(container: HTMLElement, options?: MountOptions): ShowcaseHandle {
|
|
44
|
+
// Build UI — mirrors GJS Blueprint structure
|
|
45
|
+
const win = document.createElement('adw-window');
|
|
46
|
+
win.setAttribute('width', '1100');
|
|
47
|
+
win.setAttribute('height', '700');
|
|
48
|
+
|
|
49
|
+
// Header bar (toggle button added after DOM connection below)
|
|
50
|
+
const headerBar = document.createElement('adw-header-bar') as AdwHeaderBar;
|
|
51
|
+
headerBar.setAttribute('title', 'Three.js Teapot');
|
|
52
|
+
|
|
53
|
+
// Sidebar toggle button
|
|
54
|
+
const toggleBtn = document.createElement('button');
|
|
55
|
+
toggleBtn.className = 'adw-header-btn adw-sidebar-toggle-icon active';
|
|
56
|
+
toggleBtn.title = 'Toggle Sidebar';
|
|
57
|
+
|
|
58
|
+
// Pause/Resume rendering button — placed in header bar end section.
|
|
59
|
+
const pauseBtn = document.createElement('button');
|
|
60
|
+
pauseBtn.className = 'adw-header-btn';
|
|
61
|
+
pauseBtn.title = 'Pause Rendering';
|
|
62
|
+
setButtonIcon(pauseBtn, mediaPlaybackPauseSymbolic);
|
|
63
|
+
|
|
64
|
+
// OverlaySplitView — sidebar + content
|
|
65
|
+
const splitView = document.createElement('adw-overlay-split-view') as AdwOverlaySplitView;
|
|
66
|
+
splitView.setAttribute('min-sidebar-width', '280');
|
|
67
|
+
splitView.setAttribute('max-sidebar-width', '400');
|
|
68
|
+
splitView.setAttribute('sidebar-width-fraction', '0.30');
|
|
69
|
+
splitView.setAttribute('show-sidebar', '');
|
|
70
|
+
|
|
71
|
+
// Sidebar content
|
|
72
|
+
const sidebarContent = document.createElement('div');
|
|
73
|
+
sidebarContent.setAttribute('slot', 'sidebar');
|
|
74
|
+
sidebarContent.className = 'adw-sidebar-content';
|
|
75
|
+
|
|
76
|
+
// Geometry group
|
|
77
|
+
const geoGroup = document.createElement('adw-preferences-group');
|
|
78
|
+
geoGroup.setAttribute('title', 'Geometry');
|
|
79
|
+
|
|
80
|
+
const tessRow = document.createElement('adw-combo-row');
|
|
81
|
+
tessRow.setAttribute('title', 'Tessellation Level');
|
|
82
|
+
tessRow.setAttribute('items', JSON.stringify(TESS_VALUES.map(String)));
|
|
83
|
+
tessRow.setAttribute('selected', String(DEFAULT_TESS_INDEX));
|
|
84
|
+
|
|
85
|
+
const lidRow = document.createElement('adw-switch-row');
|
|
86
|
+
lidRow.setAttribute('title', 'Display Lid');
|
|
87
|
+
lidRow.setAttribute('active', '');
|
|
88
|
+
|
|
89
|
+
const bodyRow = document.createElement('adw-switch-row');
|
|
90
|
+
bodyRow.setAttribute('title', 'Display Body');
|
|
91
|
+
bodyRow.setAttribute('active', '');
|
|
92
|
+
|
|
93
|
+
const bottomRow = document.createElement('adw-switch-row');
|
|
94
|
+
bottomRow.setAttribute('title', 'Display Bottom');
|
|
95
|
+
bottomRow.setAttribute('active', '');
|
|
96
|
+
|
|
97
|
+
const fitLidRow = document.createElement('adw-switch-row');
|
|
98
|
+
fitLidRow.setAttribute('title', 'Snug Lid');
|
|
99
|
+
|
|
100
|
+
const nonblinnRow = document.createElement('adw-switch-row');
|
|
101
|
+
nonblinnRow.setAttribute('title', 'Original Scale');
|
|
102
|
+
|
|
103
|
+
geoGroup.append(tessRow, lidRow, bodyRow, bottomRow, fitLidRow, nonblinnRow);
|
|
104
|
+
|
|
105
|
+
// Material group
|
|
106
|
+
const matGroup = document.createElement('adw-preferences-group');
|
|
107
|
+
matGroup.setAttribute('title', 'Material');
|
|
108
|
+
|
|
109
|
+
const shadingRow = document.createElement('adw-combo-row');
|
|
110
|
+
shadingRow.setAttribute('title', 'Shading');
|
|
111
|
+
shadingRow.setAttribute('items', JSON.stringify([...SHADING_VALUES]));
|
|
112
|
+
shadingRow.setAttribute('selected', String(DEFAULT_SHADING_INDEX));
|
|
113
|
+
|
|
114
|
+
matGroup.append(shadingRow);
|
|
115
|
+
|
|
116
|
+
sidebarContent.append(geoGroup, matGroup);
|
|
117
|
+
|
|
118
|
+
// GL container (content slot) — inline styles so the showcase
|
|
119
|
+
// is self-contained and works regardless of host CSS.
|
|
120
|
+
const glContainer = document.createElement('div');
|
|
121
|
+
glContainer.setAttribute('slot', 'content');
|
|
122
|
+
glContainer.id = 'gl-area-container';
|
|
123
|
+
glContainer.style.cssText = 'flex:1;position:relative;min-width:0;min-height:0';
|
|
124
|
+
|
|
125
|
+
const canvas = document.createElement('canvas');
|
|
126
|
+
canvas.id = 'webgl-canvas';
|
|
127
|
+
canvas.style.cssText = 'display:block;width:100%;height:100%;position:absolute;inset:0';
|
|
128
|
+
glContainer.append(canvas);
|
|
129
|
+
|
|
130
|
+
splitView.append(sidebarContent, glContainer);
|
|
131
|
+
win.append(headerBar, splitView);
|
|
132
|
+
container.append(win);
|
|
133
|
+
|
|
134
|
+
// Append toggle button to header bar start section AFTER DOM connection
|
|
135
|
+
const startSection = headerBar.startSection ?? headerBar.querySelector('.adw-header-bar-start');
|
|
136
|
+
if (startSection) {
|
|
137
|
+
startSection.appendChild(toggleBtn);
|
|
138
|
+
} else {
|
|
139
|
+
headerBar.prepend(toggleBtn);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const endSection = headerBar.endSection ?? headerBar.querySelector('.adw-header-bar-end');
|
|
143
|
+
if (endSection) {
|
|
144
|
+
endSection.appendChild(pauseBtn);
|
|
145
|
+
} else {
|
|
146
|
+
headerBar.append(pauseBtn);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Sidebar toggle wiring
|
|
150
|
+
toggleBtn.addEventListener('click', () => {
|
|
151
|
+
splitView.toggleSidebar();
|
|
152
|
+
toggleBtn.classList.toggle('active', splitView.showSidebar);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
splitView.addEventListener('sidebar-toggled', () => {
|
|
156
|
+
toggleBtn.classList.toggle('active', splitView.showSidebar);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Responsive breakpoints — mirror GJS Adw.Breakpoint behavior
|
|
160
|
+
let lastCollapsed: boolean | null = null;
|
|
161
|
+
new ResizeObserver(([entry]) => {
|
|
162
|
+
const width = entry.contentRect.width;
|
|
163
|
+
const shouldCollapse = width < 800;
|
|
164
|
+
if (shouldCollapse === lastCollapsed) return;
|
|
165
|
+
lastCollapsed = shouldCollapse;
|
|
166
|
+
splitView.collapsed = shouldCollapse;
|
|
167
|
+
splitView.showSidebar = !shouldCollapse;
|
|
168
|
+
toggleBtn.classList.toggle('active', !shouldCollapse);
|
|
169
|
+
}).observe(win);
|
|
170
|
+
|
|
171
|
+
// Sync canvas size with container and re-render on resize
|
|
172
|
+
// (needed when slide becomes visible after being display:none)
|
|
173
|
+
canvas.width = glContainer.clientWidth;
|
|
174
|
+
canvas.height = glContainer.clientHeight;
|
|
175
|
+
|
|
176
|
+
let demo: TeapotDemo | null = null;
|
|
177
|
+
// Buffers pause() calls that arrive before the demo exists.
|
|
178
|
+
let pendingPause = false;
|
|
179
|
+
new ResizeObserver(() => {
|
|
180
|
+
const w = glContainer.clientWidth;
|
|
181
|
+
const h = glContainer.clientHeight;
|
|
182
|
+
if (w > 0 && h > 0) {
|
|
183
|
+
canvas.width = w;
|
|
184
|
+
canvas.height = h;
|
|
185
|
+
if (!demo) {
|
|
186
|
+
demo = start(canvas, { assetBase: options?.assetBase });
|
|
187
|
+
connectControls(
|
|
188
|
+
demo,
|
|
189
|
+
tessRow as AdwRow,
|
|
190
|
+
shadingRow as AdwRow,
|
|
191
|
+
lidRow as AdwRow,
|
|
192
|
+
bodyRow as AdwRow,
|
|
193
|
+
bottomRow as AdwRow,
|
|
194
|
+
fitLidRow as AdwRow,
|
|
195
|
+
nonblinnRow as AdwRow,
|
|
196
|
+
);
|
|
197
|
+
if (pendingPause) {
|
|
198
|
+
demo.pause();
|
|
199
|
+
pendingPause = false;
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
demo.render();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}).observe(glContainer);
|
|
206
|
+
|
|
207
|
+
// Pause button wiring — toggles demo state and swaps the icon.
|
|
208
|
+
function updatePauseButton(paused: boolean): void {
|
|
209
|
+
setButtonIcon(pauseBtn, paused ? mediaPlaybackStartSymbolic : mediaPlaybackPauseSymbolic);
|
|
210
|
+
pauseBtn.title = paused ? 'Resume Rendering' : 'Pause Rendering';
|
|
211
|
+
}
|
|
212
|
+
pauseBtn.addEventListener('click', () => {
|
|
213
|
+
if (demo) {
|
|
214
|
+
if (demo.isPaused) demo.resume();
|
|
215
|
+
else demo.pause();
|
|
216
|
+
updatePauseButton(demo.isPaused);
|
|
217
|
+
} else {
|
|
218
|
+
pendingPause = !pendingPause;
|
|
219
|
+
updatePauseButton(pendingPause);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
get isPaused() {
|
|
225
|
+
return demo ? demo.isPaused : pendingPause;
|
|
226
|
+
},
|
|
227
|
+
pause() {
|
|
228
|
+
if (demo) {
|
|
229
|
+
demo.pause();
|
|
230
|
+
updatePauseButton(true);
|
|
231
|
+
} else {
|
|
232
|
+
pendingPause = true;
|
|
233
|
+
updatePauseButton(true);
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
resume() {
|
|
237
|
+
if (demo) {
|
|
238
|
+
demo.resume();
|
|
239
|
+
updatePauseButton(false);
|
|
240
|
+
} else {
|
|
241
|
+
pendingPause = false;
|
|
242
|
+
updatePauseButton(false);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Adwaita web components expose custom properties (.selected, .active) not in HTMLElement types.
|
|
249
|
+
type AdwRow = HTMLElement & Record<string, unknown>;
|
|
250
|
+
|
|
251
|
+
function connectControls(
|
|
252
|
+
demo: TeapotDemo,
|
|
253
|
+
tessRow: AdwRow,
|
|
254
|
+
shadingRow: AdwRow,
|
|
255
|
+
lidRow: AdwRow,
|
|
256
|
+
bodyRow: AdwRow,
|
|
257
|
+
bottomRow: AdwRow,
|
|
258
|
+
fitLidRow: AdwRow,
|
|
259
|
+
nonblinnRow: AdwRow,
|
|
260
|
+
) {
|
|
261
|
+
tessRow.addEventListener('notify::selected', () => {
|
|
262
|
+
demo.effectController.newTess = TESS_VALUES[tessRow.selected as number];
|
|
263
|
+
demo.render();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
shadingRow.addEventListener('notify::selected', () => {
|
|
267
|
+
demo.effectController.newShading = SHADING_VALUES[shadingRow.selected as number];
|
|
268
|
+
demo.render();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const toggleRows: Array<[AdwRow, 'lid' | 'body' | 'bottom' | 'fitLid' | 'nonblinn']> = [
|
|
272
|
+
[lidRow, 'lid'],
|
|
273
|
+
[bodyRow, 'body'],
|
|
274
|
+
[bottomRow, 'bottom'],
|
|
275
|
+
[fitLidRow, 'fitLid'],
|
|
276
|
+
[nonblinnRow, 'nonblinn'],
|
|
277
|
+
];
|
|
278
|
+
for (const [row, key] of toggleRows) {
|
|
279
|
+
row.addEventListener('notify::active', () => {
|
|
280
|
+
demo.effectController[key] = Boolean(row.active);
|
|
281
|
+
demo.render();
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Three.js Teapot — gjsify</title>
|
|
6
|
+
<link rel="stylesheet" href="./webgl.css" type="text/css" />
|
|
7
|
+
<link rel="stylesheet" href="./browser.css" type="text/css" />
|
|
8
|
+
<script src="./browser.js" type="module" defer></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body></body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
html, body {
|
|
8
|
+
width: 100%;
|
|
9
|
+
height: 100%;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
background: #2d2d2d;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
adw-window {
|
|
18
|
+
max-width: 100%;
|
|
19
|
+
max-height: 100%;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.adw-window-body {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex: 1;
|
|
25
|
+
min-height: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.adw-sidebar {
|
|
29
|
+
width: 320px;
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.adw-sidebar-content {
|
|
35
|
+
padding: 12px;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
gap: 12px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#gl-area-container {
|
|
42
|
+
flex: 1;
|
|
43
|
+
position: relative;
|
|
44
|
+
min-width: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#gl-area-container canvas {
|
|
48
|
+
display: block;
|
|
49
|
+
width: 100%;
|
|
50
|
+
height: 100%;
|
|
51
|
+
position: absolute;
|
|
52
|
+
inset: 0;
|
|
53
|
+
}
|
package/src/gjs/gjs.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// GJS/Adwaita entry point for three-geometry-teapot example.
|
|
2
|
+
// Ported from refs/three/examples/webgl_geometry_teapot.html
|
|
3
|
+
|
|
4
|
+
import Adw from 'gi://Adw?version=1';
|
|
5
|
+
import Gio from 'gi://Gio?version=2.0';
|
|
6
|
+
|
|
7
|
+
import { installDevtools } from '@gjsify/devtools';
|
|
8
|
+
import { TeapotWindow } from './teapot-window.js';
|
|
9
|
+
|
|
10
|
+
const app = new Adw.Application({
|
|
11
|
+
application_id: 'gjsify.examples.three-geometry-teapot',
|
|
12
|
+
flags: Gio.ApplicationFlags.FLAGS_NONE,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
app.connect('startup', () => installDevtools(app));
|
|
16
|
+
|
|
17
|
+
app.connect('activate', () => {
|
|
18
|
+
let win = app.get_active_window();
|
|
19
|
+
if (!win) win = new TeapotWindow(app);
|
|
20
|
+
win.present();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// runAsync (NOT the sync run()): defers the blocking main loop to a macrotask so
|
|
24
|
+
// the synchronous view load doesn't hang on its spinner, and it is the required
|
|
25
|
+
// lifecycle on the `--app node` reverse bridge (a sync run() deadlocks there) as
|
|
26
|
+
// well as the GJS-recommended one (Gio.Application.runAsync). Runs on all runtimes.
|
|
27
|
+
await app.runAsync([]);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
using Gtk 4.0;
|
|
2
|
+
using Adw 1;
|
|
3
|
+
|
|
4
|
+
template $TeapotWindow: Adw.ApplicationWindow {
|
|
5
|
+
default-width: 1100;
|
|
6
|
+
default-height: 700;
|
|
7
|
+
title: "Three.js Teapot";
|
|
8
|
+
|
|
9
|
+
[breakpoint]
|
|
10
|
+
Adw.Breakpoint {
|
|
11
|
+
condition ("max-width: 799sp")
|
|
12
|
+
setters {
|
|
13
|
+
splitView.collapsed: true;
|
|
14
|
+
splitView.show-sidebar: false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
[breakpoint]
|
|
19
|
+
Adw.Breakpoint {
|
|
20
|
+
condition ("min-width: 800sp")
|
|
21
|
+
setters {
|
|
22
|
+
splitView.collapsed: false;
|
|
23
|
+
splitView.show-sidebar: true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
content: Gtk.Box {
|
|
28
|
+
orientation: vertical;
|
|
29
|
+
|
|
30
|
+
Adw.HeaderBar {
|
|
31
|
+
[start]
|
|
32
|
+
ToggleButton sidebarToggleButton {
|
|
33
|
+
icon-name: "sidebar-show-symbolic";
|
|
34
|
+
tooltip-text: "Toggle Sidebar";
|
|
35
|
+
active: bind splitView.show-sidebar;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[end]
|
|
39
|
+
Gtk.Button pauseButton {
|
|
40
|
+
icon-name: "media-playback-pause-symbolic";
|
|
41
|
+
tooltip-text: "Pause Rendering";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Adw.OverlaySplitView splitView {
|
|
46
|
+
sidebar-width-fraction: 0.30;
|
|
47
|
+
min-sidebar-width: 280;
|
|
48
|
+
max-sidebar-width: 400;
|
|
49
|
+
show-sidebar: bind sidebarToggleButton.active;
|
|
50
|
+
|
|
51
|
+
sidebar: Gtk.ScrolledWindow {
|
|
52
|
+
hscrollbar-policy: never;
|
|
53
|
+
|
|
54
|
+
Gtk.Box {
|
|
55
|
+
orientation: vertical;
|
|
56
|
+
spacing: 12;
|
|
57
|
+
margin-top: 12;
|
|
58
|
+
margin-bottom: 12;
|
|
59
|
+
margin-start: 12;
|
|
60
|
+
margin-end: 12;
|
|
61
|
+
|
|
62
|
+
Adw.PreferencesGroup {
|
|
63
|
+
title: "Geometry";
|
|
64
|
+
|
|
65
|
+
Adw.ComboRow tessRow {
|
|
66
|
+
title: "Tessellation Level";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Adw.SwitchRow lidRow {
|
|
70
|
+
title: "Display Lid";
|
|
71
|
+
active: true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Adw.SwitchRow bodyRow {
|
|
75
|
+
title: "Display Body";
|
|
76
|
+
active: true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Adw.SwitchRow bottomRow {
|
|
80
|
+
title: "Display Bottom";
|
|
81
|
+
active: true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Adw.SwitchRow fitLidRow {
|
|
85
|
+
title: "Snug Lid";
|
|
86
|
+
active: false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Adw.SwitchRow nonblinnRow {
|
|
90
|
+
title: "Original Scale";
|
|
91
|
+
active: false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Adw.PreferencesGroup {
|
|
96
|
+
title: "Material";
|
|
97
|
+
|
|
98
|
+
Adw.ComboRow shadingRow {
|
|
99
|
+
title: "Shading";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
content: Gtk.Box glAreaContainer {
|
|
106
|
+
hexpand: true;
|
|
107
|
+
vexpand: true;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|