add window manager system
This commit is contained in:
parent
21863fa357
commit
d3d12ac68d
@ -1,4 +1,4 @@
|
||||
use crate::firmware::firmware_state::{FirmwareState, LoadGameStep, ResetStep};
|
||||
use crate::firmware::firmware_state::{FirmwareState, LoadCartridgeStep, ResetStep};
|
||||
use crate::firmware::prometeu_context::PrometeuContext;
|
||||
use crate::hardware::{HardwareBridge, InputSignals};
|
||||
use crate::model::Cartridge;
|
||||
@ -26,6 +26,10 @@ impl Firmware {
|
||||
}
|
||||
|
||||
pub fn step_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
||||
// Atualiza input uma vez por frame de host
|
||||
hw.pad_mut().begin_frame(signals);
|
||||
hw.touch_mut().begin_frame(signals);
|
||||
|
||||
if !self.state_initialized {
|
||||
self.on_enter(signals, hw);
|
||||
self.state_initialized = true;
|
||||
@ -59,7 +63,7 @@ impl Firmware {
|
||||
FirmwareState::SplashScreen(s) => s.on_enter(&mut req),
|
||||
FirmwareState::LaunchHub(s) => s.on_enter(&mut req),
|
||||
FirmwareState::HubHome(s) => s.on_enter(&mut req),
|
||||
FirmwareState::LoadGame(s) => s.on_enter(&mut req),
|
||||
FirmwareState::LoadCartridge(s) => s.on_enter(&mut req),
|
||||
FirmwareState::GameRunning(s) => s.on_enter(&mut req),
|
||||
FirmwareState::AppCrashes(s) => s.on_enter(&mut req),
|
||||
}
|
||||
@ -78,7 +82,7 @@ impl Firmware {
|
||||
FirmwareState::SplashScreen(s) => s.on_update(&mut req),
|
||||
FirmwareState::LaunchHub(s) => s.on_update(&mut req),
|
||||
FirmwareState::HubHome(s) => s.on_update(&mut req),
|
||||
FirmwareState::LoadGame(s) => s.on_update(&mut req),
|
||||
FirmwareState::LoadCartridge(s) => s.on_update(&mut req),
|
||||
FirmwareState::GameRunning(s) => s.on_update(&mut req),
|
||||
FirmwareState::AppCrashes(s) => s.on_update(&mut req),
|
||||
}
|
||||
@ -97,14 +101,14 @@ impl Firmware {
|
||||
FirmwareState::SplashScreen(s) => s.on_exit(&mut req),
|
||||
FirmwareState::LaunchHub(s) => s.on_exit(&mut req),
|
||||
FirmwareState::HubHome(s) => s.on_exit(&mut req),
|
||||
FirmwareState::LoadGame(s) => s.on_exit(&mut req),
|
||||
FirmwareState::LoadCartridge(s) => s.on_exit(&mut req),
|
||||
FirmwareState::GameRunning(s) => s.on_exit(&mut req),
|
||||
FirmwareState::AppCrashes(s) => s.on_exit(&mut req),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_cartridge(&mut self, cartridge: Cartridge) {
|
||||
self.state = FirmwareState::LoadGame(LoadGameStep { cartridge });
|
||||
self.state = FirmwareState::LoadCartridge(LoadCartridgeStep { cartridge });
|
||||
self.state_initialized = false;
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@ pub use crate::firmware::firmware_step_reset::ResetStep;
|
||||
pub use crate::firmware::firmware_step_splash_screen::SplashScreenStep;
|
||||
pub use crate::firmware::firmware_step_launch_hub::LaunchHubStep;
|
||||
pub use crate::firmware::firmware_step_hub_home::HubHomeStep;
|
||||
pub use crate::firmware::firmware_step_load_game::LoadGameStep;
|
||||
pub use crate::firmware::firmware_step_load_cartridge::LoadCartridgeStep;
|
||||
pub use crate::firmware::firmware_step_game_running::GameRunningStep;
|
||||
pub use crate::firmware::firmware_step_crash_screen::AppCrashesStep;
|
||||
|
||||
@ -12,7 +12,7 @@ pub enum FirmwareState {
|
||||
SplashScreen(SplashScreenStep),
|
||||
LaunchHub(LaunchHubStep),
|
||||
HubHome(HubHomeStep),
|
||||
LoadGame(LoadGameStep),
|
||||
LoadCartridge(LoadCartridgeStep),
|
||||
GameRunning(GameRunningStep),
|
||||
AppCrashes(AppCrashesStep),
|
||||
}
|
||||
@ -8,7 +8,13 @@ impl GameRunningStep {
|
||||
pub fn on_enter(&mut self, _ctx: &mut PrometeuContext) {}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
ctx.os.step_frame(ctx.vm, ctx.signals, ctx.hw).map(|err| FirmwareState::AppCrashes(AppCrashesStep { error: err }))
|
||||
let result = ctx.os.step_frame(ctx.vm, ctx.signals, ctx.hw);
|
||||
|
||||
if !ctx.os.logical_frame_active {
|
||||
ctx.hw.gfx_mut().present();
|
||||
}
|
||||
|
||||
result.map(|err| FirmwareState::AppCrashes(AppCrashesStep { error: err }))
|
||||
}
|
||||
|
||||
pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::firmware::firmware_state::FirmwareState;
|
||||
use crate::firmware::firmware_state::{AppCrashesStep, FirmwareState};
|
||||
use crate::firmware::prometeu_context::PrometeuContext;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -8,11 +8,29 @@ impl HubHomeStep {
|
||||
pub fn on_enter(&mut self, _ctx: &mut PrometeuContext) {}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
// req.hw.gfx_mut().clear(Color::BLACK);
|
||||
ctx.hw.pad_mut().begin_frame(ctx.signals);
|
||||
let mut error = None;
|
||||
|
||||
// Sempre atualiza a GUI do Hub (limpa a tela e processa input se não houver foco)
|
||||
ctx.hub.gui_update(ctx.os, ctx.hw);
|
||||
|
||||
if let Some(focused_id) = ctx.hub.window_manager.focused {
|
||||
if ctx.hw.pad().start.down {
|
||||
ctx.hub.window_manager.remove_window(focused_id);
|
||||
} else {
|
||||
// System App roda aqui, desenhando sobre o fundo do Hub
|
||||
error = ctx.os.step_frame(ctx.vm, ctx.signals, ctx.hw);
|
||||
}
|
||||
}
|
||||
|
||||
// Renderiza as bordas das janelas do System App
|
||||
ctx.hub.render(ctx.os, ctx.hw);
|
||||
|
||||
ctx.hw.gfx_mut().present();
|
||||
|
||||
if let Some(err) = error {
|
||||
return Some(FirmwareState::AppCrashes(AppCrashesStep { error: err }));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
use crate::firmware::firmware_state::{FirmwareState, GameRunningStep, HubHomeStep};
|
||||
use crate::firmware::prometeu_context::PrometeuContext;
|
||||
use crate::model::{AppMode, Cartridge, Color, Rect};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoadCartridgeStep {
|
||||
pub cartridge: Cartridge,
|
||||
}
|
||||
|
||||
impl LoadCartridgeStep {
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.initialize_vm(ctx.vm, &self.cartridge);
|
||||
}
|
||||
|
||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
if self.cartridge.header.mode == AppMode::System {
|
||||
let id = ctx.hub.window_manager.add_window(
|
||||
self.cartridge.header.title.clone(),
|
||||
Rect { x: 40, y: 20, w: 240, h: 140 },
|
||||
Color::WHITE
|
||||
);
|
||||
ctx.hub.window_manager.set_focus(id);
|
||||
return Some(FirmwareState::HubHome(HubHomeStep));
|
||||
}
|
||||
|
||||
Some(FirmwareState::GameRunning(GameRunningStep))
|
||||
}
|
||||
|
||||
pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
use crate::firmware::firmware_state::{FirmwareState, GameRunningStep};
|
||||
use crate::firmware::prometeu_context::PrometeuContext;
|
||||
use crate::model::Cartridge;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoadGameStep {
|
||||
pub cartridge: Cartridge,
|
||||
}
|
||||
|
||||
impl LoadGameStep {
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.initialize_vm(ctx.vm, &self.cartridge);
|
||||
}
|
||||
|
||||
pub fn on_update(&mut self, _ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||
Some(FirmwareState::GameRunning(GameRunningStep))
|
||||
}
|
||||
|
||||
pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {}
|
||||
}
|
||||
@ -5,7 +5,7 @@ pub(crate) mod firmware_step_reset;
|
||||
pub(crate) mod firmware_step_splash_screen;
|
||||
pub(crate) mod firmware_step_launch_hub;
|
||||
pub(crate) mod firmware_step_hub_home;
|
||||
pub(crate) mod firmware_step_load_game;
|
||||
pub(crate) mod firmware_step_load_cartridge;
|
||||
pub(crate) mod firmware_step_game_running;
|
||||
pub(crate) mod firmware_step_crash_screen;
|
||||
mod prometeu_context;
|
||||
|
||||
@ -6,6 +6,7 @@ mod tile_bank;
|
||||
mod sprite;
|
||||
mod sample;
|
||||
mod cartridge;
|
||||
mod window;
|
||||
|
||||
pub use button::Button;
|
||||
pub use cartridge::{AppHeader, AppMode, Cartridge};
|
||||
@ -15,3 +16,4 @@ pub use sprite::Sprite;
|
||||
pub use tile::Tile;
|
||||
pub use tile_bank::{TileBank, TileSize};
|
||||
pub use tile_layer::{HudTileLayer, ScrollableTileLayer, TileMap};
|
||||
pub use window::{Rect, Window, WindowId};
|
||||
|
||||
21
crates/prometeu-core/src/model/window.rs
Normal file
21
crates/prometeu-core/src/model/window.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::model::Color;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Rect {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub w: i32,
|
||||
pub h: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct WindowId(pub u32);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Window {
|
||||
pub id: WindowId,
|
||||
pub viewport: Rect,
|
||||
pub has_focus: bool,
|
||||
pub title: String,
|
||||
pub color: Color,
|
||||
}
|
||||
@ -1,32 +1,63 @@
|
||||
use crate::hardware::HardwareBridge;
|
||||
use crate::model::Color;
|
||||
use crate::model::{Color, Rect, Window, WindowId};
|
||||
use crate::prometeu_os::PrometeuOS;
|
||||
|
||||
// TODO: Mover para arquivo próprio se crescer.
|
||||
/// Sistema de janelas do PROMETEU.
|
||||
pub struct WindowSystem {
|
||||
pub theme_color: Color,
|
||||
/// Gerenciador de janelas do PROMETEU.
|
||||
pub struct WindowManager {
|
||||
pub windows: Vec<Window>,
|
||||
pub focused: Option<WindowId>,
|
||||
}
|
||||
|
||||
impl WindowSystem {
|
||||
impl WindowManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
theme_color: Color::INDIGO,
|
||||
windows: Vec::new(),
|
||||
focused: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_window(&mut self, title: String, viewport: Rect, color: Color) -> WindowId {
|
||||
let id = WindowId(self.windows.len() as u32);
|
||||
let window = Window {
|
||||
id,
|
||||
viewport,
|
||||
has_focus: false,
|
||||
title,
|
||||
color,
|
||||
};
|
||||
self.windows.push(window);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn remove_window(&mut self, id: WindowId) {
|
||||
self.windows.retain(|w| w.id != id);
|
||||
if self.focused == Some(id) {
|
||||
self.focused = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_all_windows(&mut self) {
|
||||
self.windows.clear();
|
||||
self.focused = None;
|
||||
}
|
||||
|
||||
pub fn set_focus(&mut self, id: WindowId) {
|
||||
self.focused = Some(id);
|
||||
for window in &mut self.windows {
|
||||
window.has_focus = window.id == id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PrometeuHub: Launcher e ambiente de UI do sistema.
|
||||
pub struct PrometeuHub {
|
||||
pub window_system: WindowSystem,
|
||||
pub color: Color,
|
||||
pub window_manager: WindowManager,
|
||||
}
|
||||
|
||||
impl PrometeuHub {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
window_system: WindowSystem::new(),
|
||||
color: Color::BLACK,
|
||||
window_manager: WindowManager::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,22 +66,74 @@ impl PrometeuHub {
|
||||
}
|
||||
|
||||
pub fn gui_update(&mut self, _os: &mut PrometeuOS, hw: &mut dyn HardwareBridge) {
|
||||
if hw.pad().a.down {
|
||||
self.color = Color::GREEN;
|
||||
}
|
||||
if hw.pad().b.down {
|
||||
self.color = Color::INDIGO;
|
||||
}
|
||||
if hw.pad().x.down {
|
||||
self.color = Color::YELLOW;
|
||||
}
|
||||
if hw.pad().y.down {
|
||||
self.color = Color::RED;
|
||||
hw.gfx_mut().clear(Color::BLACK);
|
||||
|
||||
let mut next_window = None;
|
||||
|
||||
if hw.pad().a.pressed {
|
||||
next_window = Some(("Green Window".to_string(), Rect { x: 0, y: 0, w: 160, h: 90 }, Color::GREEN));
|
||||
} else if hw.pad().b.pressed {
|
||||
next_window = Some(("Indigo Window".to_string(), Rect { x: 160, y: 0, w: 160, h: 90 }, Color::INDIGO));
|
||||
} else if hw.pad().x.pressed {
|
||||
next_window = Some(("Yellow Window".to_string(), Rect { x: 0, y: 90, w: 160, h: 90 }, Color::YELLOW));
|
||||
} else if hw.pad().y.pressed {
|
||||
next_window = Some(("Red Window".to_string(), Rect { x: 160, y: 90, w: 160, h: 90 }, Color::RED));
|
||||
}
|
||||
|
||||
hw.gfx_mut().clear(self.color);
|
||||
if let Some((title, rect, color)) = next_window {
|
||||
self.window_manager.remove_all_windows();
|
||||
let id = self.window_manager.add_window(title, rect, color);
|
||||
self.window_manager.set_focus(id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self, _os: &mut PrometeuOS, _hw: &mut dyn HardwareBridge) {
|
||||
pub fn render(&mut self, _os: &mut PrometeuOS, hw: &mut dyn HardwareBridge) {
|
||||
for window in &self.window_manager.windows {
|
||||
hw.gfx_mut().fill_rect(
|
||||
window.viewport.x,
|
||||
window.viewport.y,
|
||||
window.viewport.w,
|
||||
window.viewport.h,
|
||||
window.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::model::Rect;
|
||||
|
||||
#[test]
|
||||
fn test_window_manager_focus() {
|
||||
let mut wm = WindowManager::new();
|
||||
let id1 = wm.add_window("Window 1".to_string(), Rect { x: 0, y: 0, w: 10, h: 10 }, Color::WHITE);
|
||||
let id2 = wm.add_window("Window 2".to_string(), Rect { x: 10, y: 10, w: 10, h: 10 }, Color::WHITE);
|
||||
|
||||
assert_eq!(wm.windows.len(), 2);
|
||||
assert_eq!(wm.focused, None);
|
||||
|
||||
wm.set_focus(id1);
|
||||
assert_eq!(wm.focused, Some(id1));
|
||||
assert!(wm.windows[0].has_focus);
|
||||
assert!(!wm.windows[1].has_focus);
|
||||
|
||||
wm.set_focus(id2);
|
||||
assert_eq!(wm.focused, Some(id2));
|
||||
assert!(!wm.windows[0].has_focus);
|
||||
assert!(wm.windows[1].has_focus);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_window_manager_remove_window() {
|
||||
let mut wm = WindowManager::new();
|
||||
let id = wm.add_window("Window".to_string(), Rect { x: 0, y: 0, w: 10, h: 10 }, Color::WHITE);
|
||||
wm.set_focus(id);
|
||||
assert_eq!(wm.focused, Some(id));
|
||||
|
||||
wm.remove_window(id);
|
||||
assert_eq!(wm.windows.len(), 0);
|
||||
assert_eq!(wm.focused, None);
|
||||
}
|
||||
}
|
||||
@ -79,7 +79,6 @@ impl PrometeuOS {
|
||||
|
||||
if run.reason == crate::virtual_machine::LogicalFrameEndingReason::FrameSync {
|
||||
hw.gfx_mut().render_all();
|
||||
self.end_logical_frame(hw);
|
||||
self.logical_frame_index += 1;
|
||||
self.logical_frame_active = false;
|
||||
self.logical_frame_remaining_cycles = 0;
|
||||
@ -95,15 +94,10 @@ impl PrometeuOS {
|
||||
None
|
||||
}
|
||||
|
||||
fn begin_logical_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
||||
fn begin_logical_frame(&mut self, _signals: &InputSignals, hw: &mut dyn HardwareBridge) {
|
||||
hw.audio_mut().clear_commands();
|
||||
hw.touch_mut().begin_frame(signals);
|
||||
hw.pad_mut().begin_frame(signals);
|
||||
}
|
||||
|
||||
fn end_logical_frame(&mut self, hw: &mut dyn HardwareBridge) {
|
||||
hw.gfx_mut().present();
|
||||
}
|
||||
|
||||
// Helper para syscalls
|
||||
pub fn get_color(&self, index: usize, hw: &mut dyn HardwareBridge) -> Color {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user