Skip to content

Commit 735a8ee

Browse files
committed
feat(native): migrate panels to typed Rml bindings
1 parent a542fec commit 735a8ee

62 files changed

Lines changed: 1870 additions & 1378 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎native/src/sbc/chonsole/ui/controller.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl ChonsoleController {
4141
if self.view.process_suggestion_clicks(core) {
4242
self.view.refresh(interface, core)?;
4343
}
44-
self.view.process_suggestion_hovers(interface);
44+
self.view.process_suggestion_hovers();
4545
self.view.update(interface)
4646
}
4747
pub fn draw_screen(&mut self, interface: &NativeInterfaceRef) -> Result<(), Error> {

‎native/src/sbc/chonsole/ui/ui.rml‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,18 @@
1-
<div id="native-chonsole"></div>
1+
<div id="native-chonsole" data-model="chonsole">
2+
<div id="native-chonsole-lines" class="lines">
3+
<div data-for="line : lines" class="line output-line" data-class-input-line="line.muted">{{ line.text }}</div>
4+
</div>
5+
<div id="native-chonsole-suggestions" class="suggestions theme-surface" data-class-hidden="suggestions_hidden">
6+
<div data-for="suggestion : suggestions" data-if="suggestion.visible" class="suggestion" data-class-selected-suggestion="suggestion.selected" data-class-hovered-suggestion="suggestion.highlighted">
7+
<span class="suggestion-command">{{ suggestion.label }}</span>
8+
<span class="suggestion-description">{{ suggestion.detail }}</span>
9+
</div>
10+
</div>
11+
<div id="native-chonsole-suggestion-details" class="suggestion-details theme-surface" data-class-hidden="suggestions_hidden">
12+
<span class="suggestion-details-command">{{ detail_command }}</span>
13+
<span class="suggestion-details-text"> {{ detail_text }}</span>
14+
</div>
15+
<div id="native-chonsole-input" class="input-row theme-surface">
16+
<span class="prompt">&gt;</span><span class="input">{{ input_before }}</span><span class="cursor" data-if="cursor_before_selection">&nbsp;</span><span class="input selection" data-if="has_selection">{{ input_selection }}</span><span class="cursor" data-if="cursor_after_selection">&nbsp;</span><span class="input">{{ input_after }}</span>
17+
</div>
18+
</div>

‎native/src/sbc/chonsole/ui/view.rs‎

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::sbc::chonsole::framework::{
55
ChonsoleLine, ChonsoleLineKind, ChonsoleResponse, TextInput,
66
};
77

8-
use super::view_render::{draw_texture_preview, render_body};
8+
use super::view_render::draw_texture_preview;
99
use super::view_rml::{ChonsoleRml, SuggestionClickQueue, SuggestionHoverQueue};
1010
use super::view_suggestions::SuggestionView;
1111

@@ -28,6 +28,7 @@ pub(super) struct ChonsoleView {
2828
suggestion_hovers: SuggestionHoverQueue,
2929
hovered_suggestion: Option<usize>,
3030
reset_suggestion_scroll: bool,
31+
suggestion_events_dirty: bool,
3132
}
3233

3334
impl ChonsoleView {
@@ -48,7 +49,17 @@ impl ChonsoleView {
4849
}
4950

5051
pub(super) fn update(&mut self, interface: &NativeInterfaceRef) -> Result<(), Error> {
51-
self.rml.update(interface)
52+
self.rml.update(interface)?;
53+
if self.suggestion_events_dirty {
54+
self.rml.bind_suggestion_events(
55+
interface,
56+
self.suggestions.len(),
57+
self.suggestion_clicks.clone(),
58+
self.suggestion_hovers.clone(),
59+
)?;
60+
self.suggestion_events_dirty = false;
61+
}
62+
Ok(())
5263
}
5364

5465
pub(super) fn draw_screen(&mut self, interface: &NativeInterfaceRef) -> Result<(), Error> {
@@ -213,7 +224,7 @@ impl ChonsoleView {
213224
index.is_some_and(|index| self.suggestions.select(core, &mut self.input, index))
214225
}
215226

216-
pub(super) fn process_suggestion_hovers(&mut self, interface: &NativeInterfaceRef) {
227+
pub(super) fn process_suggestion_hovers(&mut self) {
217228
let hovered = self.suggestion_hovers.borrow_mut().pop();
218229
self.suggestion_hovers.borrow_mut().clear();
219230
let Some(hovered) = hovered else {
@@ -222,11 +233,13 @@ impl ChonsoleView {
222233
if hovered == self.hovered_suggestion {
223234
return;
224235
}
225-
self.rml
226-
.set_suggestion_hovered(interface, self.hovered_suggestion, hovered);
227236
self.hovered_suggestion = hovered;
228-
self.rml
229-
.set_suggestion_details(interface, self.suggestions.detail(hovered));
237+
let _ = self
238+
.rml
239+
.set_suggestion_rows(&self.suggestions.rml_rows(hovered));
240+
let _ = self
241+
.rml
242+
.set_suggestion_details(self.suggestions.detail(hovered));
230243
}
231244

232245
pub(super) fn refresh(
@@ -235,7 +248,7 @@ impl ChonsoleView {
235248
core: &ChonsoleCore,
236249
) -> Result<(), Error> {
237250
self.refresh_suggestions(core);
238-
let body = render_body(&self.input, &self.output, &self.suggestions);
251+
let suggestion_rows = self.suggestions.rml_rows(None);
239252
let scroll_top = (!self.reset_suggestion_scroll)
240253
.then(|| self.rml.suggestion_scroll_top(interface))
241254
.flatten();
@@ -244,16 +257,18 @@ impl ChonsoleView {
244257
.selected_index()
245258
.map(|selected| reveal_suggestion_scroll_top(scroll_top.unwrap_or_default(), selected))
246259
.or(scroll_top);
247-
self.rml.refresh(interface, &body, scroll_top)?;
260+
self.rml.refresh(
261+
&self.input,
262+
&self.output,
263+
&suggestion_rows,
264+
self.suggestions.detail(None),
265+
scroll_top,
266+
)?;
248267
self.reset_suggestion_scroll = false;
249268
self.hovered_suggestion = None;
250269
self.suggestion_hovers.borrow_mut().clear();
251-
self.rml.bind_suggestion_events(
252-
interface,
253-
self.suggestions.len(),
254-
self.suggestion_clicks.clone(),
255-
self.suggestion_hovers.clone(),
256-
)
270+
self.suggestion_events_dirty = true;
271+
Ok(())
257272
}
258273

259274
pub(super) fn process_key_up(
Lines changed: 2 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
//! Stateless rendering for the native Chonsole view.
1+
//! Non-Rml drawing for the native Chonsole view.
22
33
use spring_native::prelude::NativeInterfaceRef;
44

5-
use super::view_suggestions::{escape_rml, SuggestionView};
6-
use crate::sbc::chonsole::framework::{ChonsoleLine, ChonsoleLineKind, TextInput};
5+
use crate::sbc::chonsole::framework::TextInput;
76

87
pub(super) fn draw_texture_preview(interface: &NativeInterfaceRef, input: &TextInput) {
98
let Some(texture) = input
@@ -32,79 +31,3 @@ pub(super) fn draw_texture_preview(interface: &NativeInterfaceRef, input: &TextI
3231
let _ = gfx.end_text();
3332
});
3433
}
35-
36-
pub(super) fn render_body(
37-
input: &TextInput,
38-
output: &[ChonsoleLine],
39-
suggestions: &SuggestionView,
40-
) -> String {
41-
let rendered_suggestions = suggestions.render();
42-
let suggestions = if rendered_suggestions.is_empty() {
43-
String::new()
44-
} else {
45-
format!(
46-
r#"<div id="native-chonsole-suggestions" class="suggestions theme-surface">{rendered_suggestions}</div><div id="native-chonsole-suggestion-details" class="suggestion-details theme-surface">{}</div>"#,
47-
render_suggestion_details(suggestions.detail(None)),
48-
)
49-
};
50-
format!(
51-
r#"<div id="native-chonsole-lines" class="lines">{}</div>{}<div id="native-chonsole-input" class="input-row theme-surface"><span class="prompt">&gt;</span>{}</div>"#,
52-
render_lines(output),
53-
suggestions,
54-
render_input(input),
55-
)
56-
}
57-
58-
/// Render the full command description separately from its compact list row.
59-
/// This strip has a fixed footprint, so selecting or hovering a command never
60-
/// shifts the suggestion list or its scrollbar.
61-
pub(super) fn render_suggestion_details(details: Option<(&str, &str)>) -> String {
62-
let Some((command, description)) = details else {
63-
return String::new();
64-
};
65-
format!(
66-
r#"<span class="suggestion-details-command">{}</span><span class="suggestion-details-text"> {}</span>"#,
67-
escape_rml(command),
68-
escape_rml(description),
69-
)
70-
}
71-
72-
fn render_lines(output: &[ChonsoleLine]) -> String {
73-
output
74-
.iter()
75-
.map(|line| {
76-
let class = match line.kind {
77-
ChonsoleLineKind::Input => "line input-line",
78-
ChonsoleLineKind::Output => "line output-line",
79-
};
80-
format!(r#"<div class="{class}">{}</div>"#, escape_rml(&line.text))
81-
})
82-
.collect::<Vec<_>>()
83-
.join("")
84-
}
85-
86-
fn render_input(input: &TextInput) -> String {
87-
let value = input.value();
88-
match input.selection_range() {
89-
Some((start, end)) if input.cursor() == start => format!(
90-
r#"<span class="input">{}</span><span class="cursor">&nbsp;</span><span class="input selection">{}</span><span class="input">{}</span>"#,
91-
escape_rml(&value[..start]),
92-
escape_rml(&value[start..end]),
93-
escape_rml(&value[end..]),
94-
),
95-
Some((start, end)) => format!(
96-
r#"<span class="input">{}</span><span class="input selection">{}</span><span class="cursor">&nbsp;</span><span class="input">{}</span>"#,
97-
escape_rml(&value[..start]),
98-
escape_rml(&value[start..end]),
99-
escape_rml(&value[end..]),
100-
),
101-
None => {
102-
let (before, after) = value.split_at(input.cursor());
103-
format!(
104-
r#"<span class="input">{}</span><span class="cursor">&nbsp;</span><span class="input">{}</span>"#,
105-
escape_rml(before),
106-
escape_rml(after),
107-
)
108-
}
109-
}
110-
}

0 commit comments

Comments
 (0)