Refactor main.rs into focused modules: render, editor, glyph_picker
Splits the flat 432-line main.rs into four modules so each file has one clear job: render.rs owns layout constants and drawing primitives, editor.rs owns EditorTab/EditorState and the side panel, glyph_picker.rs owns the floating picker dialog with a decoupled (open, glyph) interface, and main.rs is reduced to AppMode, App, and the frame loop (~150 lines). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+117
@@ -0,0 +1,117 @@
|
||||
use eframe::egui;
|
||||
use egui::{Align2, FontId, Sense, vec2};
|
||||
|
||||
use crate::game::{Archetype, Board, Glyph, ALL_ARCHETYPES};
|
||||
use crate::render::{CELL_H, CELL_W, PALETTE_PANEL_W};
|
||||
|
||||
/// Which tab is active in the editor side panel.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum EditorTab {
|
||||
/// Archetype + glyph palette for painting cells.
|
||||
Palette,
|
||||
/// Board-level properties (placeholder).
|
||||
Board,
|
||||
/// World-level properties (placeholder).
|
||||
World,
|
||||
}
|
||||
|
||||
/// Transient editor state. Only meaningful when the app is in Edit mode.
|
||||
pub(crate) struct EditorState {
|
||||
/// The archetype that will be stamped when the user clicks a cell.
|
||||
pub(crate) selected: Archetype,
|
||||
/// The glyph (character + colors) to stamp. Independent of the archetype's default.
|
||||
pub(crate) glyph: Glyph,
|
||||
/// Whether the glyph picker dialog is currently open.
|
||||
pub(crate) glyph_picker_open: bool,
|
||||
/// Which side-panel tab is currently shown.
|
||||
pub(crate) tab: EditorTab,
|
||||
}
|
||||
|
||||
impl EditorState {
|
||||
/// Creates editor state with `Wall` selected and the picker closed.
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
selected: Archetype::Wall,
|
||||
glyph: Archetype::Wall.default_glyph(),
|
||||
glyph_picker_open: false,
|
||||
tab: EditorTab::Palette,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the editor side panel (must be called before `CentralPanel`).
|
||||
///
|
||||
/// Shows tab bar (Palette / Board / World) and, on the Palette tab, the
|
||||
/// scrollable archetype list and current-glyph preview button.
|
||||
pub(crate) fn show_editor_panel(ctx: &egui::Context, editor: &mut EditorState, _board: &Board) {
|
||||
egui::SidePanel::right("palette_panel")
|
||||
.resizable(true)
|
||||
.default_width(PALETTE_PANEL_W)
|
||||
.show(ctx, |ui| {
|
||||
// Tab bar
|
||||
ui.horizontal(|ui| {
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::Palette, "Palette");
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::Board, "Board");
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::World, "World");
|
||||
});
|
||||
ui.separator();
|
||||
|
||||
match editor.tab {
|
||||
EditorTab::Palette => {
|
||||
ui.label("Element");
|
||||
|
||||
// Scrollable archetype list; shows at most 5 rows before scrolling.
|
||||
let row_h = CELL_H + ui.spacing().item_spacing.y;
|
||||
egui::ScrollArea::vertical()
|
||||
.max_height(row_h * 5.0)
|
||||
.id_salt("arch_list")
|
||||
.show(ui, |ui| {
|
||||
for &archetype in ALL_ARCHETYPES {
|
||||
let is_selected = editor.selected == archetype;
|
||||
let glyph = archetype.default_glyph();
|
||||
ui.horizontal(|ui| {
|
||||
let (r, cell_resp) = ui.allocate_exact_size(
|
||||
vec2(CELL_W, CELL_H), Sense::click());
|
||||
{
|
||||
let p = ui.painter();
|
||||
p.rect_filled(r, 0.0, glyph.bg);
|
||||
p.text(r.center(), Align2::CENTER_CENTER,
|
||||
glyph.ch, FontId::monospace(CELL_H), glyph.fg);
|
||||
}
|
||||
let label_resp =
|
||||
ui.selectable_label(is_selected, archetype.name());
|
||||
if cell_resp.clicked() || label_resp.clicked() {
|
||||
editor.selected = archetype;
|
||||
editor.glyph = archetype.default_glyph();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label("Glyph");
|
||||
// Paint a preview cell showing the current glyph, sized like a board cell.
|
||||
let (cell_rect, glyph_btn) =
|
||||
ui.allocate_exact_size(vec2(CELL_W, CELL_H), Sense::click());
|
||||
{
|
||||
let p = ui.painter();
|
||||
p.rect_filled(cell_rect, 0.0, editor.glyph.bg);
|
||||
p.text(
|
||||
cell_rect.center(),
|
||||
Align2::CENTER_CENTER,
|
||||
editor.glyph.ch,
|
||||
FontId::monospace(CELL_H),
|
||||
editor.glyph.fg,
|
||||
);
|
||||
}
|
||||
if glyph_btn.clicked() {
|
||||
editor.glyph_picker_open = true;
|
||||
}
|
||||
}
|
||||
EditorTab::Board => {}
|
||||
EditorTab::World => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user