| 1 |
<?php |
| 2 |
/** |
| 3 |
* OpenStation — Recycle Bin: tile art, gate, shell config. |
| 4 |
* |
| 5 |
* The bin WINDOW is an App Framework app (`apps/trash/trash.os.php`) |
| 6 |
* that claims the frozen `desktop-mode-recycle-bin` id, so every |
| 7 |
* shortcut, dock placement and drop target keeps working. This file |
| 8 |
* keeps what the shell needs regardless of the window: the two bin |
| 9 |
* drawings (the dock tile's empty/full swap — `icon-state.ts` — and |
| 10 |
* the app's own `ctx.host.setIcon()` swap read the same pair), the |
| 11 |
* capability gate, the shell-config seed that makes the tile correct |
| 12 |
* on the very first paint, and the stylesheet enqueue (the |
| 13 |
* drag-to-trash drop-target highlight must exist at boot, not at |
| 14 |
* window-open). |
| 15 |
* |
| 16 |
* @package OpenStation |
| 17 |
*/ |
| 18 |
|
| 19 |
defined( 'ABSPATH' ) || exit; |
| 20 |
|
| 21 |
/** |
| 22 |
* The bin SVG, used by the app window's icon and its dock tile. |
| 23 |
* |
| 24 |
* The bin used to be `dashicons-trash`, which worked but wore the |
| 25 |
* wrong clothes. Dashicons are WP core's icon set: solid fills on a |
| 26 |
* 20-unit grid, tuned for admin-menu sizes. The shell's own icons are |
| 27 |
* outlined vessels on a 64-unit grid at stroke 3. Sitting next to |
| 28 |
* WP Explorer, Corkboard and Games in the dock, the Dashicon was |
| 29 |
* visibly a guest from another system: heavier, tighter, and drawn to |
| 30 |
* a different rhythm. |
| 31 |
* |
| 32 |
* So this is the same object, redrawn to the house rule the other |
| 33 |
* three follow: an outlined vessel with solid content, three elements |
| 34 |
* because it renders as small as 20px in the dock. The lid is the |
| 35 |
* solid one, which gives the mark a single dense horizontal to be |
| 36 |
* recognised by when the tapered body below it thins out. |
| 37 |
* |
| 38 |
* Drawn in `currentColor`, so `renderIcon()` paints it as a CSS mask |
| 39 |
* and it takes the surface's own text colour. Dashicons already |
| 40 |
* inherited colour, being font glyphs; the point of the change is the |
| 41 |
* drawing, not the theming. |
| 42 |
* |
| 43 |
* Note that the row actions inside the bin window, and the "Move to |
| 44 |
* trash" entries in context menus, stay on `dashicons-trash`. Those |
| 45 |
* are menu glyphs sitting among other menu glyphs, and they should |
| 46 |
* match their neighbours rather than this icon. |
| 47 |
* |
| 48 |
* The bin has two states. Empty is the vessel on its own; full adds |
| 49 |
* three crumpled balls inside it and knocks the lid askew. |
| 50 |
* |
| 51 |
* @param bool $full Whether to draw the bin holding something. |
| 52 |
* @return string Raw `<svg>` markup. |
| 53 |
*/ |
| 54 |
function openstation_recycle_bin_icon_svg( $full = false ) { |
| 55 |
// The lid and the handle travel together. In the full state the |
| 56 |
// pair is lifted clear of the rim and tipped, hinged at the far |
| 57 |
// corner, the way a lid sits on a bin that has too much in it. |
| 58 |
// |
| 59 |
// It was a 2.5-unit lift at 8 degrees, which on the 20px dock |
| 60 |
// tile and the 58px home tile was no difference at all: the bin |
| 61 |
// read as empty while it held things. At 4 units and 12 degrees |
| 62 |
// (a positive angle turns clockwise about the far corner, so the |
| 63 |
// near end rises) the lid's near end clears the rim by a fifth |
| 64 |
// of the body's height and its far end by nine units, and the |
| 65 |
// wedge of light under it survives every size the mark is drawn. |
| 66 |
$lid_transform = $full |
| 67 |
? ' transform="translate(0 -4) rotate(12 52 21.5)"' |
| 68 |
: ''; |
| 69 |
|
| 70 |
$svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">' |
| 71 |
. '<g' . $lid_transform . '>' |
| 72 |
// The handle, outlined so it reads as a loop rather than a tab. |
| 73 |
. '<path d="M25 19v-2.5a3.5 3.5 0 0 1 3.5-3.5h7a3.5 3.5 0 0 1 3.5 3.5V19" fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/>' |
| 74 |
// The lid: the one solid element, and the widest, so it anchors |
| 75 |
// the mark at small sizes. |
| 76 |
. '<rect x="10" y="19" width="44" height="5" rx="2.5" fill="currentColor"/>' |
| 77 |
. '</g>' |
| 78 |
// The body, tapered towards the base the way a real bin is, which |
| 79 |
// is also what separates it from a plain bucket. |
| 80 |
// |
| 81 |
// 27 units tall, narrowing to 71% of its top width. It was 24 |
| 82 |
// tall at 59%, which drew a shallower, more conical tub than a |
| 83 |
// bin actually is, and left an interior too cramped to hold |
| 84 |
// anything. Taken further, to a bottom much past 75%, the walls |
| 85 |
// go vertical and the mark reads as a bucket; this sits at the |
| 86 |
// edge of that. |
| 87 |
. '<path d="M15.5 28.5h33l-1.2 24a3.5 3.5 0 0 1-3.5 3H20.2a3.5 3.5 0 0 1-3.5-3z" fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/>'; |
| 88 |
|
| 89 |
if ( $full ) { |
| 90 |
// Three crumpled balls, one path with three subpaths so the |
| 91 |
// mark stays at four elements rather than six. |
| 92 |
// |
| 93 |
// Each is a seven-point polygon on a radius jittered between |
| 94 |
// 0.81 and 1.0, with a same-colour round-joined stroke that |
| 95 |
// turns the corners into creases instead of points. Seven |
| 96 |
// points rather than nine because a ball this small needs |
| 97 |
// deeper, fewer facets to keep any texture at all. |
| 98 |
// |
| 99 |
// The layout is staggered deliberately. Two balls at the same |
| 100 |
// height with a third centred under them reads as a face, and |
| 101 |
// it cannot be unseen once noticed, so no two share a y (the |
| 102 |
// top pair are 4.2 units apart) and the third sits below-left |
| 103 |
// rather than on the centreline. Every gap in the mark clears |
| 104 |
// 2 units: 5.1 to the left wall, 4.4 to the right, 2.8 to the |
| 105 |
// rim, 2.5 to the base, and 2.5 to 4.2 between the balls. Those |
| 106 |
// last three decide how far down the size ladder they stay |
| 107 |
// three things instead of one. |
| 108 |
$svg .= '<path d="M29.7 38 27.4 39.5 24.7 39.6 23.6 37.1 24.1 34.4 26.8 34.1 29.2 35.1' |
| 109 |
. 'ZM39.4 44.1 36.7 43.5 34.8 41.6 35.8 39.1 38.1 37.6 40.2 39.3 41.1 41.8' |
| 110 |
. 'ZM28.1 50.4 27 47.9 27.4 45.2 30 44.6 32.6 45.6 32.4 48.3 31 50.5Z"' |
| 111 |
. ' fill="currentColor" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>'; |
| 112 |
// A fourth, larger sheet rides the rim under the lifted end of |
| 113 |
// the lid — the one element of the full state that is legible |
| 114 |
// at 20px, where the balls inside the body are a texture at |
| 115 |
// best. Same seven-point construction, twice the radius, |
| 116 |
// straddling the rim (y 28.5) so it reads as spilling out |
| 117 |
// rather than sitting in. |
| 118 |
$svg .= '<path d="M30 26 27.3 30 22.7 31.7 19.5 28.2 18.6 23.4 22.8 20.7 27.6 21.6Z"' |
| 119 |
. ' fill="currentColor" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>'; |
| 120 |
} |
| 121 |
|
| 122 |
return $svg . '</svg>'; |
| 123 |
} |
| 124 |
|
| 125 |
/** |
| 126 |
* Both bin states as base64 data URIs, ready for `renderIcon()`. |
| 127 |
* |
| 128 |
* The client swaps between these as the count crosses zero, so both |
| 129 |
* have to reach the page on the first paint. Cheap: two string |
| 130 |
* builds and two base64 encodes, no queries. |
| 131 |
* |
| 132 |
* @return array{empty:string,full:string} |
| 133 |
*/ |
| 134 |
function openstation_recycle_bin_icon_uris() { |
| 135 |
return array( |
| 136 |
'empty' => 'data:image/svg+xml;base64,' . base64_encode( openstation_recycle_bin_icon_svg( false ) ), |
| 137 |
'full' => 'data:image/svg+xml;base64,' . base64_encode( openstation_recycle_bin_icon_svg( true ) ), |
| 138 |
); |
| 139 |
} |
| 140 |
|
| 141 |
/** |
| 142 |
* Whether the current user should see the recycle bin at all. |
| 143 |
* |
| 144 |
* Filterable so plugins can hide it from authors/contributors who |
| 145 |
* don't manage trash, or invert the gate to expose it to a custom |
| 146 |
* role. The app's gate (`apps/trash/trash.os.php`) delegates here. |
| 147 |
* |
| 148 |
* @return bool |
| 149 |
*/ |
| 150 |
function openstation_recycle_bin_user_can_use() { |
| 151 |
$can = current_user_can( 'edit_posts' ); |
| 152 |
|
| 153 |
/** |
| 154 |
* Filter whether the current user can see the recycle bin window. |
| 155 |
* |
| 156 |
* @param bool $can Default: edit_posts capability. |
| 157 |
*/ |
| 158 |
return (bool) apply_filters( 'openstation_recycle_bin_user_can_use', $can ); |
| 159 |
} |
| 160 |
|
| 161 |
/** |
| 162 |
* Enqueue the bin stylesheet with the shell. |
| 163 |
* |
| 164 |
* Not a window-open companion: the sheet also paints the |
| 165 |
* drag-to-trash drop-target highlight on the CLOSED bin's dock tile |
| 166 |
* (`data-os-trash-drop-active`), which must work before the window |
| 167 |
* has ever opened. |
| 168 |
*/ |
| 169 |
function openstation_recycle_bin_enqueue_style() { |
| 170 |
if ( ! openstation_recycle_bin_user_can_use() ) { |
| 171 |
return; |
| 172 |
} |
| 173 |
wp_enqueue_style( 'desktop-mode-recycle-bin' ); |
| 174 |
} |
| 175 |
add_action( 'admin_enqueue_scripts', 'openstation_recycle_bin_enqueue_style', 5 ); |
| 176 |
|
| 177 |
/** |
| 178 |
* Inject the initial trash count and both bin drawings into the |
| 179 |
* shell config, so the dock tile (and any icon a user or plugin has |
| 180 |
* placed against this window) shows the right one on the very first |
| 181 |
* paint, before the bin window has ever opened. |
| 182 |
* |
| 183 |
* Both drawings travel together rather than the server picking one: |
| 184 |
* the count changes without a reload, and shipping the pair makes |
| 185 |
* crossing zero a local swap instead of a round trip. |
| 186 |
* |
| 187 |
* @param array $config Shell config blob. |
| 188 |
* @return array |
| 189 |
*/ |
| 190 |
function openstation_recycle_bin_inject_shell_config( $config ) { |
| 191 |
if ( ! is_array( $config ) ) { |
| 192 |
return $config; |
| 193 |
} |
| 194 |
$icons = openstation_recycle_bin_icon_uris(); |
| 195 |
|
| 196 |
$config['recycleBinCount'] = openstation_recycle_bin_count(); |
| 197 |
$config['recycleBinCountUrl'] = esc_url_raw( rest_url( 'desktop-mode/v1/recycle-bin/count' ) ); |
| 198 |
$config['recycleBinPostTypes'] = openstation_recycle_bin_capture_post_types(); |
| 199 |
$config['recycleBinIconEmpty'] = $icons['empty']; |
| 200 |
$config['recycleBinIconFull'] = $icons['full']; |
| 201 |
return $config; |
| 202 |
} |
| 203 |
add_filter( 'openstation_shell_config', 'openstation_recycle_bin_inject_shell_config', 20 ); |
| 204 |
|