mirror of https://codeberg.org/topola/topola.git
Add toggle button and slider to change step rate
This commit is contained in:
parent
0cd4b005fd
commit
c7430f9fc9
|
|
@ -2,19 +2,24 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use egui::{Context, Ui};
|
||||
|
||||
use crate::{
|
||||
action::{Action, Trigger},
|
||||
action::{Action, Switch, Trigger},
|
||||
menu_bar::MenuBar,
|
||||
translator::Translator,
|
||||
};
|
||||
|
||||
pub struct Actions {
|
||||
pub file: FileActions,
|
||||
pub debug: DebugActions,
|
||||
}
|
||||
|
||||
impl Actions {
|
||||
pub fn new(tr: &Translator) -> Self {
|
||||
Self {
|
||||
file: FileActions::new(tr),
|
||||
debug: DebugActions::new(tr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,3 +73,20 @@ impl FileActions {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugActions {
|
||||
pub fix_step_rate: Switch,
|
||||
}
|
||||
|
||||
impl DebugActions {
|
||||
pub fn new(tr: &Translator) -> Self {
|
||||
Self {
|
||||
fix_step_rate: Action::new_keyless(tr.text("tr-menu-debug-fix-step-rate"))
|
||||
.into_switch(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_menu(&mut self, _ctx: &Context, ui: &mut Ui, menu_bar: &mut MenuBar) {
|
||||
self.fix_step_rate.checkbox(ui, &mut menu_bar.fix_step_rate);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,12 @@ impl eframe::App for App {
|
|||
workspace.update_appearance_panel(ctx);
|
||||
}
|
||||
|
||||
self.viewport.update(ctx, self.workspace.as_mut());
|
||||
self.viewport.update(
|
||||
&self.translator,
|
||||
ctx,
|
||||
&self.menu_bar,
|
||||
self.workspace.as_mut(),
|
||||
);
|
||||
|
||||
self.update_locale();
|
||||
self.update_title(ctx);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use egui::{
|
||||
PopupCloseBehavior,
|
||||
containers::menu::{MenuButton, MenuConfig},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specctra::{
|
||||
error::{ParseError, ParseErrorContext},
|
||||
|
|
@ -19,12 +23,16 @@ use crate::{
|
|||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct MenuBar {
|
||||
step_rate: f32,
|
||||
pub fix_step_rate: bool,
|
||||
pub step_rate: f64,
|
||||
}
|
||||
|
||||
impl MenuBar {
|
||||
pub fn new() -> Self {
|
||||
Self { step_rate: 1.0 }
|
||||
Self {
|
||||
fix_step_rate: false,
|
||||
step_rate: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
|
|
@ -42,6 +50,29 @@ impl MenuBar {
|
|||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
MenuButton::new(tr.text("tr-menu-debug"))
|
||||
.config(
|
||||
MenuConfig::default()
|
||||
.close_behavior(PopupCloseBehavior::CloseOnClickOutside),
|
||||
)
|
||||
.ui(ui, |ui| {
|
||||
actions.debug.render_menu(ctx, ui, self);
|
||||
|
||||
ui.add_enabled_ui(self.fix_step_rate, |ui| {
|
||||
ui.label(tr.text("tr-menu-debug-step-rate"));
|
||||
ui.add(
|
||||
egui::widgets::Slider::new(&mut self.step_rate, 0.1..=1000.0)
|
||||
.suffix(format!(
|
||||
" {}",
|
||||
tr.text("tr-menu-debug-step-rate-unit")
|
||||
)),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
egui::widgets::global_theme_preference_switch(ui);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
use egui::Pos2;
|
||||
use topola::{MasterInteractor, Vector2, Workspace};
|
||||
|
||||
use crate::{display::Display, workspace::GuiWorkspace};
|
||||
use crate::{display::Display, menu_bar::MenuBar, translator::Translator, workspace::GuiWorkspace};
|
||||
|
||||
pub struct Viewport {
|
||||
pub scene_rect: egui::Rect,
|
||||
|
|
@ -24,7 +24,13 @@ impl Viewport {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, ctx: &egui::Context, workspace: Option<&mut GuiWorkspace>) {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
tr: &Translator,
|
||||
ctx: &egui::Context,
|
||||
menu_bar: &MenuBar,
|
||||
workspace: Option<&mut GuiWorkspace>,
|
||||
) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||
ui.ctx().request_repaint();
|
||||
|
|
@ -51,6 +57,20 @@ impl Viewport {
|
|||
Self::fit_to_rect_in_scene(viewport_rect, scene_rect, zoom_range.into());
|
||||
|
||||
if let Some(workspace) = workspace {
|
||||
workspace.advance_state_by_dt(
|
||||
tr,
|
||||
menu_bar.fix_step_rate.then_some(menu_bar.step_rate),
|
||||
ctx.input(|i| {
|
||||
if i.stable_dt <= i.predicted_dt {
|
||||
i.stable_dt
|
||||
} else {
|
||||
// Clamp dt to egui's predicted dt to
|
||||
// additionally safeguard against stuttering.
|
||||
i.predicted_dt
|
||||
}
|
||||
}) as f64,
|
||||
);
|
||||
|
||||
let escape_pressed = ctx.input(|i| i.key_pressed(egui::Key::Escape));
|
||||
if escape_pressed {
|
||||
if let Some(interactor) = self.master_interactor.as_mut() {
|
||||
|
|
@ -70,6 +90,7 @@ impl Viewport {
|
|||
ctx.input(|i| i.pointer.button_down(egui::PointerButton::Primary));
|
||||
let primary_released =
|
||||
ctx.input(|i| i.pointer.button_released(egui::PointerButton::Primary));
|
||||
|
||||
let delete_pressed = ctx.input(|i| i.key_pressed(egui::Key::Delete));
|
||||
let mut maybe_pointer_on_scene: Option<Vector2<i64>> = None;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use std::{ops::ControlFlow, time::Instant};
|
||||
|
||||
use topola::{Board, Workspace};
|
||||
|
||||
use crate::{layers_panel::LayersPanel, translator::Translator};
|
||||
|
|
@ -9,6 +11,7 @@ use crate::{layers_panel::LayersPanel, translator::Translator};
|
|||
pub struct GuiWorkspace {
|
||||
pub workspace: Workspace,
|
||||
pub appearance_panel: LayersPanel,
|
||||
pub dt_accum: f64,
|
||||
}
|
||||
|
||||
impl GuiWorkspace {
|
||||
|
|
@ -18,14 +21,53 @@ impl GuiWorkspace {
|
|||
Self {
|
||||
workspace: Workspace::new_board(board),
|
||||
appearance_panel,
|
||||
dt_accum: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn advance_state_by_dt(
|
||||
&mut self,
|
||||
tr: &Translator,
|
||||
step_rate: Option<f64>,
|
||||
dt: f64,
|
||||
) -> bool {
|
||||
let instant = Instant::now();
|
||||
|
||||
if step_rate.is_some() {
|
||||
self.dt_accum += dt;
|
||||
}
|
||||
|
||||
while step_rate.is_none_or(|step_rate| self.dt_accum >= 1.0 / step_rate) {
|
||||
if let Some(step_rate) = step_rate {
|
||||
self.dt_accum -= 1.0 / step_rate;
|
||||
}
|
||||
|
||||
if let ControlFlow::Break(()) = self.step(tr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Hard limit: never spend more time on advancing state than the
|
||||
// duration of last frame to prevent stuttering.
|
||||
// Of course, this does not safeguard against infinite loops.
|
||||
if instant.elapsed().as_secs_f64() >= dt {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn step(&mut self, tr: &Translator) -> ControlFlow<()> {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
pub fn update_appearance_panel(&mut self, ctx: &egui::Context) {
|
||||
let Self {
|
||||
workspace,
|
||||
appearance_panel,
|
||||
dt_accum,
|
||||
} = self;
|
||||
|
||||
appearance_panel.update(ctx, workspace.board());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue