117 lines
3.5 KiB
Rust
117 lines
3.5 KiB
Rust
//! Aspirational RUIN app API for imperative widget refs, typed shortcuts, and overlay layers.
|
|
//!
|
|
//! Intentionally non-compiling; this is a design sketch.
|
|
|
|
use ruin::prelude::*;
|
|
|
|
#[ruin_runtime::async_main]
|
|
async fn main() -> ruin::Result<()> {
|
|
App::new()
|
|
.window(Window::new().title("RUIN Search").size(1100.0, 760.0))
|
|
.mount::<CommandSearchApp>()
|
|
.run()
|
|
.await
|
|
}
|
|
|
|
#[component]
|
|
fn CommandSearchApp() -> impl View {
|
|
let query = use_signal(String::new);
|
|
let selected_index = use_signal(|| 0usize);
|
|
|
|
let search_input = use_widget_ref::<TextInput>();
|
|
let results_list = use_widget_ref::<ScrollArea>();
|
|
let toast_region = use_widget_ref::<ToastRegion>();
|
|
|
|
let results = use_memo(move || fuzzy::search(COMMANDS, &query.get()));
|
|
|
|
use_mount({
|
|
let search_input = search_input.clone();
|
|
move || search_input.focus()
|
|
});
|
|
|
|
use_shortcut(
|
|
Shortcut::new(Key::Character('K')).with_ctrl(),
|
|
ShortcutScope::Application,
|
|
{
|
|
let search_input = search_input.clone();
|
|
move || search_input.focus()
|
|
},
|
|
);
|
|
|
|
use_shortcut(
|
|
Shortcut::new(Key::ArrowDown),
|
|
ShortcutScope::FocusedWithin(results_list.focus_scope()),
|
|
move || {
|
|
selected_index.update(|index| {
|
|
*index = (*index + 1).min(results.with(|items| items.len().saturating_sub(1)));
|
|
});
|
|
results_list.scroll_selected_into_view();
|
|
},
|
|
);
|
|
|
|
use_shortcut(
|
|
Shortcut::new(Key::Enter),
|
|
ShortcutScope::FocusedItem(results_list.focus_scope()),
|
|
{
|
|
let toast_region = toast_region.clone();
|
|
move || {
|
|
if let Some(command) = results.with(|items| items.get(selected_index.get()).cloned()) {
|
|
toast_region.show(command.title.to_string());
|
|
command.run();
|
|
}
|
|
}
|
|
},
|
|
);
|
|
|
|
view! {
|
|
layers(fill = true) {
|
|
layer(kind = LayerKind::Content) {
|
|
column(fill = true, gap = 16, padding = 20) {
|
|
text(role = TextRole::Heading(1), size = 28, weight = FontWeight::Semibold) {
|
|
"Command search"
|
|
}
|
|
|
|
text_input(
|
|
ref = search_input,
|
|
value = query,
|
|
placeholder = "Search commands...",
|
|
) {}
|
|
|
|
scroll_area(ref = results_list, fill = true) {
|
|
list(gap = 8) {
|
|
for (row_index, command) in results.iter().enumerate() keyed by command.id {
|
|
CommandRow(
|
|
selected = row_index == selected_index.get(),
|
|
title = command.title,
|
|
subtitle = command.subtitle,
|
|
on_press = move |_| command.run(),
|
|
) {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
layer(
|
|
kind = LayerKind::Overlay,
|
|
anchor = Anchor::bottom_right(20.0, 20.0),
|
|
pointer_events = PointerEvents::PassThrough,
|
|
) {
|
|
ToastRegion(ref = toast_region) {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static COMMANDS: &[Command] = &[];
|
|
|
|
struct Command {
|
|
id: &'static str,
|
|
title: &'static str,
|
|
subtitle: &'static str,
|
|
}
|
|
|
|
impl Command {
|
|
fn run(&self) {}
|
|
}
|