PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.8
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.8
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 / desktop-files / grid.php

grid.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.8, at includes/desktop-files/grid.php

171 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — the icon grid, server side.
4 *
5 * The PHP mirror of `src/desktop-files/grid.ts`. Same pitch, same
6 * fallbacks, same reading order — because the server picks cells for
7 * tiles the client then has to lay out, and the two disagreeing is
8 * visible on the wallpaper.
9 *
10 * It disagreed for a while. The client's grid was retuned to a
11 * 108 × 120 pitch and this side kept the 96 × 110 one it was written
12 * against, so every coordinate the server minted landed off-grid;
13 * from the seventh row down two server rows aliased onto the same
14 * client cell and the tiles had to be displaced on sight. Worse, the
15 * server packed a column to unbounded depth — it has no viewport and
16 * asked nothing about one — so a desktop with more icons than fit in
17 * one column always had a tile stored past the bottom edge of a layer
18 * that doesn't scroll. The client's rescue pass then repacked the
19 * whole wallpaper to bring it back, and the desktop the user knew
20 * came back rearranged.
21 *
22 * Hence {@see openstation_files_grid_fallback_rows()}: the server
23 * can't measure the canvas, so it assumes a small one and wraps
24 * early. Wrapping one cell early costs a column the desktop had room
25 * for. Wrapping one cell late loses a tile.
26 *
27 * `Tests_OpenStation_DesktopFilesGrid` parses the TypeScript and
28 * fails if either side moves without the other.
29 *
30 * @package OpenStation
31 */
32
33 defined( 'ABSPATH' ) || exit;
34
35 /**
36 * Gutter from the top / inline-start edge of an icon canvas.
37 *
38 * Mirrors `GRID_PADDING` / `--os-grid-padding`.
39 */
40 const OPENSTATION_GRID_PADDING = 16;
41
42 /**
43 * Cell pitch — the tile box plus the air after it.
44 *
45 * Mirrors `GRID_CELL_W` / `GRID_CELL_H`, which are themselves derived
46 * (`--os-tile-w` + `--os-grid-gap-x`, `--os-tile-h` + `--os-grid-gap-y`).
47 * Declared here rather than derived because PHP has no reason to know
48 * a cell is made of two things; the test proves the total agrees.
49 */
50 const OPENSTATION_GRID_CELL_W = 108;
51 const OPENSTATION_GRID_CELL_H = 120;
52
53 /**
54 * How far a scan runs along the axis it wraps on.
55 *
56 * The server never measures a canvas, so it always uses these.
57 * Mirrors `GRID_FALLBACK_ROWS` / `GRID_FALLBACK_COLS`.
58 */
59 const OPENSTATION_GRID_FALLBACK_ROWS = 5;
60 const OPENSTATION_GRID_FALLBACK_COLS = 4;
61
62 /**
63 * Upper bound on a scan's unbounded axis. Packing 999 columns of
64 * icons is not a layout, it's a runaway loop.
65 */
66 const OPENSTATION_GRID_SCAN_LIMIT = 999;
67
68 /**
69 * Which way a canvas reads.
70 *
71 * The desktop root reads in columns — it is tall, and every desktop
72 * metaphor worth copying fills a column before starting the next. A
73 * folder reads in rows: it is wide and short, and a column-major fill
74 * would run a two-item folder off the bottom of its own window.
75 *
76 * The same rule as `orderForFolder()` in `src/desktop-files/layer.ts`.
77 *
78 * @param int $parent_id Folder id. `0` is the desktop root.
79 * @return string `'column'` or `'row'`.
80 */
81 function openstation_files_grid_order( $parent_id ) {
82 return 0 === (int) $parent_id ? 'column' : 'row';
83 }
84
85 /**
86 * Snap a stored pixel coordinate to the cell it belongs to.
87 *
88 * Rounds rather than floors, so a coordinate that predates a pitch
89 * change — or that a plugin wrote by hand — is read as the cell it is
90 * nearest to instead of leaving a phantom hole one cell up and left.
91 * The client's `pointToCell()` does the same, which is what keeps an
92 * occupancy set built here and one built there describing the same
93 * canvas.
94 *
95 * @param int $x Horizontal pixel offset.
96 * @param int $y Vertical pixel offset.
97 * @return array{0:int,1:int} `array( $col, $row )`, both >= 0.
98 */
99 function openstation_files_grid_point_to_cell( $x, $y ) {
100 $col = (int) round( ( (int) $x - OPENSTATION_GRID_PADDING ) / OPENSTATION_GRID_CELL_W );
101 $row = (int) round( ( (int) $y - OPENSTATION_GRID_PADDING ) / OPENSTATION_GRID_CELL_H );
102 return array( max( 0, $col ), max( 0, $row ) );
103 }
104
105 /**
106 * The pixel coordinate of a cell.
107 *
108 * @param int $col Column index.
109 * @param int $row Row index.
110 * @return array{x:int,y:int} Placement coordinates.
111 */
112 function openstation_files_grid_cell_to_point( $col, $row ) {
113 return array(
114 'x' => OPENSTATION_GRID_PADDING + max( 0, (int) $col ) * OPENSTATION_GRID_CELL_W,
115 'y' => OPENSTATION_GRID_PADDING + max( 0, (int) $row ) * OPENSTATION_GRID_CELL_H,
116 );
117 }
118
119 /**
120 * Build an occupancy set from rows carrying `x` / `y`.
121 *
122 * Keys are `"<col>,<row>"`, the same convention `cellKey()` uses on
123 * the client.
124 *
125 * @param array $rows Rows with `x` and `y` keys.
126 * @return array<string,bool> Occupancy set.
127 */
128 function openstation_files_grid_occupied( $rows ) {
129 $occupied = array();
130 foreach ( (array) $rows as $row ) {
131 if ( ! isset( $row['x'], $row['y'] ) ) {
132 continue;
133 }
134 list( $col, $r ) = openstation_files_grid_point_to_cell( $row['x'], $row['y'] );
135 $occupied[ "$col,$r" ] = true;
136 }
137 return $occupied;
138 }
139
140 /**
141 * First free cell in `$order`, wrapping at the assumed canvas edge.
142 *
143 * Marks the cell it returns, so a caller allocating several in a row
144 * gets consecutive slots without bookkeeping of its own.
145 *
146 * @param array<string,bool> $occupied Occupancy set, by reference.
147 * @param string $order `'column'` or `'row'`.
148 * @return array{0:int,1:int} `array( $col, $row )`.
149 */
150 function openstation_files_grid_next_free( &$occupied, $order = 'column' ) {
151 if ( 'row' === $order ) {
152 $outer = OPENSTATION_GRID_SCAN_LIMIT;
153 $inner = OPENSTATION_GRID_FALLBACK_COLS;
154 } else {
155 $outer = OPENSTATION_GRID_SCAN_LIMIT;
156 $inner = OPENSTATION_GRID_FALLBACK_ROWS;
157 }
158 for ( $o = 0; $o < $outer; $o++ ) {
159 for ( $i = 0; $i < $inner; $i++ ) {
160 $col = 'row' === $order ? $i : $o;
161 $row = 'row' === $order ? $o : $i;
162 if ( ! isset( $occupied[ "$col,$row" ] ) ) {
163 $occupied[ "$col,$row" ] = true;
164 return array( $col, $row );
165 }
166 }
167 }
168 $occupied['0,0'] = true;
169 return array( 0, 0 );
170 }
171