mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-28 08:58:07 +00:00
Enable selection from hierarchy
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user