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
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use egui::{Context, Ui};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{Action, Trigger},
|
action::{Action, Switch, Trigger},
|
||||||
|
menu_bar::MenuBar,
|
||||||
translator::Translator,
|
translator::Translator,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Actions {
|
pub struct Actions {
|
||||||
pub file: FileActions,
|
pub file: FileActions,
|
||||||
|
pub debug: DebugActions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actions {
|
impl Actions {
|
||||||
pub fn new(tr: &Translator) -> Self {
|
pub fn new(tr: &Translator) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file: FileActions::new(tr),
|
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);
|
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_locale();
|
||||||
self.update_title(ctx);
|
self.update_title(ctx);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
use egui::{
|
||||||
|
PopupCloseBehavior,
|
||||||
|
containers::menu::{MenuButton, MenuConfig},
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specctra::{
|
use specctra::{
|
||||||
error::{ParseError, ParseErrorContext},
|
error::{ParseError, ParseErrorContext},
|
||||||
|
|
@ -19,12 +23,16 @@ use crate::{
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct MenuBar {
|
pub struct MenuBar {
|
||||||
step_rate: f32,
|
pub fix_step_rate: bool,
|
||||||
|
pub step_rate: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MenuBar {
|
impl MenuBar {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { step_rate: 1.0 }
|
Self {
|
||||||
|
fix_step_rate: false,
|
||||||
|
step_rate: 1.0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(
|
pub fn update(
|
||||||
|
|
@ -42,6 +50,29 @@ impl MenuBar {
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.separator();
|
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);
|
egui::widgets::global_theme_preference_switch(ui);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
use egui::Pos2;
|
use egui::Pos2;
|
||||||
use topola::{MasterInteractor, Vector2, Workspace};
|
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 struct Viewport {
|
||||||
pub scene_rect: egui::Rect,
|
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::CentralPanel::default().show(ctx, |ui| {
|
||||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||||
ui.ctx().request_repaint();
|
ui.ctx().request_repaint();
|
||||||
|
|
@ -51,6 +57,20 @@ impl Viewport {
|
||||||
Self::fit_to_rect_in_scene(viewport_rect, scene_rect, zoom_range.into());
|
Self::fit_to_rect_in_scene(viewport_rect, scene_rect, zoom_range.into());
|
||||||
|
|
||||||
if let Some(workspace) = workspace {
|
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));
|
let escape_pressed = ctx.input(|i| i.key_pressed(egui::Key::Escape));
|
||||||
if escape_pressed {
|
if escape_pressed {
|
||||||
if let Some(interactor) = self.master_interactor.as_mut() {
|
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));
|
ctx.input(|i| i.pointer.button_down(egui::PointerButton::Primary));
|
||||||
let primary_released =
|
let primary_released =
|
||||||
ctx.input(|i| i.pointer.button_released(egui::PointerButton::Primary));
|
ctx.input(|i| i.pointer.button_released(egui::PointerButton::Primary));
|
||||||
|
|
||||||
let delete_pressed = ctx.input(|i| i.key_pressed(egui::Key::Delete));
|
let delete_pressed = ctx.input(|i| i.key_pressed(egui::Key::Delete));
|
||||||
let mut maybe_pointer_on_scene: Option<Vector2<i64>> = None;
|
let mut maybe_pointer_on_scene: Option<Vector2<i64>> = None;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use std::{ops::ControlFlow, time::Instant};
|
||||||
|
|
||||||
use topola::{Board, Workspace};
|
use topola::{Board, Workspace};
|
||||||
|
|
||||||
use crate::{layers_panel::LayersPanel, translator::Translator};
|
use crate::{layers_panel::LayersPanel, translator::Translator};
|
||||||
|
|
@ -9,6 +11,7 @@ use crate::{layers_panel::LayersPanel, translator::Translator};
|
||||||
pub struct GuiWorkspace {
|
pub struct GuiWorkspace {
|
||||||
pub workspace: Workspace,
|
pub workspace: Workspace,
|
||||||
pub appearance_panel: LayersPanel,
|
pub appearance_panel: LayersPanel,
|
||||||
|
pub dt_accum: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuiWorkspace {
|
impl GuiWorkspace {
|
||||||
|
|
@ -18,14 +21,53 @@ impl GuiWorkspace {
|
||||||
Self {
|
Self {
|
||||||
workspace: Workspace::new_board(board),
|
workspace: Workspace::new_board(board),
|
||||||
appearance_panel,
|
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) {
|
pub fn update_appearance_panel(&mut self, ctx: &egui::Context) {
|
||||||
let Self {
|
let Self {
|
||||||
workspace,
|
workspace,
|
||||||
appearance_panel,
|
appearance_panel,
|
||||||
|
dt_accum,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
appearance_panel.update(ctx, workspace.board());
|
appearance_panel.update(ctx, workspace.board());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue