From 66065e0b96f0477c8aa6ee67a4fea1fb61440b95 Mon Sep 17 00:00:00 2001 From: Danny McGee Date: Wed, 6 Dec 2023 18:00:23 -0500 Subject: [PATCH] Enable selection from hierarchy --- studio-frontend/src/app/app.element.scss | 4 +- studio-frontend/src/app/tree.element.scss | 31 ++++++++++++-- studio-frontend/src/app/tree.element.ts | 24 +++++++++-- studio/src/interaction/selection.rs | 51 ++++++++++++++++++----- 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/studio-frontend/src/app/app.element.scss b/studio-frontend/src/app/app.element.scss index 7d60320..f909dd3 100644 --- a/studio-frontend/src/app/app.element.scss +++ b/studio-frontend/src/app/app.element.scss @@ -112,9 +112,7 @@ box-sizing: border-box; width: 290px; max-height: calc(100vh - 32px); - padding: 10px 15px { - inline-end: 30px; - } + padding: 10px 15px; background: #0006; backdrop-filter: blur(16px); diff --git a/studio-frontend/src/app/tree.element.scss b/studio-frontend/src/app/tree.element.scss index 800cf56..c5a2d87 100644 --- a/studio-frontend/src/app/tree.element.scss +++ b/studio-frontend/src/app/tree.element.scss @@ -3,9 +3,14 @@ flex-direction: column; } +:host { + min-width: min-content; +} + header { display: flex; - padding-block: 5px; + align-items: center; + border-radius: 4px; font: { family: "Fira Sans", sans-serif; @@ -20,14 +25,28 @@ header { user-select: none; color: #FFFC; + transition: { + property: background, color; + duration: 0.1s; + timing-function: linear; + } + + &:hover:not(:has(.toggle-expanded:hover)) { + background: #0000001F; + } &.selected { - font-weight: 500; + background: #7A2C34E3; color: #FFF; + font-weight: 500; + + &:hover { + background: #90363F !important; + } } } -.button { +.toggle-expanded { display: flex; align-items: center; justify-content: center; @@ -54,7 +73,11 @@ header { } .name { - padding-inline-start: 5px; + flex: 1 0 0; + padding: { + block: 5px; + inline: 5px 10px; + } } .children { diff --git a/studio-frontend/src/app/tree.element.ts b/studio-frontend/src/app/tree.element.ts index 237ed99..64008d0 100644 --- a/studio-frontend/src/app/tree.element.ts +++ b/studio-frontend/src/app/tree.element.ts @@ -1,5 +1,8 @@ +import { on } from "@storyteller/framework"; +import * as studio from "@storyteller/studio"; +import { type EntitySelectEvent } from "@storyteller/studio"; import { LitElement, html, nothing, unsafeCSS } from "lit"; -import { customElement, property } from "lit/decorators.js"; +import { customElement, property, state } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import "./icon.element"; @@ -20,15 +23,26 @@ export class TreeElement extends LitElement { @property() treeChildren?: Tree[]; @property() expanded = false; + @state() selected = false; + toggleExpanded(): void { this.expanded = !this.expanded; } + @on("window:entity-select") + onEntitySelect({ detail: entity }: EntitySelectEvent): void { + this.selected = this.id === entity; + } + + selectEntity(): void { + studio.select(this.id); + } + // TODO: A11y protected override render = () => html` -
+
${this.treeChildren?.length ? html` - ` : nothing} - + ${this.name}
diff --git a/studio/src/interaction/selection.rs b/studio/src/interaction/selection.rs index fa600fe..7b79cee 100644 --- a/studio/src/interaction/selection.rs +++ b/studio/src/interaction/selection.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, render::mesh::skinning::SkinnedMesh}; use bevy_mod_outline::{ AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume, }; @@ -26,12 +26,13 @@ impl Plugin for SelectionPlugin { ); #[cfg(feature = "wasm")] - app.add_systems( - PostUpdate, - wasm::notify_entity_selection_change - .map(bevy::utils::error) - .run_if(resource_changed_or_removed::()), - ); + app.add_systems(Update, wasm::update_selection_from_frontend) + .add_systems( + PostUpdate, + wasm::notify_entity_selection_change + .map(bevy::utils::error) + .run_if(resource_changed_or_removed::()), + ); } } @@ -116,7 +117,15 @@ fn update_selection( fn draw_selection_outline( mut cmd: Commands, q_selected: Query<&Children, With>, - q_meshes: Query>, Without)>, + q_meshes: Query< + Entity, + ( + With>, + Without, + // FIXME: It shouldn't be necessary to filter out SkinnedMesh here + Without, + ), + >, q_outlines: Query>, ) { // Remove existing outlines @@ -143,13 +152,13 @@ fn draw_selection_outline( #[cfg(feature = "wasm")] mod wasm { use bevy::{ - prelude::{Query, Res, With}, + prelude::*, window::{PrimaryWindow, Window}, }; use wasm_bindgen::prelude::*; - use super::Selection; - use crate::wasm; + use super::{Selected, Selection}; + use crate::wasm::{self, StaticBuffer, WasmMessageBuffer}; #[wasm_bindgen(typescript_custom_section)] const EVENT_DECLARATION: &str = r#" @@ -176,4 +185,24 @@ declare global { let entity = r_selection.map(|selection| **selection); wasm::dispatch_to_js(q_window.single(), "entity-select", &entity) } + + static SELECTION_REQUEST: WasmMessageBuffer = WasmMessageBuffer::new(); + + #[wasm_bindgen] + pub fn select(entity: &str) { + let mut split = entity.split('v'); + let index = split.next().unwrap().parse::().unwrap(); + let generation = split.next().unwrap().parse::().unwrap(); + let bits = (generation as u64) << 32 | index as u64; + + let entity = Entity::from_bits(bits); + SELECTION_REQUEST.write_message(entity); + } + + pub(super) fn update_selection_from_frontend(mut cmd: Commands) { + if let Some(entity) = SELECTION_REQUEST.take_message() { + cmd.insert_resource(Selection(entity)); + cmd.entity(entity).insert(Selected); + } + } }