PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.9
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.9
1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 0.8.6 All 33 releases
desktop-mode / includes / games / window.php

window.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.9, at includes/games/window.php

247 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — Games: hub window + desktop icon registration.
4 *
5 * Native window with id `desktop-mode-games` — the "Games folder"
6 * fixture on the wallpaper. The template body is a static skeleton
7 * (a game grid + a detail panel, Steam-library style) that the JS
8 * bundle enhances on first open: the grid paints from the games
9 * registry; selecting a game reveals its description, Play +
10 * Challenge actions, its scoreboard, and its challenges below.
11 *
12 * Both registrations are filterable via
13 * `openstation_games_window_args` / `openstation_games_icon_args`,
14 * mirroring the Recycle Bin.
15 *
16 * @package OpenStation
17 */
18
19 defined( 'ABSPATH' ) || exit;
20
21 /**
22 * The stylesheet handles a game window needs, hub or no hub.
23 *
24 * Set once while the Games window is registered — after the
25 * `openstation_games_window_args` filter, so it includes the built-in
26 * games' own sheets and any a plugin appended — and read back when the
27 * shell config is assembled.
28 *
29 * These ride the hub window as companion styles, which is right when
30 * the hub is what opened. It is not the only way in: the challenge
31 * toast's "Accept & Play" is built to work with the hub closed, solo
32 * mode boots straight to `?openstation_solo=os-game-<id>`, and
33 * `wp.os.games.launch()` is documented for plugin authors. Each of
34 * those reaches `launchGame()` in the shell bundle without the hub
35 * window ever existing, and the game then paints unstyled.
36 *
37 * @param string[]|null $set Handles to store, when registering.
38 * @return string[] The stored handles.
39 */
40 function openstation_games_style_handles( $set = null ) {
41 static $handles = array();
42 if ( null !== $set ) {
43 $handles = array_values( array_unique( array_filter( array_map( 'strval', (array) $set ) ) ) );
44 }
45 return $handles;
46 }
47
48 /**
49 * The shared gamepad SVG used by the window icon and the desktop
50 * icon.
51 *
52 * Drawn in `currentColor`, which is what makes it a silhouette:
53 * `isSilhouetteSvg()` in `src/icon.ts` looks for exactly that token
54 * and, finding it, paints the art as a CSS mask instead of a
55 * background-image, so it takes whatever colour the surface is
56 * already using for text.
57 *
58 * This icon used to be the only fixed-colour one in the shell, in
59 * four hues (`#6c5ce7`, white, `#ffd166`, `#06d6a0`) that belong to
60 * no palette the plugin ships. Two things followed from that. It
61 * could not invert on a light title bar or a light desktop theme,
62 * because a background-image has no colour to inherit. And under a
63 * desktop theme's icon tint it collapsed outright: that path runs
64 * `applyIconMask()`, which keeps only the artwork's alpha, and every
65 * pixel of the old gamepad was opaque, so the d-pad and the buttons
66 * vanished into a featureless blob.
67 *
68 * Redrawn as an outlined body with the controls knocked into it, the
69 * detail is carried by negative space rather than by colour, so it
70 * survives both. Held to five elements because it renders as small as
71 * 20px in the dock: body, two d-pad bars, two buttons.
72 *
73 * @return string Raw `<svg>` markup.
74 */
75 function openstation_games_icon_svg() {
76 return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">'
77 // The body: outlined, so what sits inside it reads as detail
78 // rather than as more fill.
79 . '<path d="M21 21h22a13 13 0 0 1 12.6 9.8l2.8 11.2a7.5 7.5 0 0 1-13.6 5.8L40.5 41h-17l-4.3 6.8A7.5 7.5 0 0 1 5.6 42l2.8-11.2A13 13 0 0 1 21 21z" fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/>'
80 // D-pad, two crossed bars sharing a centre.
81 . '<rect x="14" y="28.5" width="14" height="4.6" rx="2.3" fill="currentColor"/>'
82 . '<rect x="18.7" y="23.8" width="4.6" height="14" rx="2.3" fill="currentColor"/>'
83 // Face buttons, offset on the diagonal the way a real pad has
84 // them. Two, not four: four would not separate at 20px.
85 . '<circle cx="40.5" cy="34" r="3.1" fill="currentColor"/>'
86 . '<circle cx="47.5" cy="28.5" r="3.1" fill="currentColor"/>'
87 . '</svg>';
88 }
89
90 /**
91 * Whether the current user can use desktop games.
92 *
93 * @return bool
94 */
95 function openstation_games_user_can_use() {
96 $can = is_user_logged_in() && current_user_can( 'read' );
97
98 /**
99 * Filter whether the current user can see the Games window,
100 * icon, and play games.
101 *
102 * @param bool $can Default: logged-in + `read` capability.
103 */
104 return (bool) apply_filters( 'openstation_games_user_can_use', $can );
105 }
106
107 /**
108 * Echoes the Games window's template body.
109 *
110 * The `data-os-games-*` hooks are the contract the JS
111 * render callback relies on — keep them intact (or rename via the
112 * filter) when customizing the layout.
113 */
114 function openstation_games_render_template() {
115 ob_start();
116 ?>
117 <div class="desktop-mode-games" data-os-games-root>
118 <div class="os-games__library">
119 <div class="os-games__grid" data-os-games-grid role="listbox" aria-label="<?php esc_attr_e( 'Games', 'desktop-mode' ); ?>"></div>
120 <div class="os-games__detail" data-os-games-detail hidden></div>
121 </div>
122 </div>
123 <?php
124 $html = (string) ob_get_clean();
125
126 /**
127 * Filter the Games window's template HTML.
128 *
129 * Keep the `data-os-games-*` hooks intact so the JS
130 * render callback can find its mount points.
131 *
132 * @param string $html Default template HTML.
133 */
134 $filtered = (string) apply_filters( 'openstation_games_template_html', $html );
135 echo wp_kses( $filtered, openstation_native_window_allowed_html() );
136 }
137
138 /**
139 * Register the Games window + desktop icon on `init`.
140 *
141 * Priority 20, after the native-window registry bootstraps — same
142 * timing as the Recycle Bin.
143 */
144 function openstation_games_register_window() {
145 if ( ! openstation_games_user_can_use() ) {
146 return;
147 }
148
149 $icon_uri = 'data:image/svg+xml;base64,' . base64_encode( openstation_games_icon_svg() );
150
151 $window_args = array(
152 'title' => __( 'Games', 'desktop-mode' ),
153 'icon' => $icon_uri,
154 'template' => 'openstation_games_render_template',
155 'script' => 'desktop-mode-games',
156 // Companion styles load with the games bundle on first open.
157 // Every game window (`os-game-<id>`) is opened by
158 // `launchGame()`, which lives in that bundle — so a sheet
159 // riding it here is guaranteed in the tab before any game
160 // paints. Built-in games append their own via the
161 // `openstation_games_window_args` filter below.
162 'styles' => array( 'desktop-mode-games' ),
163 'width' => 900,
164 'height' => 600,
165 'min_width' => 560,
166 'min_height' => 400,
167 // No dock tile from the window registration: the desktop icon
168 // below is this app's one launcher. Registering both used to
169 // mint two entries for one app, each with its own default, and
170 // the dock painted a tile while Preferences said "On the
171 // desktop". The navigation model collapses them either way —
172 // this keeps Games matching how My WordPress and Corkboard
173 // already register.
174 'placement' => 'none',
175 );
176
177 /**
178 * Filter the args used to register the Games native window.
179 *
180 * @param array $window_args Args passed to `openstation_register_window()`.
181 */
182 $window_args = (array) apply_filters( 'openstation_games_window_args', $window_args );
183
184 // Remember the finished style list — base sheet plus whatever the
185 // built-in games and any plugin appended through the filter above.
186 // A game window does NOT always arrive through the hub: the
187 // challenge toast's "Accept & Play" is designed to work with the
188 // hub closed, solo mode boots straight into
189 // `?openstation_solo=os-game-<id>`, and `wp.os.games.launch()` is
190 // documented for plugins. In each of those, `launchGame()` runs
191 // from the SHELL bundle's copy, the hub window never opens, and its
192 // companion sheets never load — the HUD paints as raw text. The
193 // shell needs the list to inject them on demand.
194 openstation_games_style_handles( $window_args['styles'] );
195
196 $registered = openstation_register_window( 'desktop-mode-games', $window_args );
197 if ( is_wp_error( $registered ) ) {
198 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
199 error_log( '[openstation] Games window registration failed: ' . $registered->get_error_message() );
200 return;
201 }
202
203 $icon_args = array(
204 'title' => __( 'Games', 'desktop-mode' ),
205 'icon_svg' => openstation_games_icon_svg(),
206 'window' => 'desktop-mode-games',
207 'position' => 85,
208 );
209
210 /**
211 * Filter the args used to register the Games desktop icon.
212 *
213 * @param array $icon_args Args passed to `openstation_register_icon()`.
214 */
215 $icon_args = (array) apply_filters( 'openstation_games_icon_args', $icon_args );
216
217 openstation_register_icon( 'desktop-mode-games', $icon_args );
218 }
219 add_action( 'init', 'openstation_games_register_window', 20 );
220
221 /**
222 * Localize REST endpoints for the JS bundle.
223 *
224 * The bundle reads its config off `window.openStationGamesConfig`
225 * and never hardcodes URLs.
226 */
227 function openstation_games_localize_config() {
228 if ( ! openstation_games_user_can_use() ) {
229 return;
230 }
231
232 wp_localize_script(
233 'desktop-mode-games',
234 'openStationGamesConfig',
235 array(
236 'restNonce' => wp_create_nonce( 'wp_rest' ),
237 'gamesUrlBase' => esc_url_raw( rest_url( 'desktop-mode/v1/games' ) ),
238 'challengesUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/games/challenges' ) ),
239 'usersSearchUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/games/users/search' ) ),
240 )
241 );
242 }
243 // Priority 5 for the same reason as the recycle bin: the lazy-load payload is
244 // built at priority 10, and localize data attached after that never reaches a
245 // bundle that loads on window open.
246 add_action( 'admin_enqueue_scripts', 'openstation_games_localize_config', 5 );
247