Skip to content

Commit 26910a4

Browse files
committed
Track the engine's new callin and coordinate contracts
The engine grew explicit parameters on several callins and moved mouse callbacks onto Lua's coordinate contract. Follow both, and use the new data rather than re-deriving it. Callins: update takes a frame delta, draw_screen* take the view size, and key_press/key_release carry modifiers, label, utf32 and the resolved action list. Modifier flags now thread through the event dispatcher as KeyMods, replacing the get_mod_key_state() polls that read live SDL state a tick after the key they were matching. add_grass takes a grass value, so the map writer stores each cell's real byte and the brush writes its target level instead of a binary on/off. Coordinates: mouse callbacks are now view-relative and bottom-origin, the same space GetMouseState and the ray query already used. Traces therefore stop flipping, while the panel and chonsole managers flip once on entry for RmlUi's top-origin layout, and the control API converts explicitly because its clients address the window like a screenshot. E2E: park the cursor 40px above the window's bottom edge. EdgeMoveWidth is a fraction of the view (~4px here), so parking at height-4 sat inside the edge-scroll band and walked the camera south before most captures.
1 parent 429ecde commit 26910a4

55 files changed

Lines changed: 1150 additions & 439 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/commands/executor.rs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ impl CommandExecutor {
7777
width,
7878
height,
7979
&export.output,
80-
true,
81-
true,
82-
export.grayscale16,
80+
spring_native::GfxSaveImageOptions {
81+
alpha: true,
82+
yflip: true,
83+
grayscale16bit: export.grayscale16,
84+
},
8385
0x8CE0,
8486
);
8587
});

‎native/src/sbc/chonsole/event_listener.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use spring_native::prelude::Error;
55
use super::model::ChonsoleManager;
66
use crate::sbc::command_system::model::Models;
77
use crate::sbc::events::{Event, EventListener, EventListenerFactory, ListenerId};
8+
use crate::sbc::keys::KeyMods;
89

910
inventory::submit! { EventListenerFactory { make: |_| Box::new(ChonsoleTextEvents) } }
1011
inventory::submit! { EventListenerFactory { make: |_| Box::new(ChonsoleEvents) } }
@@ -28,8 +29,9 @@ impl EventListener for ChonsoleTextEvents {
2829
key_code: i32,
2930
_scan_code: i32,
3031
_is_repeat: bool,
32+
mods: KeyMods,
3133
) -> Result<bool, Error> {
32-
models.get::<ChonsoleManager>().text_key(key_code)
34+
models.get::<ChonsoleManager>().text_key(key_code, mods)
3335
}
3436
}
3537

@@ -68,17 +70,19 @@ impl EventListener for ChonsoleEvents {
6870
key_code: i32,
6971
scan_code: i32,
7072
is_repeat: bool,
73+
mods: KeyMods,
7174
) -> Result<bool, Error> {
7275
models
7376
.get::<ChonsoleManager>()
74-
.key_press(key_code, scan_code, is_repeat)
77+
.key_press(key_code, scan_code, is_repeat, mods)
7578
}
7679

7780
fn key_release(
7881
&mut self,
7982
models: &mut Models,
8083
key_code: i32,
8184
scan_code: i32,
85+
_mods: KeyMods,
8286
) -> Result<bool, Error> {
8387
models
8488
.get::<ChonsoleManager>()

‎native/src/sbc/chonsole/integration/integration.rs‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,12 @@ fn unitrules_lists_and_sets_selected_units(ctx: &mut TestCtx) -> Result<(), Stri
155155
},
156156
0,
157157
team,
158-
false,
159-
false,
160-
-1,
161-
-1,
158+
spring_native::CreateUnitOptions {
159+
build: false,
160+
flatten_ground: false,
161+
unit_id: -1,
162+
builder_id: -1,
163+
},
162164
)
163165
.map_err(|err| format!("create test unit: {err:?}"))?;
164166
interface

‎native/src/sbc/chonsole/model.rs‎

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use super::commands::{CatalogRefresher, ChonsoleCore, CommandExecutor, CommandRe
66
use super::framework::{ChonsoleResponse, ChonsoleSuggestion, HistoryStore};
77
use super::ui::{ChonsoleController, UiKeyOutcome};
88
use crate::sbc::command_system::model::{Model, ModelFactory};
9+
use crate::sbc::keys::KeyMods;
910
use crate::sbc::port_flags::{self, PortImpl};
1011

1112
inventory::submit! { ModelFactory { make: |iface| Box::new(ChonsoleManager::new(iface)) } }
@@ -128,14 +129,19 @@ impl ChonsoleManager {
128129
key_code: i32,
129130
scan_code: i32,
130131
is_repeat: bool,
132+
mods: KeyMods,
131133
) -> Result<bool, Error> {
132134
if !self.enabled {
133135
return Ok(false);
134136
}
135-
match self
136-
.ui
137-
.key_press(&self.interface, &self.core, key_code, scan_code, is_repeat)?
138-
{
137+
match self.ui.key_press(
138+
&self.interface,
139+
&self.core,
140+
key_code,
141+
scan_code,
142+
is_repeat,
143+
mods,
144+
)? {
139145
UiKeyOutcome::Unhandled => Ok(false),
140146
UiKeyOutcome::Handled => Ok(true),
141147
UiKeyOutcome::Execute(input) => {
@@ -146,11 +152,12 @@ impl ChonsoleManager {
146152
}
147153
}
148154

149-
pub fn text_key(&mut self, key_code: i32) -> Result<bool, Error> {
155+
pub fn text_key(&mut self, key_code: i32, mods: KeyMods) -> Result<bool, Error> {
150156
if !self.enabled {
151157
return Ok(false);
152158
}
153-
self.ui.text_key(&self.interface, &self.core, key_code)
159+
self.ui
160+
.text_key(&self.interface, &self.core, key_code, mods)
154161
}
155162

156163
pub fn key_release(&mut self, key_code: i32, scan_code: i32) -> Result<bool, Error> {
@@ -179,20 +186,23 @@ impl ChonsoleManager {
179186
return Ok(false);
180187
}
181188
let _ = (dx, dy, button);
189+
let y = self.rml_y(y);
182190
self.ui.mouse_move(&self.interface, x, y)
183191
}
184192

185193
pub fn mouse_press(&mut self, x: i32, y: i32, button: i32) -> Result<bool, Error> {
186194
if !self.enabled {
187195
return Ok(false);
188196
}
197+
let y = self.rml_y(y);
189198
self.ui.mouse_press(&self.interface, x, y, button)
190199
}
191200

192201
pub fn mouse_release(&mut self, x: i32, y: i32, button: i32) -> Result<(), Error> {
193202
if !self.enabled {
194203
return Ok(());
195204
}
205+
let y = self.rml_y(y);
196206
self.ui.mouse_release(&self.interface, x, y, button)
197207
}
198208

@@ -203,6 +213,15 @@ impl ChonsoleManager {
203213
self.ui.mouse_wheel(&self.interface, up, value)
204214
}
205215

216+
/// Mouse callbacks report the engine's bottom-origin y; the view hit tests
217+
/// against RmlUi's top-origin layout.
218+
fn rml_y(&self, y: i32) -> i32 {
219+
match self.interface.display().get_view_geometry() {
220+
Ok(geometry) => geometry.viewSizeY - 1 - y,
221+
Err(_) => y,
222+
}
223+
}
224+
206225
fn persist_history_change(&self, previous_history: &[String]) {
207226
let Some(store) = &self.history_store else {
208227
return;

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::events::{ChonsoleEvents, KeyOutcome};
44
use super::view::ChonsoleView;
55
use crate::sbc::chonsole::commands::ChonsoleCore;
66
use crate::sbc::chonsole::framework::ChonsoleResponse;
7+
use crate::sbc::keys::KeyMods;
78

89
#[derive(Default)]
910
pub struct ChonsoleController {
@@ -54,16 +55,14 @@ impl ChonsoleController {
5455
key_code: i32,
5556
scan_code: i32,
5657
is_repeat: bool,
58+
mods: KeyMods,
5759
) -> Result<UiKeyOutcome, Error> {
60+
let _ = (scan_code, is_repeat);
5861
Ok(
59-
match self.events.key_press(
60-
interface,
61-
core,
62-
&mut self.view,
63-
key_code,
64-
scan_code,
65-
is_repeat,
66-
)? {
62+
match self
63+
.events
64+
.key_press(interface, core, &mut self.view, key_code, mods)?
65+
{
6766
KeyOutcome::Unhandled => UiKeyOutcome::Unhandled,
6867
KeyOutcome::Handled => UiKeyOutcome::Handled,
6968
KeyOutcome::Execute(input) => UiKeyOutcome::Execute(input),
@@ -75,9 +74,10 @@ impl ChonsoleController {
7574
interface: &NativeInterfaceRef,
7675
core: &ChonsoleCore,
7776
key_code: i32,
77+
mods: KeyMods,
7878
) -> Result<bool, Error> {
7979
self.events
80-
.text_key(interface, core, &mut self.view, key_code)
80+
.text_key(interface, core, &mut self.view, key_code, mods)
8181
}
8282
pub fn hide(&mut self, interface: &NativeInterfaceRef) -> Result<(), Error> {
8383
self.view.set_visible(interface, false)

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

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use spring_native::prelude::{Error, NativeInterfaceRef};
22

33
use crate::sbc::chonsole::commands::ChonsoleCore;
4-
use crate::sbc::keys::is_key;
4+
use crate::sbc::keys::{is_key, KeyMods};
55

66
use super::view::ChonsoleView;
77

@@ -28,10 +28,8 @@ impl ChonsoleEvents {
2828
core: &ChonsoleCore,
2929
view: &mut ChonsoleView,
3030
key_code: i32,
31-
scan_code: i32,
32-
is_repeat: bool,
31+
key_mods: KeyMods,
3332
) -> Result<KeyOutcome, Error> {
34-
let _ = (scan_code, is_repeat);
3533
self.modifiers.key_down(interface, key_code);
3634
if is_key(interface, key_code, "f10") {
3735
view.toggle(interface)?;
@@ -55,10 +53,10 @@ impl ChonsoleEvents {
5553
if is_key(interface, key_code, "enter") || is_key(interface, key_code, "numpad_enter") {
5654
return Ok(KeyOutcome::Execute(view.take_input()));
5755
}
58-
if self.text_key(interface, core, view, key_code)? {
56+
if self.text_key(interface, core, view, key_code, key_mods)? {
5957
return Ok(KeyOutcome::Handled);
6058
}
61-
let mods = ModState::read(interface, self.modifiers);
59+
let mods = ModState::read(key_mods, self.modifiers);
6260
if mods.ctrl && is_key(interface, key_code, "u") {
6361
self.reset_history_cursor();
6462
view.delete_to_start();
@@ -171,8 +169,9 @@ impl ChonsoleEvents {
171169
core: &ChonsoleCore,
172170
view: &mut ChonsoleView,
173171
key_code: i32,
172+
key_mods: KeyMods,
174173
) -> Result<bool, Error> {
175-
if !view.visible() || !ModState::read(interface, self.modifiers).ctrl {
174+
if !view.visible() || !ModState::read(key_mods, self.modifiers).ctrl {
176175
return Ok(false);
177176
}
178177
if is_key(interface, key_code, "a") {
@@ -272,14 +271,10 @@ struct ModState {
272271
}
273272

274273
impl ModState {
275-
fn read(interface: &NativeInterfaceRef, tracked: TrackedModifiers) -> Self {
276-
let (_, ctrl, _, shift) = interface
277-
.input()
278-
.get_mod_key_state()
279-
.unwrap_or((false, false, false, false));
274+
fn read(mods: KeyMods, tracked: TrackedModifiers) -> Self {
280275
ModState {
281-
shift: tracked.shift || shift,
282-
ctrl: tracked.ctrl || ctrl,
276+
shift: tracked.shift || mods.shift,
277+
ctrl: tracked.ctrl || mods.ctrl,
283278
}
284279
}
285280
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl ChonsoleRml {
127127
rml.document_set_title(document, "Native Chonsole")?;
128128
rml.document_append_to_style_sheet(document, UI_STYLE)?;
129129
rml.element_set_inner_rml(document, UI_BODY)?;
130-
rml.document_show(document, None, None)?;
130+
rml.document_show(document, spring_native::RmlDocumentShowOptions::default())?;
131131
self.context = Some(context);
132132
self.document = Some(document);
133133
self.root = element_by_id(interface, document, "native-chonsole");
@@ -190,7 +190,7 @@ impl ChonsoleRml {
190190
};
191191
let rml = interface.rml_ui();
192192
if visible {
193-
rml.document_show(document, None, None)?;
193+
rml.document_show(document, spring_native::RmlDocumentShowOptions::default())?;
194194
if let Some(context) = self.context {
195195
// Keep the engine/editor cursor stable over console controls.
196196
let _ = rml.context_enable_mouse_cursor(context, false);

‎native/src/sbc/control/api/camera.rs‎

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
33
use serde::Deserialize;
44
use serde_json::json;
5+
56
use spring_native::prelude::NativeInterfaceRef;
67

78
use crate::sbc::sbc::SBC;
9+
use crate::sbc::states::trace::{flip_screen_y, trace_editor_ground, trace_ground};
810

911
use super::super::{ControlError, Handled, Reply};
1012

@@ -162,7 +164,13 @@ pub(crate) fn set(sbc: &mut SBC, params: Set) -> Handled {
162164
if let Some([x, y, z]) = params.target {
163165
let target = spring_native::prelude::sys::Float3 { x, y, z };
164166
camera
165-
.set_camera_target(target, params.transition)
167+
.set_camera_target(
168+
target,
169+
spring_native::SetCameraTargetOptions {
170+
transition_time: Some(params.transition),
171+
..Default::default()
172+
},
173+
)
166174
.map_err(|err| ControlError::failed(format!("set_camera_target: {err:?}")))?;
167175
}
168176
get(sbc)
@@ -181,7 +189,8 @@ pub(crate) fn zoom(sbc: &mut SBC, params: Zoom) -> Handled {
181189
"camera.zoom screen coordinates must be finite",
182190
));
183191
}
184-
trace_ground(interface, screen)?
192+
trace_ground(interface, screen[0], engine_screen_y(interface, screen[1]))
193+
.map(|hit| [hit.x, hit.y, hit.z])
185194
} else {
186195
None
187196
};
@@ -211,7 +220,10 @@ pub(crate) fn zoom(sbc: &mut SBC, params: Zoom) -> Handled {
211220
// point beneath the cursor. Re-trace after scaling and apply the
212221
// horizontal ground delta in the controller, where the engine owns the
213222
// camera projection and terrain height.
214-
if let Some(current) = trace_ground(interface, screen)? {
223+
if let Some(current) =
224+
trace_ground(interface, screen[0], engine_screen_y(interface, screen[1]))
225+
.map(|hit| [hit.x, hit.y, hit.z])
226+
{
215227
let mut state = camera
216228
.get_camera_state(false)
217229
.map_err(|err| ControlError::failed(format!("get_camera_state: {err:?}")))?;
@@ -234,25 +246,21 @@ pub(crate) fn trace(sbc: &mut SBC, params: Trace) -> Handled {
234246
"camera.trace screen coordinates must be finite",
235247
));
236248
}
237-
let (hit_type, hit_id, position) = sbc
238-
.interface()
239-
.camera()
240-
.trace_screen_ray(x, y, true, false, false, true, 0.0)
249+
let interface = sbc.interface();
250+
let trace = trace_editor_ground(interface, x, engine_screen_y(interface, y))
241251
.map_err(|err| ControlError::failed(format!("trace_screen_ray: {err:?}")))?;
242252
Ok(Reply::now(json!({
243-
"hit_type": hit_type,
244-
"hit_id": hit_id,
245-
"position": [position.x, position.y, position.z],
253+
"hit_type": trace.hit_type,
254+
"hit_id": trace.hit_id,
255+
"position": [trace.position.x, trace.position.y, trace.position.z],
246256
})))
247257
}
248258

249-
fn trace_ground(
250-
interface: &NativeInterfaceRef,
251-
screen: [f32; 2],
252-
) -> Result<Option<[f32; 3]>, ControlError> {
253-
let (hit_type, _, position) = interface
254-
.camera()
255-
.trace_screen_ray(screen[0], screen[1], true, false, false, true, 0.0)
256-
.map_err(|err| ControlError::failed(format!("trace_screen_ray: {err:?}")))?;
257-
Ok((hit_type == 3).then_some([position.x, position.y, position.z]))
259+
/// Control clients address the window the way a screenshot does: top-origin.
260+
/// The engine's screen space is bottom-origin, so convert on the way in.
261+
fn engine_screen_y(interface: &NativeInterfaceRef, y: f32) -> f32 {
262+
match interface.display().get_view_geometry() {
263+
Ok(geometry) => flip_screen_y(geometry.viewSizeY as f32, y),
264+
Err(_) => y,
265+
}
258266
}

0 commit comments

Comments
 (0)