@vestra-player/sdk 1.6.7 → 1.6.9

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.
@@ -954,17 +954,17 @@ export type BarButtonIcon = "play" | "pause" | "stop" | "back" | "forward" | "op
954
954
  * A command's button on the bottom bar: {@link CommandOptions.button}.
955
955
  *
956
956
  * @remarks
957
- * **Where it shows.** The bottom bar places the player's strip of command
958
- * buttons, `BarButtons` from `@vestra/bar-buttons.slint`, at its right end;
959
- * the player fills it with the buttons of every plugin, left to right by
960
- * `order`, and its own sidebar toggle rightmost. When the window is narrow
961
- * the strip loses cells from its outer (left) end inward: the lowest order
962
- * goes first, the sidebar toggle last. A
963
- * bottom bar that does not place `BarButtons` shows no buttons: the
964
- * command's menu entry stays, and the player logs the buttons it cannot
965
- * show and whose bar hides them. The built-in bar (`vestra.playback`) places
966
- * it; see
967
- * [Writing a bottom bar](../../docs/guidelines/interface.md#writing-a-bottom-bar).
957
+ * **Where it shows.** The player draws the bottom bar's button strip and
958
+ * decides where it goes: today at the right end of the bottom bar; this may
959
+ * change (older players let the bar place `BarButtons` itself). Buttons
960
+ * are ordered by `order`, then by plugin id; lower `order` comes first and
961
+ * stays visible longest. When the strip runs out of room, or more than 8
962
+ * plugin buttons are declared, the remaining buttons move to the strip's »
963
+ * menu, in the same order. A button is never dropped: it is either in the
964
+ * strip or in the » menu, and its command keeps its menu entry, its key and
965
+ * its place in the command palette. Users may pin and unpin buttons later;
966
+ * do not rely on a position. See
967
+ * [Bottom-bar buttons](../../docs/explanation/commands.md#bottom-bar-buttons).
968
968
  *
969
969
  * **What it shows.** The icon, the command's title (translated, a trailing
970
970
  * "…" removed) and its first key as the tooltip ("Plugin Store (F9)"),
@@ -974,9 +974,9 @@ export type BarButtonIcon = "play" | "pause" | "stop" | "back" | "forward" | "op
974
974
  * a command with `checked` shows a small dot under its icon while checked.
975
975
  * A click runs the command exactly like a click on its menu item.
976
976
  *
977
- * **Limits.** One button per plugin and eight in the player
978
- * (`BAR_BUTTON_LIMITS`): a button past them is left out (its command and
979
- * menu entry stay) and the player logs it with `limit`. The button goes
977
+ * **Limits.** One button per plugin, the first it declares (a later one
978
+ * stays a menu item only), and eight in the strip (`BAR_BUTTON_LIMITS`); the
979
+ * rest are in the » menu. The button goes
980
980
  * with its command: disposing the command, or disabling, stopping or
981
981
  * uninstalling the plugin, removes it.
982
982
  *
@@ -984,8 +984,7 @@ export type BarButtonIcon = "play" | "pause" | "stop" | "back" | "forward" | "op
984
984
  * A Store icon appears in the bottom bar's button strip; a click opens
985
985
  * the store window, exactly like the menu item. The player draws the
986
986
  * button from the command's title, keys and state, and places it by
987
- * `order` (200 is where the player's own Plugin Store button sits); a
988
- * bar that does not place `BarButtons` shows only the menu item.
987
+ * `order` (200 is where the player's own Plugin Store button sits).
989
988
  * ```ts
990
989
  * const store = plugin.windows.create("ui/store.slint");
991
990
  * plugin.commands.add({
@@ -1238,8 +1237,8 @@ export interface CommandOptions {
1238
1237
  /**
1239
1238
  * A button for this command on the bottom bar (SDK 1.5.29, player 0.5.10):
1240
1239
  * an icon and a place, `{ icon: "store", order: 200 }`. Needs a `path`: the
1241
- * menu entry is where the command stays reachable when the bar shows no
1242
- * button (a bar without `BarButtons`, a narrow window, a limit). See
1240
+ * menu entry is where the command stays reachable wherever the button
1241
+ * is (the strip or its » menu). See
1243
1242
  * {@link CommandButton} for where it shows, what it shows and the limits.
1244
1243
  * Fails with `invalid` for an unknown icon name, an order outside 0–999 or
1245
1244
  * a button without a path.
@@ -4217,6 +4216,36 @@ export interface TimelineMark {
4217
4216
  /** The manifest id of the plugin that published it. */
4218
4217
  readonly plugin: string;
4219
4218
  }
4219
+ /**
4220
+ * How a timeline range is drawn; readers choose colours by it.
4221
+ *
4222
+ * - `"selection"`: a span the user picked to act on, such as a clip to
4223
+ * export. The player's seek bar draws it in the accent colour.
4224
+ * - `"region"`: a part of the media the plugin describes, such as an intro
4225
+ * or a chapter's span. Drawn dimmer, under selections.
4226
+ *
4227
+ * @since 1.6.9
4228
+ * @category Playback
4229
+ */
4230
+ export type RangeStyle = "selection" | "region";
4231
+ /**
4232
+ * One range on the seek bar (player 0.7.6).
4233
+ *
4234
+ * @since 1.6.9
4235
+ * @category Playback
4236
+ */
4237
+ export interface TimelineRange {
4238
+ /** Seconds from the start. */
4239
+ readonly start: number;
4240
+ /** Seconds from the start, after `start`. */
4241
+ readonly end: number;
4242
+ /** How it is drawn. */
4243
+ readonly style: RangeStyle;
4244
+ /** Label, `null` without one. */
4245
+ readonly title: string | null;
4246
+ /** The manifest id of the plugin that published it. */
4247
+ readonly plugin: string;
4248
+ }
4220
4249
  /**
4221
4250
  * This plugin's own marks; disposing it removes them.
4222
4251
  *
@@ -4274,6 +4303,54 @@ export interface TimelineLayer extends Disposable {
4274
4303
  }[], options?: {
4275
4304
  ifCurrent?: PlayerState;
4276
4305
  }): void;
4306
+ /**
4307
+ * Replace the layer's ranges (last write wins; `[]` clears them), spans of
4308
+ * the media drawn on the seek bar (player 0.7.6). The marks and the ranges
4309
+ * of a layer are replaced on their own: `set` keeps the ranges and
4310
+ * `setRanges` keeps the marks. Disposing the layer, its scope or stopping
4311
+ * the plugin removes both, and a new source clears every layer.
4312
+ *
4313
+ * At most 100 ranges (`TIMELINE_LIMITS.ranges`), `0 ≤ start < end`, `end`
4314
+ * within the media while its duration is known, titles ≤ 100 characters.
4315
+ * Ranges may overlap. Failures go to `onError`; with `ifCurrent`, a
4316
+ * write for another source is dropped silently. A title is data or a
4317
+ * {@link Text} the player translates, as in `set`.
4318
+ *
4319
+ * Players before 0.7.6 build `plugin` from an SDK without `setRanges`, so
4320
+ * the call throws a `TypeError` there: declare
4321
+ * `"minPlayerVersion": "0.7.6"`.
4322
+ *
4323
+ * @example Show the clip range on the seek bar while a panel is open
4324
+ * The span the user picked shows on the seek bar in the accent colour
4325
+ * while the plugin's tab is showing, and goes when it is not. Two point
4326
+ * marks would show the ends but not the span between them.
4327
+ * ```ts
4328
+ * declare const panel: Panel;
4329
+ * declare const clip: { start: number; end: number };
4330
+ * // ---cut---
4331
+ * const layer = plugin.timeline.layer();
4332
+ * // Only while the tab is on screen; [] takes the range away.
4333
+ * const show = () =>
4334
+ * layer.setRanges(
4335
+ * panel.state?.effective
4336
+ * ? [{ start: clip.start, end: clip.end, style: "selection", title: "Clip" }]
4337
+ * : [],
4338
+ * { ifCurrent: plugin.player.state },
4339
+ * );
4340
+ * panel.onChange(show);
4341
+ * ```
4342
+ *
4343
+ * @capability timeline.marks
4344
+ * @since 1.6.9
4345
+ */
4346
+ setRanges(ranges: {
4347
+ start: number;
4348
+ end: number;
4349
+ style: RangeStyle;
4350
+ title?: string | Text;
4351
+ }[], options?: {
4352
+ ifCurrent?: PlayerState;
4353
+ }): void;
4277
4354
  }
4278
4355
  /**
4279
4356
  * `plugin.timeline`: marks on the seek bar. Every plugin publishes one
@@ -4304,6 +4381,38 @@ export interface TimelineLayer extends Disposable {
4304
4381
  export interface Timeline {
4305
4382
  /** Every layer merged, by time (`playback.read`). Empty on a new source. */
4306
4383
  readonly marks: readonly TimelineMark[];
4384
+ /**
4385
+ * Every layer's ranges merged, by start (`playback.read`; player 0.7.6:
4386
+ * on older players `ranges` does not exist and reads `undefined`). Empty
4387
+ * on a new source. Kept current like `marks` (with `events`): read it in
4388
+ * a {@link View.bind} function and the view follows it. `onChange` runs
4389
+ * only when the marks change.
4390
+ *
4391
+ * @example Draw the published ranges in a seek bar of your own
4392
+ * A bottom bar draws a band for every range on the seek bar, whoever
4393
+ * published it, as fractions of the duration.
4394
+ * ```ts
4395
+ * declare const bar: View;
4396
+ * // ---cut---
4397
+ * bar.bind(() => {
4398
+ * // Reading `ranges` here runs the binding again when they change.
4399
+ * const duration = plugin.player.state.duration ?? 0;
4400
+ * return {
4401
+ * ranges: duration
4402
+ * ? plugin.timeline.ranges.map((range) => ({
4403
+ * from: range.start / duration,
4404
+ * to: range.end / duration,
4405
+ * selection: range.style === "selection",
4406
+ * }))
4407
+ * : [],
4408
+ * };
4409
+ * });
4410
+ * ```
4411
+ *
4412
+ * @capability playback.read
4413
+ * @since 1.6.9
4414
+ */
4415
+ readonly ranges: readonly TimelineRange[];
4307
4416
  /**
4308
4417
  * The merged list changed (needs `events`; at most every 100 ms).
4309
4418
  *
@@ -6701,12 +6810,19 @@ export interface Osd {
6701
6810
  *
6702
6811
  * @param text - The template, 1–200 characters once translated and
6703
6812
  * formatted; line breaks become spaces.
6813
+ * With `options.actions` (player 0.7.6) the message ends in up to two
6814
+ * buttons, each running one of the plugin's own commands
6815
+ * ({@link OsdShowOptions}). Older players show the message without them.
6816
+ *
6704
6817
  * @param args - Values for its placeholders (player 0.4.3; older players
6705
- * show the template as it is).
6818
+ * show the template as it is). Pass `null` with a `Text` and options.
6819
+ * @param options - Action buttons after the text (SDK 1.6.9).
6706
6820
  * @returns Resolves once the message is on screen.
6707
6821
  *
6708
6822
  * @throws `invalid` (rejects) The shown text is empty, only white space,
6709
- * or longer than 200 characters, or `args` is not a {@link TemplateArgs}.
6823
+ * or longer than 200 characters, or `args` is not a {@link TemplateArgs};
6824
+ * or there are more than 2 actions, a title is empty or longer than 40
6825
+ * characters once translated, or a command is not one of the plugin's.
6710
6826
  * @throws `unavailable` (rejects) The player runs without a user interface.
6711
6827
  * @throws `capability` (rejects) The manifest does not declare `ui.osd`.
6712
6828
  *
@@ -6720,9 +6836,28 @@ export interface Osd {
6720
6836
  * await plugin.osd.show("Speed {}×", [plugin.player.state.rate]);
6721
6837
  * ```
6722
6838
  *
6839
+ * @example Offer "Show in Folder" after saving a file
6840
+ * The OSD reads "Saved clip.mp4 [Show in Folder]" for about 4 seconds,
6841
+ * longer while the pointer is on it. A click runs the plugin's own
6842
+ * `reveal` command, which knows the last saved file; the message goes.
6843
+ * ```ts
6844
+ * declare const path: string;
6845
+ * // ---cut---
6846
+ * let saved = path; // the command acts on the last saved file
6847
+ * plugin.commands.add({
6848
+ * id: "reveal",
6849
+ * title: "Show Last Saved File in Folder",
6850
+ * enabled: () => saved !== "",
6851
+ * run: () => plugin.system.reveal(saved),
6852
+ * });
6853
+ * await plugin.osd.show("Saved {}", [saved.split(/[\\/]/).pop() ?? saved], {
6854
+ * actions: [{ title: "Show in Folder", command: "reveal" }], // one button
6855
+ * });
6856
+ * ```
6857
+ *
6723
6858
  * @since 1.0.0
6724
6859
  */
6725
- show(text: TextValue, args?: TemplateArgs): Promise<void>;
6860
+ show(text: TextValue, args?: TemplateArgs | null, options?: OsdShowOptions): Promise<void>;
6726
6861
  /**
6727
6862
  * Create this plugin's information block.
6728
6863
  *
@@ -6749,6 +6884,54 @@ export interface Osd {
6749
6884
  */
6750
6885
  panel(): OsdPanel;
6751
6886
  }
6887
+ /**
6888
+ * One button at the end of an OSD message ({@link Osd.show}, player 0.7.6).
6889
+ *
6890
+ * @since 1.6.9
6891
+ * @category UI
6892
+ */
6893
+ export interface OsdAction {
6894
+ /**
6895
+ * The button's label: English source text the player translates with the
6896
+ * plugin's catalog, or a {@link Text}; 1–40 characters once translated
6897
+ * (`OSD_LIMITS.actionChars`). A verb, as on a menu item: "Show in
6898
+ * Folder", "Copy", "Undo".
6899
+ */
6900
+ title: TextValue;
6901
+ /**
6902
+ * The local id of one of this plugin's own commands
6903
+ * ({@link CommandOptions.id}), declared when the message is shown. A
6904
+ * click runs it as if the user picked it from a menu: a disabled command
6905
+ * does nothing. The command takes no arguments, so it acts on what the
6906
+ * plugin remembers (the file it just saved).
6907
+ */
6908
+ command: string;
6909
+ }
6910
+ /**
6911
+ * Options of {@link Osd.show} (SDK 1.6.9).
6912
+ *
6913
+ * @remarks
6914
+ * A message with actions stays about 4 seconds instead of 2, and does not
6915
+ * fade while the pointer is on it and moving: it goes 2 seconds after the
6916
+ * pointer leaves or rests (the player's idle time), and at the latest 30
6917
+ * seconds after it showed, hovered or not. A click on a button runs its
6918
+ * command and removes the message.
6919
+ * It is still one line: the buttons follow the text. Buttons take no
6920
+ * keyboard focus; screen readers can press them. A newer message, from any
6921
+ * plugin or the player, replaces the message with its buttons, and the
6922
+ * buttons go when the plugin stops. While its buttons show, the plugin is
6923
+ * not stopped for being idle.
6924
+ *
6925
+ * Players before 0.7.6 ignore `actions` and show the text alone for 2
6926
+ * seconds, so do not make an action the only way to reach something.
6927
+ *
6928
+ * @since 1.6.9
6929
+ * @category UI
6930
+ */
6931
+ export interface OsdShowOptions {
6932
+ /** At most 2 buttons (`OSD_LIMITS.actions`), in order after the text. */
6933
+ actions?: readonly OsdAction[];
6934
+ }
6752
6935
  /**
6753
6936
  * The state of a filter, display layer or render options handle.
6754
6937
  * After `failed` nothing retries automatically until the next `set()`,
package/dist/wire.d.ts CHANGED
@@ -262,6 +262,19 @@ export interface WireMark {
262
262
  title: string | null;
263
263
  plugin: string;
264
264
  }
265
+ /** One timeline range as the player merges them (player 0.7.6). */
266
+ export interface WireRange {
267
+ start: number;
268
+ end: number;
269
+ style: "selection" | "region";
270
+ title: string | null;
271
+ plugin: string;
272
+ }
273
+ /** `timeline.marks` and `timeline.changed`; `ranges` from player 0.7.6. */
274
+ export interface WireTimeline {
275
+ marks: WireMark[];
276
+ ranges?: WireRange[];
277
+ }
265
278
  /** The backdrop as `layout.state` reports it; `null` without one. */
266
279
  export interface LayoutBackdrop {
267
280
  owner: number;
@@ -568,9 +581,7 @@ export declare function createWire(transport: Transport): {
568
581
  };
569
582
  /** Mark layers on the seek bar (`plugin.timeline`). */
570
583
  timeline: {
571
- marks: () => Promise<{
572
- marks: WireMark[];
573
- }>;
584
+ marks: () => Promise<WireTimeline>;
574
585
  set: (marks: {
575
586
  time: number;
576
587
  kind: string;
@@ -578,6 +589,15 @@ export declare function createWire(transport: Transport): {
578
589
  }[], sourceId?: number) => Promise<{
579
590
  applied: boolean;
580
591
  }>;
592
+ /** Player 0.7.6. */
593
+ setRanges: (ranges: {
594
+ start: number;
595
+ end: number;
596
+ style: string;
597
+ title?: string | TextJson;
598
+ }[], sourceId?: number) => Promise<{
599
+ applied: boolean;
600
+ }>;
581
601
  };
582
602
  /** Chapters and the A-B loop of the current media. */
583
603
  marks: {
@@ -666,7 +686,10 @@ export declare function createWire(transport: Transport): {
666
686
  };
667
687
  /** `text` is a template in the plugin's catalog, formatted with `args` (player 0.4.3). */
668
688
  osd: {
669
- show: (text: TextValueJson, args?: TemplateArgsJson) => Promise<void>;
689
+ show: (text: TextValueJson, args?: TemplateArgsJson, actions?: {
690
+ title: TextValueJson;
691
+ command: string;
692
+ }[]) => Promise<void>;
670
693
  panel: (text: TextValueJson | null, args?: TemplateArgsJson, maxColumns?: number) => Promise<void>;
671
694
  };
672
695
  layout: {
package/dist/wire.js CHANGED
@@ -156,6 +156,11 @@ export function createWire(transport) {
156
156
  marks,
157
157
  ...(sourceId === undefined ? {} : { sourceId }),
158
158
  }),
159
+ /** Player 0.7.6. */
160
+ setRanges: (ranges, sourceId) => transport.request("timeline.setRanges", {
161
+ ranges,
162
+ ...(sourceId === undefined ? {} : { sourceId }),
163
+ }),
159
164
  },
160
165
  /** Chapters and the A-B loop of the current media. */
161
166
  marks: {
@@ -245,9 +250,11 @@ export function createWire(transport) {
245
250
  },
246
251
  /** `text` is a template in the plugin's catalog, formatted with `args` (player 0.4.3). */
247
252
  osd: {
248
- show: (text, args) => transport.request("osd.show", {
253
+ show: (text, args, actions) => transport.request("osd.show", {
249
254
  text,
250
255
  ...(args === undefined ? {} : { args }),
256
+ // Player 0.7.6; older players ignore it.
257
+ ...(actions === undefined ? {} : { actions }),
251
258
  }),
252
259
  panel: (text, args, maxColumns) => transport.request("osd.panel", {
253
260
  text,
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.6.7: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
3
+ > @vestra-player/sdk 1.6.9: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
4
4
 
5
5
  ## Read the documentation
6
6
 
@@ -31,12 +31,12 @@
31
31
  - `/API/Commands`: 15 entries: CommandState, Bindable, Condition, When, MenuId, BarButtonIcon, CommandButton, CommandOptions, Command, Commands, HostAction, Host, BindingRef, BindingInfo, Bindings
32
32
  - `/API/Media`: 6 entries: CaptureOptions, Snapshot, ExportOptions, JobState, ExportJob, MediaCapture
33
33
  - `/API/Packages`: 10 entries: PackageManifest, PackageSource, InstallOptions, PackageCandidate, PackageChangeStatus, PackageChange, PluginInfo, PluginDisplay, PluginWarning, Packages
34
- - `/API/Playback`: 18 entries: MarkKind, TimelineMark, TimelineLayer, Timeline, AudioTrack, PlayerState, AudioDevice, Chapter, Conditional, Media, Scrub, StreamFrame, StreamOptions, StreamStartError, Stream,…
34
+ - `/API/Playback`: 20 entries: MarkKind, TimelineMark, RangeStyle, TimelineRange, TimelineLayer, Timeline, AudioTrack, PlayerState, AudioDevice, Chapter, Conditional, Media, Scrub, StreamFrame, StreamOptions,…
35
35
  - `/API/Plugin`: 13 entries: Disposable, PluginError, Scope, Plugin, StopReason, DisposeInfo, WindowState, MainWindow, AppInfo, App, SystemInfo, DisplayInfo, Json
36
36
  - `/API/Processing`: 18 entries: FilterStatus, VideoFilter, AudioFilter, ViewSettings, VideoView, RenderSettings, RenderOptions, VideoShaders, UpscalingReasonCode, UpscalingReason, UpscalingDecodingCause,…
37
37
  - `/API/Services`: 15 entries: Clipboard, Power, ToolResult, Tools, JsonStore, Storage, SessionStorage, System, Secrets, RenderingMode, RenderingPreference, RenderingReasonCode, RenderingReason, RenderingState,…
38
38
  - `/API/Sources`: 7 entries: ResolverOptions, WebResolverOptions, FileResolverOptions, ResolvedStream, Resolver, Resolvers, ResolvedMedia
39
39
  - `/API/Subtitles`: 5 entries: SubtitleTrack, SubtitlesState, SubtitleConditional, Subtitles, SubtitleStyle
40
- - `/API/UI`: 42 entries: ImageRef, ImageOptions, MarkdownRef, MarkdownOptions, PropValue, PropData, Props, Ui, View, Window, Windows, SlotState, SlotPanel, PanelState, PanelSlot, SidebarSlot, Panel,…
41
- - `/API/Testing`: 32 entries: CapabilityInfo, CAPABILITY_INFO, MESSAGE_LIMIT, COMMAND_LIMITS, MENU_IDS, BAR_BUTTON_ICONS, BAR_BUTTON_LIMITS, BuiltinSubmenu, BUILTIN_SUBMENUS, BuiltinCommand, BUILTIN_COMMANDS, LOCALES,…
40
+ - `/API/UI`: 44 entries: ImageRef, ImageOptions, MarkdownRef, MarkdownOptions, PropValue, PropData, Props, Ui, View, Window, Windows, SlotState, SlotPanel, PanelState, PanelSlot, SidebarSlot, Panel,…
41
+ - `/API/Testing`: 33 entries: CapabilityInfo, CAPABILITY_INFO, MESSAGE_LIMIT, COMMAND_LIMITS, MENU_IDS, BAR_BUTTON_ICONS, BAR_BUTTON_LIMITS, BuiltinSubmenu, BUILTIN_SUBMENUS, BuiltinCommand, BUILTIN_COMMANDS, LOCALES,…
42
42
  - `/API/Docs`: 4 entries: DOCS_PAGE_BYTES, DocsOptions, docs, searchDocs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.6.7",
3
+ "version": "1.6.9",
4
4
  "description": "Plugin API types, test host and plugin CLI for Vestra Player (apiVersion vestra/1)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,15 +1,26 @@
1
- // Command buttons of a bottom bar (player 0.5.10, SDK 1.5.29): the buttons
1
+ // Command buttons of the bottom bar (player 0.5.10, SDK 1.5.29): the buttons
2
2
  // that plugins' commands contribute (`commands.add({ button })`) and the
3
- // player's sidebar toggle, rightmost. The player fills `BarButtonsHost` in
4
- // every window it composes and runs the clicks; a bottom bar places one
5
- // `BarButtons` in its own layout and gives it the width it can spare:
6
- // import { BarButtons } from "@vestra/bar-buttons.slint";
7
- // strip := BarButtons { x: parent.width - self.width - 4px; room: ...; }
8
- // It shows as many 41×40 cells as fit in `room`, the strip as wide as the
9
- // cells shown (`width`). Cells go from the strip's outer (left) end inward:
10
- // of the plugins' buttons (ascending `order`, left to right) the lowest
11
- // order goes first, and the sidebar toggle goes last. A bar without a `BarButtons` shows none of them (their menu
12
- // entries stay; the player logs which buttons are hidden).
3
+ // player's own. Since SDK 1.6.8 (menu redesign step 3a) the player draws
4
+ // the strip itself, at the right end of the bottom slot, its 40px cells on
5
+ // the slot's bottom edge (a bar's 40px button row), and also when no plugin
6
+ // mounts a bottom bar: `[plugin buttons…][»][Command Palette][Toggle
7
+ // Sidebar]`. A bottom bar places nothing: it lays its controls out left of
8
+ // the strip and says how wide they must stay:
9
+ // import { BarButtonsHost } from "@vestra/bar-buttons.slint";
10
+ // export component MyBar inherits Rectangle {
11
+ // out property <length> content-min-width: ...; // the least your row needs
12
+ // // your row ends at: root.width - BarButtonsHost.width - 4px
13
+ // }
14
+ // The player gives its strip `bar width - content-min-width - 4px` (all of
15
+ // the width without `content-min-width`), so plugin buttons never cover
16
+ // what the bar needs (its time label, UI-BAR-10). Plugin buttons go left to
17
+ // right by ascending `order`, then plugin id, then registration; at most 8
18
+ // are in the strip, and the ones past 8 or past the room are in the `»`
19
+ // menu, in the same order: the lowest order stays longest. `»` shows only
20
+ // while something is in it; the player's own cells always show.
21
+ //
22
+ // `BarButtons` (below) is deprecated: the strip a bar placed itself before
23
+ // SDK 1.6.8. While a bar still places it, the player draws no second strip.
13
24
  //
14
25
  // The cells look like the bar's own icon buttons: a 32×32 plate on hover
15
26
  // and press, the bar's tooltip (ToolTipArea). Every enabled cell draws its
@@ -43,14 +54,56 @@ export struct BarButtonItem {
43
54
  dimmed: bool,
44
55
  }
45
56
 
46
- // Filled by the player; a bar reads it only through `BarButtons`.
57
+ // Filled by the player. A bar reads `width` (to end its own row left of
58
+ // the strip) and `fixed`; everything else is the player's.
47
59
  export global BarButtonsHost {
48
- // The plugins' buttons, left to right, at most 8.
60
+ // The plugins' buttons for the strip, left to right, at most 8.
49
61
  in property <[BarButtonItem]> buttons;
62
+ // How many plugin buttons are past those 8 (always in the `»` menu).
63
+ in property <int> overflow;
50
64
  // The sidebar toggle (`token` "" while there is none).
51
65
  in property <BarButtonItem> toggle;
52
- // Set when a `BarButtons` exists in this window.
66
+ // The command palette (`host/palette`; `token` "" while there is none).
67
+ in property <BarButtonItem> palette;
68
+ // Set when a (deprecated) `BarButtons` exists in this window.
53
69
  in-out property <bool> placed;
70
+ // The width the player gives its strip (the player sets it).
71
+ in-out property <length> room;
72
+ // The player's own cells, always shown (step 3b adds Adjust).
73
+ private property <int> own: (root.palette.token != "" ? 1 : 0) + (root.toggle.token != "" ? 1 : 0);
74
+ // The cells a bar must always leave free, 41px each: the player's own,
75
+ // and the `»` cell whenever any plugin button exists (drawn only on
76
+ // overflow, but always reserved, so `»` never covers the bar). Never
77
+ // depends on `room`. A bar that keeps an optional control ahead of the
78
+ // plugins' buttons counts it in `content-min-width` while it fits
79
+ // beside these.
80
+ out property <int> fixed: root.own + (root.buttons.length > 0 || root.overflow > 0 ? 1 : 0);
81
+ // The strip's layout for a room, as pure functions, so the player's
82
+ // strip binds them to its own `room` (no frame behind the shell's
83
+ // layout) and the properties below give the same numbers for `room`.
84
+ // The plugin buttons in the strip (the first of `buttons`) at `room`.
85
+ public pure function shown-in(room: length) -> int {
86
+ // Cells left for plugin buttons and `»`.
87
+ let free = Math.max(0, Math.floor(room / 41px) - root.own);
88
+ if root.overflow == 0 && root.buttons.length <= free {
89
+ return root.buttons.length;
90
+ }
91
+ return Math.max(0, Math.min(root.buttons.length, free - 1));
92
+ }
93
+ // Whether `»` shows at `room` (a button past 8 or past the room).
94
+ public pure function more-in(room: length) -> bool {
95
+ return root.shown-in(room) < root.buttons.length || root.overflow > 0;
96
+ }
97
+ // The strip's width at `room` (0 while a bar places `BarButtons`).
98
+ public pure function width-in(room: length) -> length {
99
+ if root.placed {
100
+ return 0px;
101
+ }
102
+ return (root.shown-in(room) + (root.more-in(room) ? 1 : 0) + root.own) * 41px;
103
+ }
104
+ out property <int> shown: root.shown-in(root.room);
105
+ out property <bool> more: root.more-in(root.room);
106
+ out property <length> width: root.width-in(root.room);
54
107
  // A click on the cell with this token.
55
108
  callback clicked(string);
56
109
  }
@@ -127,8 +180,11 @@ component Cell inherits Rectangle {
127
180
  }
128
181
  }
129
182
 
130
- // The strip: place it at the bar's right end and give it the width you can
131
- // spare; it is as wide as the cells it shows (read `width` to lay out the rest).
183
+ // Deprecated (SDK 1.6.8; the player places its strip): the strip a bar
184
+ // placed at its right end with the width it could spare. A bar that still
185
+ // places it shows this one instead of the player's (no palette cell, no
186
+ // `»`): the cells that fit in `room`, lost from its outer (left) end
187
+ // inward, the sidebar toggle last; it is as wide as the cells it shows.
132
188
  export component BarButtons {
133
189
  // The width the bar can give the strip; the cells that fit show.
134
190
  in property <length> room;