Enable selection from hierarchy

This commit is contained in:
Danny McGee
2023-12-06 18:00:23 -05:00
parent 52c46d3efc
commit 66065e0b96
4 changed files with 88 additions and 22 deletions
+1 -3
View File
@@ -112,9 +112,7 @@
box-sizing: border-box; box-sizing: border-box;
width: 290px; width: 290px;
max-height: calc(100vh - 32px); max-height: calc(100vh - 32px);
padding: 10px 15px { padding: 10px 15px;
inline-end: 30px;
}
background: #0006; background: #0006;
backdrop-filter: blur(16px); backdrop-filter: blur(16px);
+27 -4
View File
@@ -3,9 +3,14 @@
flex-direction: column; flex-direction: column;
} }
:host {
min-width: min-content;
}
header { header {
display: flex; display: flex;
padding-block: 5px; align-items: center;
border-radius: 4px;
font: { font: {
family: "Fira Sans", sans-serif; family: "Fira Sans", sans-serif;
@@ -20,14 +25,28 @@ header {
user-select: none; user-select: none;
color: #FFFC; color: #FFFC;
transition: {
property: background, color;
duration: 0.1s;
timing-function: linear;
}
&:hover:not(:has(.toggle-expanded:hover)) {
background: #0000001F;
}
&.selected { &.selected {
font-weight: 500; background: #7A2C34E3;
color: #FFF; color: #FFF;
font-weight: 500;
&:hover {
background: #90363F !important;
}
} }
} }
.button { .toggle-expanded {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -54,7 +73,11 @@ header {
} }
.name { .name {
padding-inline-start: 5px; flex: 1 0 0;
padding: {
block: 5px;
inline: 5px 10px;
}
} }
.children { .children {
+20 -4
View File
@@ -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 { 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 { classMap } from "lit/directives/class-map.js";
import "./icon.element"; import "./icon.element";
@@ -20,15 +23,26 @@ export class TreeElement extends LitElement {
@property() treeChildren?: Tree[]; @property() treeChildren?: Tree[];
@property() expanded = false; @property() expanded = false;
@state() selected = false;
toggleExpanded(): void { toggleExpanded(): void {
this.expanded = !this.expanded; 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 // TODO: A11y
protected override render = () => html` protected override render = () => html`
<header> <header class=${classMap({ selected: this.selected })}>
${this.treeChildren?.length ? html` ${this.treeChildren?.length ? html`
<button class="button" <button class="toggle-expanded"
@click=${this.toggleExpanded} @click=${this.toggleExpanded}
> >
<sts-icon <sts-icon
@@ -41,7 +55,9 @@ export class TreeElement extends LitElement {
></sts-icon> ></sts-icon>
</button> </button>
` : nothing} ` : nothing}
<span class="name"> <span class="name"
@click=${this.selectEntity}
>
${this.name} ${this.name}
</span> </span>
</header> </header>
+40 -11
View File
@@ -1,4 +1,4 @@
use bevy::prelude::*; use bevy::{prelude::*, render::mesh::skinning::SkinnedMesh};
use bevy_mod_outline::{ use bevy_mod_outline::{
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume, AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin, OutlineVolume,
}; };
@@ -26,12 +26,13 @@ impl Plugin for SelectionPlugin {
); );
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
app.add_systems( app.add_systems(Update, wasm::update_selection_from_frontend)
PostUpdate, .add_systems(
wasm::notify_entity_selection_change PostUpdate,
.map(bevy::utils::error) wasm::notify_entity_selection_change
.run_if(resource_changed_or_removed::<Selection>()), .map(bevy::utils::error)
); .run_if(resource_changed_or_removed::<Selection>()),
);
} }
} }
@@ -116,7 +117,15 @@ fn update_selection(
fn draw_selection_outline( fn draw_selection_outline(
mut cmd: Commands, mut cmd: Commands,
q_selected: Query<&Children, With<Selected>>, q_selected: Query<&Children, With<Selected>>,
q_meshes: Query<Entity, (With<Handle<Mesh>>, Without<OutlineVolume>)>, q_meshes: Query<
Entity,
(
With<Handle<Mesh>>,
Without<OutlineVolume>,
// FIXME: It shouldn't be necessary to filter out SkinnedMesh here
Without<SkinnedMesh>,
),
>,
q_outlines: Query<Entity, With<OutlineVolume>>, q_outlines: Query<Entity, With<OutlineVolume>>,
) { ) {
// Remove existing outlines // Remove existing outlines
@@ -143,13 +152,13 @@ fn draw_selection_outline(
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
mod wasm { mod wasm {
use bevy::{ use bevy::{
prelude::{Query, Res, With}, prelude::*,
window::{PrimaryWindow, Window}, window::{PrimaryWindow, Window},
}; };
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use super::Selection; use super::{Selected, Selection};
use crate::wasm; use crate::wasm::{self, StaticBuffer, WasmMessageBuffer};
#[wasm_bindgen(typescript_custom_section)] #[wasm_bindgen(typescript_custom_section)]
const EVENT_DECLARATION: &str = r#" const EVENT_DECLARATION: &str = r#"
@@ -176,4 +185,24 @@ declare global {
let entity = r_selection.map(|selection| **selection); let entity = r_selection.map(|selection| **selection);
wasm::dispatch_to_js(q_window.single(), "entity-select", &entity) wasm::dispatch_to_js(q_window.single(), "entity-select", &entity)
} }
static SELECTION_REQUEST: WasmMessageBuffer<Entity> = WasmMessageBuffer::new();
#[wasm_bindgen]
pub fn select(entity: &str) {
let mut split = entity.split('v');
let index = split.next().unwrap().parse::<u32>().unwrap();
let generation = split.next().unwrap().parse::<u32>().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);
}
}
} }