Touch interface for phones and tablets; category toolbar for mouse and touch

- touch.rs: gesture recognition (tap, double tap, long press, drag, two-finger pan/pinch/rotate)
- Touch mode: Auto (touch device or first touch) / On / Off in Settings -> Game
- Larger interface factor on touch screens, top bar wraps, smaller minimap, fewer messages
- Toolbar grouped into categories with pop-up rows and a menu button, also on the desktop
- Long press shows information or drops held units; double tap with the hand slaps
- Savegame name via the browser's text dialog when the name field is tapped
- Touch help texts (EN/DE)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 10:10:53 +01:00
1 parent 6f037294c0
commit af6be62805
10 files changed
+744 -152

No files matched your search

+13 -1
View File
@@ -40,6 +40,7 @@ pub enum Item {
RenderScale,
GameSpeed,
Hints,
TouchUi,
}
fn tab_items(tab: usize) -> &'static [Item] {
@@ -48,7 +49,7 @@ fn tab_items(tab: usize) -> &'static [Item] {
0 => &[Language, Volume, MusicVolume, Mute],
1 => &[RotateSpeed, ZoomSpeed, EdgeScroll, InvertMouse],
2 => &[Outlines, WallDetail, Lights, UiScale, RenderScale],
3 => &[GameSpeed, Hints],
3 => &[GameSpeed, Hints, TouchUi],
_ => &[],
}
}
@@ -70,6 +71,7 @@ fn item_label(it: Item) -> String {
Item::RenderScale => "set.render_scale",
Item::GameSpeed => "set.game_speed",
Item::Hints => "set.hints",
Item::TouchUi => "set.touch_ui",
})
}
@@ -108,6 +110,11 @@ fn item_value(it: Item, s: &Settings) -> String {
Item::RenderScale => format!("1/{}", s.render_scale.max(1)),
Item::GameSpeed => format!("{}x", s.game_speed as i32),
Item::Hints => onoff(s.hints),
Item::TouchUi => match s.touch_ui {
1 => tr("val.on"),
2 => tr("val.off"),
_ => trf("val.auto_state", &[tr(if crate::ui::touch_ui() { "val.on" } else { "val.off" })]),
},
}
}
@@ -142,6 +149,7 @@ fn item_change(it: Item, s: &mut Settings, d: i32) {
Item::RenderScale => s.render_scale = cycle(&(1u8..=16).collect::<Vec<u8>>(), s.render_scale.clamp(1, 16), d),
Item::GameSpeed => s.game_speed = cycle(&[1.0f32, 2.0], s.game_speed, d),
Item::Hints => s.hints = !s.hints,
Item::TouchUi => s.touch_ui = cycle(&[0u8, 1, 2], s.touch_ui, d),
}
}
@@ -176,6 +184,7 @@ enum Btn {
Scroll(i32),
FileSave,
FileLoad,
NameField,
}
pub enum MenuAction {
@@ -194,6 +203,7 @@ pub enum MenuAction {
CloudSave(Option<String>, String), // (id to overwrite, name)
CloudLoad(String),
CloudDelete(String),
EditName, // the name field was tapped (touch: ask with a browser dialog)
}
pub struct Menu {
@@ -364,6 +374,7 @@ impl Menu {
self.scroll_by(d * self.rows_visible.max(1) as i32, cloud.list.len());
MenuAction::Consumed
}
Some(Btn::NameField) => MenuAction::EditName,
Some(Btn::FileSave) => MenuAction::FileSave,
Some(Btn::FileLoad) => MenuAction::FileLoad,
}
@@ -599,6 +610,7 @@ impl Menu {
let fw = (inner_w - (fx - inner_x) - bw - CW()).max(CW() * 8.0);
draw_rectangle(fx, top, fw, CH() * 2.0, Color::new(0.02, 0.01, 0.02, 1.0));
draw_rectangle_lines(fx, top, fw, CH() * 2.0, 2.0, FRAME_HI);
self.buttons.push((Rect::new(fx, top, fw, CH() * 2.0), Btn::NameField));
let blink = if (get_time() * 2.0) as i64 % 2 == 0 { "_" } else { " " };
let maxc = ((fw - CW() * 2.0) / CW()).max(1.0) as usize;
let shown: String = {