@gjsify/example-dom-three-geometry-teapot 0.55.0 → 0.56.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/browser.js +149 -53
- package/dist/index.html +0 -1
- package/dist/webgl.css +3 -3
- package/package.json +7 -7
- package/src/browser/browser.ts +36 -123
- package/src/browser/index.html +0 -1
- package/src/browser/webgl.css +3 -3
package/dist/index.html
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<title>Three.js Teapot — gjsify</title>
|
|
6
6
|
<link rel="stylesheet" href="./webgl.css" type="text/css" />
|
|
7
|
-
<link rel="stylesheet" href="./browser.css" type="text/css" />
|
|
8
7
|
<script src="./browser.js" type="module" defer></script>
|
|
9
8
|
</head>
|
|
10
9
|
<body></body>
|
package/dist/webgl.css
CHANGED
|
@@ -14,7 +14,7 @@ html, body {
|
|
|
14
14
|
justify-content: center;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
adw-window {
|
|
17
|
+
adw-application-window {
|
|
18
18
|
max-width: 100%;
|
|
19
19
|
max-height: 100%;
|
|
20
20
|
}
|
|
@@ -38,13 +38,13 @@ adw-window {
|
|
|
38
38
|
gap: 12px;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
#
|
|
41
|
+
#glAreaContainer {
|
|
42
42
|
flex: 1;
|
|
43
43
|
position: relative;
|
|
44
44
|
min-width: 0;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
#
|
|
47
|
+
#glAreaContainer canvas {
|
|
48
48
|
display: block;
|
|
49
49
|
width: 100%;
|
|
50
50
|
height: 100%;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/example-dom-three-geometry-teapot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.56.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",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"build:assets": "gjsify copy src/browser/index.html src/browser/webgl.css src/assets dist/"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@gjsify/webgl": "^0.
|
|
30
|
+
"@gjsify/webgl": "^0.56.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@girs/adw-1": "5.4.0",
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"@girs/gjs": "5.4.0",
|
|
36
36
|
"@girs/gobject-2.0": "5.4.0",
|
|
37
37
|
"@girs/gtk-4.0": "5.4.0",
|
|
38
|
-
"@gjsify/adwaita-icons": "^0.
|
|
39
|
-
"@gjsify/adwaita-web": "^0.
|
|
40
|
-
"@gjsify/cli": "^0.
|
|
41
|
-
"@gjsify/devtools": "^0.
|
|
38
|
+
"@gjsify/adwaita-icons": "^0.56.0",
|
|
39
|
+
"@gjsify/adwaita-web": "^0.56.0",
|
|
40
|
+
"@gjsify/cli": "^0.56.0",
|
|
41
|
+
"@gjsify/devtools": "^0.56.0",
|
|
42
42
|
"@gjsify/node-gi": "file:../../../packages/node-gi/node-gi",
|
|
43
|
-
"@gjsify/vite-plugin-blueprint": "^0.
|
|
43
|
+
"@gjsify/vite-plugin-blueprint": "^0.56.0",
|
|
44
44
|
"@types/node": "^25.9.2",
|
|
45
45
|
"@types/three": "^0.185.0",
|
|
46
46
|
"http-server": "^14.1.1",
|
package/src/browser/browser.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// Browser UI for three-geometry-teapot example.
|
|
2
|
-
//
|
|
2
|
+
// The widget tree is the GJS window's own `teapot-window.blp`, projected and mounted by
|
|
3
|
+
// @gjsify/adwaita-web — one authored file, so the sidebar toggle, the `bind`s between the toggle
|
|
4
|
+
// and the split view and the two `[breakpoint]`s cannot drift from the GTK build.
|
|
3
5
|
|
|
4
|
-
import '@gjsify/adwaita-web'; // registers the custom elements + self-injects the stylesheet
|
|
6
|
+
import { mountSharedTree } from '@gjsify/adwaita-web'; // also registers the custom elements + self-injects the stylesheet
|
|
5
7
|
// A showcase is served to whatever browser opens it, so it cannot assume the host has
|
|
6
8
|
// Adwaita Sans the way a GNOME desktop does. `import '@gjsify/adwaita-web'` names the
|
|
7
9
|
// family and ships no `@font-face`, so without this call the chrome renders in the host's
|
|
8
10
|
// default sans on macOS, on Windows and on any Linux that is not GNOME — and looks right
|
|
9
11
|
// only on the machine it was written on.
|
|
10
12
|
import { applyAdwaitaFonts } from '@gjsify/adwaita-web/fonts';
|
|
11
|
-
import
|
|
12
|
-
import { mediaPlaybackPauseSymbolic, mediaPlaybackStartSymbolic } from '@gjsify/adwaita-icons/actions';
|
|
13
|
+
import tree from '../gjs/teapot-window.blp?shared-tree';
|
|
13
14
|
import {
|
|
14
15
|
start,
|
|
15
16
|
TESS_VALUES,
|
|
@@ -35,91 +36,42 @@ export interface ShowcaseHandle {
|
|
|
35
36
|
readonly isPaused: boolean;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
/**
|
|
39
|
-
function
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/** Replace a button's icon with a freshly-parsed copy of the given SVG source. */
|
|
45
|
-
function setButtonIcon(btn: HTMLButtonElement, svgSource: string): void {
|
|
46
|
-
btn.replaceChildren(parseSvg(svgSource));
|
|
39
|
+
/** The one element of the mounted tree with this authored id. */
|
|
40
|
+
function byId(root: HTMLElement, id: string): HTMLElement {
|
|
41
|
+
const el = root.querySelector<HTMLElement>(`#${id}`);
|
|
42
|
+
if (el === null) throw new Error(`teapot-window.blp declares no object with the id "${id}"`);
|
|
43
|
+
return el;
|
|
47
44
|
}
|
|
48
45
|
|
|
49
46
|
/**
|
|
50
|
-
* Mounts
|
|
47
|
+
* Mounts the teapot window's shared tree into `container`.
|
|
48
|
+
*
|
|
49
|
+
* The two combo rows' models are not in the tree: on GTK they are `Gtk.StringList`s the window's
|
|
50
|
+
* TypeScript builds (`teapot-window.ts`), and Blueprint has no spelling for a list model here. The
|
|
51
|
+
* browser reads the same `TESS_VALUES` / `SHADING_VALUES` / `DEFAULT_*_INDEX` from `three-demo.ts`
|
|
52
|
+
* and writes them onto the rows, so the lists themselves have one source; only the wiring is copied.
|
|
51
53
|
*/
|
|
52
54
|
export function mount(container: HTMLElement, options?: MountOptions): ShowcaseHandle {
|
|
53
|
-
const win =
|
|
54
|
-
|
|
55
|
-
win
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
splitView.setAttribute('min-sidebar-width', '280');
|
|
71
|
-
splitView.setAttribute('max-sidebar-width', '400');
|
|
72
|
-
splitView.setAttribute('sidebar-width-fraction', '0.30');
|
|
73
|
-
splitView.setAttribute('show-sidebar', '');
|
|
74
|
-
|
|
75
|
-
const sidebarContent = document.createElement('div');
|
|
76
|
-
sidebarContent.setAttribute('slot', 'sidebar');
|
|
77
|
-
sidebarContent.className = 'adw-sidebar-content';
|
|
78
|
-
|
|
79
|
-
const geoGroup = document.createElement('adw-preferences-group');
|
|
80
|
-
geoGroup.setAttribute('title', 'Geometry');
|
|
81
|
-
|
|
82
|
-
const tessRow = document.createElement('adw-combo-row');
|
|
83
|
-
tessRow.setAttribute('title', 'Tessellation Level');
|
|
84
|
-
tessRow.setAttribute('items', JSON.stringify(TESS_VALUES.map(String)));
|
|
85
|
-
tessRow.setAttribute('selected', String(DEFAULT_TESS_INDEX));
|
|
86
|
-
|
|
87
|
-
const lidRow = document.createElement('adw-switch-row');
|
|
88
|
-
lidRow.setAttribute('title', 'Display Lid');
|
|
89
|
-
lidRow.setAttribute('active', '');
|
|
90
|
-
|
|
91
|
-
const bodyRow = document.createElement('adw-switch-row');
|
|
92
|
-
bodyRow.setAttribute('title', 'Display Body');
|
|
93
|
-
bodyRow.setAttribute('active', '');
|
|
94
|
-
|
|
95
|
-
const bottomRow = document.createElement('adw-switch-row');
|
|
96
|
-
bottomRow.setAttribute('title', 'Display Bottom');
|
|
97
|
-
bottomRow.setAttribute('active', '');
|
|
98
|
-
|
|
99
|
-
const fitLidRow = document.createElement('adw-switch-row');
|
|
100
|
-
fitLidRow.setAttribute('title', 'Snug Lid');
|
|
101
|
-
|
|
102
|
-
const nonblinnRow = document.createElement('adw-switch-row');
|
|
103
|
-
nonblinnRow.setAttribute('title', 'Original Scale');
|
|
104
|
-
|
|
105
|
-
geoGroup.append(tessRow, lidRow, bodyRow, bottomRow, fitLidRow, nonblinnRow);
|
|
106
|
-
|
|
107
|
-
const matGroup = document.createElement('adw-preferences-group');
|
|
108
|
-
matGroup.setAttribute('title', 'Material');
|
|
109
|
-
|
|
110
|
-
const shadingRow = document.createElement('adw-combo-row');
|
|
111
|
-
shadingRow.setAttribute('title', 'Shading');
|
|
112
|
-
shadingRow.setAttribute('items', JSON.stringify([...SHADING_VALUES]));
|
|
113
|
-
shadingRow.setAttribute('selected', String(DEFAULT_SHADING_INDEX));
|
|
114
|
-
|
|
115
|
-
matGroup.append(shadingRow);
|
|
116
|
-
|
|
117
|
-
sidebarContent.append(geoGroup, matGroup);
|
|
55
|
+
const { root: win } = mountSharedTree(tree, { into: container });
|
|
56
|
+
|
|
57
|
+
const tessRow = byId(win, 'tessRow');
|
|
58
|
+
const shadingRow = byId(win, 'shadingRow');
|
|
59
|
+
for (const [row, values, selected] of [
|
|
60
|
+
[tessRow, TESS_VALUES.map(String), DEFAULT_TESS_INDEX],
|
|
61
|
+
[shadingRow, [...SHADING_VALUES], DEFAULT_SHADING_INDEX],
|
|
62
|
+
] as const) {
|
|
63
|
+
row.setAttribute('model', JSON.stringify(values));
|
|
64
|
+
row.setAttribute('selected', String(selected));
|
|
65
|
+
}
|
|
66
|
+
const lidRow = byId(win, 'lidRow');
|
|
67
|
+
const bodyRow = byId(win, 'bodyRow');
|
|
68
|
+
const bottomRow = byId(win, 'bottomRow');
|
|
69
|
+
const fitLidRow = byId(win, 'fitLidRow');
|
|
70
|
+
const nonblinnRow = byId(win, 'nonblinnRow');
|
|
71
|
+
const pauseBtn = byId(win, 'pauseButton');
|
|
118
72
|
|
|
119
73
|
// Inline styles so the layout holds in the website embed too, which loads no showcase CSS.
|
|
120
|
-
const glContainer =
|
|
121
|
-
glContainer.setAttribute('slot', 'content');
|
|
122
|
-
glContainer.id = 'gl-area-container';
|
|
74
|
+
const glContainer = byId(win, 'glAreaContainer');
|
|
123
75
|
glContainer.style.cssText = 'flex:1;position:relative;min-width:0;min-height:0';
|
|
124
76
|
|
|
125
77
|
const canvas = document.createElement('canvas');
|
|
@@ -127,45 +79,6 @@ export function mount(container: HTMLElement, options?: MountOptions): ShowcaseH
|
|
|
127
79
|
canvas.style.cssText = 'display:block;width:100%;height:100%;position:absolute;inset:0';
|
|
128
80
|
glContainer.append(canvas);
|
|
129
81
|
|
|
130
|
-
splitView.append(sidebarContent, glContainer);
|
|
131
|
-
win.append(headerBar, splitView);
|
|
132
|
-
container.append(win);
|
|
133
|
-
|
|
134
|
-
// AFTER DOM connection: connectedCallback is what creates the header-bar-start wrapper.
|
|
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
|
-
toggleBtn.addEventListener('click', () => {
|
|
150
|
-
splitView.toggleSidebar();
|
|
151
|
-
toggleBtn.classList.toggle('active', splitView.showSidebar);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
splitView.addEventListener('sidebar-toggled', () => {
|
|
155
|
-
toggleBtn.classList.toggle('active', splitView.showSidebar);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
let lastCollapsed: boolean | null = null;
|
|
159
|
-
new ResizeObserver(([entry]) => {
|
|
160
|
-
const width = entry.contentRect.width;
|
|
161
|
-
const shouldCollapse = width < 800;
|
|
162
|
-
if (shouldCollapse === lastCollapsed) return;
|
|
163
|
-
lastCollapsed = shouldCollapse;
|
|
164
|
-
splitView.collapsed = shouldCollapse;
|
|
165
|
-
splitView.showSidebar = !shouldCollapse;
|
|
166
|
-
toggleBtn.classList.toggle('active', !shouldCollapse);
|
|
167
|
-
}).observe(win);
|
|
168
|
-
|
|
169
82
|
// Also covers a slide becoming visible after `display: none`, which reports a size for the first
|
|
170
83
|
// time.
|
|
171
84
|
canvas.width = glContainer.clientWidth;
|
|
@@ -203,8 +116,8 @@ export function mount(container: HTMLElement, options?: MountOptions): ShowcaseH
|
|
|
203
116
|
}).observe(glContainer);
|
|
204
117
|
|
|
205
118
|
function updatePauseButton(paused: boolean): void {
|
|
206
|
-
|
|
207
|
-
pauseBtn.
|
|
119
|
+
pauseBtn.setAttribute('icon-name', paused ? 'media-playback-start-symbolic' : 'media-playback-pause-symbolic');
|
|
120
|
+
pauseBtn.setAttribute('tooltip-text', paused ? 'Resume Rendering' : 'Pause Rendering');
|
|
208
121
|
}
|
|
209
122
|
pauseBtn.addEventListener('click', () => {
|
|
210
123
|
if (demo) {
|
package/src/browser/index.html
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<title>Three.js Teapot — gjsify</title>
|
|
6
6
|
<link rel="stylesheet" href="./webgl.css" type="text/css" />
|
|
7
|
-
<link rel="stylesheet" href="./browser.css" type="text/css" />
|
|
8
7
|
<script src="./browser.js" type="module" defer></script>
|
|
9
8
|
</head>
|
|
10
9
|
<body></body>
|
package/src/browser/webgl.css
CHANGED
|
@@ -14,7 +14,7 @@ html, body {
|
|
|
14
14
|
justify-content: center;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
adw-window {
|
|
17
|
+
adw-application-window {
|
|
18
18
|
max-width: 100%;
|
|
19
19
|
max-height: 100%;
|
|
20
20
|
}
|
|
@@ -38,13 +38,13 @@ adw-window {
|
|
|
38
38
|
gap: 12px;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
#
|
|
41
|
+
#glAreaContainer {
|
|
42
42
|
flex: 1;
|
|
43
43
|
position: relative;
|
|
44
44
|
min-width: 0;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
#
|
|
47
|
+
#glAreaContainer canvas {
|
|
48
48
|
display: block;
|
|
49
49
|
width: 100%;
|
|
50
50
|
height: 100%;
|