Skip to main content

launcher/widgets/
play_quit_button.rs

1use eframe::egui;
2
3#[derive(PartialEq)]
4pub enum FooterAction {
5    None,
6    Play,
7    Quit,
8}
9
10pub fn play_quit_button(
11    ui: &mut egui::Ui,
12    should_focus: bool,
13    play_enabled: bool,
14    play_tooltip: Option<String>,
15    play_label: &str,
16) -> FooterAction {
17    let height = 32.0;
18    let gap = 6.0;
19
20    // Fill full width
21    let desired_size = egui::vec2(ui.available_width(), height);
22    let (total_rect, _) = ui.allocate_exact_size(desired_size, egui::Sense::hover());
23
24    // Split ratio (45% / 55%)
25    let split_ratio = 0.45;
26    let split_x = total_rect.min.x + total_rect.width() * split_ratio;
27
28    // Apply gap by shrinking both sides
29    let quit_rect = egui::Rect::from_min_max(
30        total_rect.min,
31        egui::pos2(split_x - gap * 0.5, total_rect.max.y),
32    );
33
34    let play_rect = egui::Rect::from_min_max(
35        egui::pos2(split_x + gap * 0.5, total_rect.min.y),
36        total_rect.max,
37    );
38
39    let quit_id = ui.make_persistent_id("footer_quit");
40    let play_id = ui.make_persistent_id("footer_play");
41
42    let quit_resp = ui.interact(quit_rect, quit_id, egui::Sense::click());
43
44    // Only interact with Play if enabled
45    let play_sense = if play_enabled {
46        egui::Sense::click()
47    } else {
48        egui::Sense::hover()
49    };
50    let mut play_resp = ui.interact(play_rect, play_id, play_sense);
51
52    if let Some(tooltip) = play_tooltip {
53        play_resp = play_resp.on_hover_text(tooltip);
54    }
55
56    if should_focus && play_enabled {
57        play_resp.request_focus();
58    }
59
60    // Painting. The web buttons carry 4 px corners, the primary one is
61    // accent-filled, the secondary one wears the chip tone.
62    if ui.is_rect_visible(total_rect) {
63        let rounding = egui::CornerRadius::same(4);
64
65        // -----------
66        // Quit button
67        // -----------
68        let palette = crate::theme::palette(ui.ctx());
69        let quit_bg = if quit_resp.hovered() {
70            crate::theme::hover_fill(palette)
71        } else {
72            palette.chip
73        };
74        let quit_text_color = palette.chip_ink;
75
76        ui.painter().rect_filled(quit_rect, rounding, quit_bg);
77
78        let quit_galley = ui.painter().layout_no_wrap(
79            "Quit".to_string(),
80            egui::FontId::proportional(15.0),
81            quit_text_color,
82        );
83
84        ui.painter().galley(
85            quit_rect.center() - quit_galley.size() / 2.0,
86            quit_galley,
87            quit_text_color,
88        );
89
90        if quit_resp.has_focus() {
91            ui.painter().rect_stroke(
92                quit_rect,
93                rounding,
94                egui::Stroke::new(1.0, palette.accent),
95                egui::StrokeKind::Inside,
96            );
97        }
98
99        // -----------
100        // Play button
101        // -----------
102        let play_bg = if !play_enabled {
103            ui.visuals().widgets.noninteractive.bg_fill
104        } else {
105            palette.accent
106        };
107
108        let play_content_color = if !play_enabled {
109            ui.visuals().widgets.noninteractive.fg_stroke.color
110        } else {
111            palette.accent_ink
112        };
113
114        ui.painter().rect_filled(play_rect, rounding, play_bg);
115
116        let play_galley = ui.painter().layout_no_wrap(
117            // `play_label` is "🎮 Play!" normally and "🌐 Online" while
118            // an enabled tournament mod has a selection;
119            // the disabled fallback stays here so the caller can't forget it.
120            if play_enabled {
121                play_label.to_owned()
122            } else {
123                "No Game".to_string()
124            },
125            egui::FontId::proportional(16.0),
126            play_content_color,
127        );
128
129        ui.painter().galley(
130            play_rect.center() - play_galley.size() / 2.0,
131            play_galley,
132            play_content_color,
133        );
134
135        if play_resp.has_focus() {
136            ui.painter().rect_stroke(
137                play_rect,
138                rounding,
139                egui::Stroke::new(1.0, palette.accent_ink),
140                egui::StrokeKind::Inside,
141            );
142        }
143    }
144
145    if quit_resp.clicked() {
146        FooterAction::Quit
147    } else if play_enabled && play_resp.clicked() {
148        FooterAction::Play
149    } else {
150        FooterAction::None
151    }
152}