generated from PlexSheep/rs-base
feat(roll): a basic roll with a few sines to show entropy or something
cargo devel CI / cargo CI (push) Successful in 3m4s
Details
cargo devel CI / cargo CI (push) Successful in 3m4s
Details
This commit is contained in:
parent
5f541f4009
commit
62ee0ee0ab
|
@ -27,3 +27,6 @@ image = "0.25.2"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rfd = "0.14.1"
|
rfd = "0.14.1"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
strum = { version = "0.26.3", features = ["derive"] }
|
||||||
|
emath = "0.28.1"
|
||||||
|
ecolor = "0.28.1"
|
||||||
|
|
66
src/app.rs
66
src/app.rs
|
@ -3,6 +3,9 @@ use egui::IconData;
|
||||||
use libpt::cli::args::{VerbosityLevel, HELP_TEMPLATE};
|
use libpt::cli::args::{VerbosityLevel, HELP_TEMPLATE};
|
||||||
use libpt::log::trace;
|
use libpt::log::trace;
|
||||||
|
|
||||||
|
mod roll;
|
||||||
|
use roll::Roller;
|
||||||
|
|
||||||
pub const TITLE: &str = "Rollator";
|
pub const TITLE: &str = "Rollator";
|
||||||
|
|
||||||
/// Placeholder comment that will show in the help in the CLI
|
/// Placeholder comment that will show in the help in the CLI
|
||||||
|
@ -19,6 +22,9 @@ pub struct RollatorApp {
|
||||||
#[clap(skip)]
|
#[clap(skip)]
|
||||||
value: f32,
|
value: f32,
|
||||||
|
|
||||||
|
#[clap(skip)]
|
||||||
|
roller: Roller,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub(crate) verbosity: VerbosityLevel,
|
pub(crate) verbosity: VerbosityLevel,
|
||||||
|
@ -36,6 +42,7 @@ impl Default for RollatorApp {
|
||||||
value: 2.7,
|
value: 2.7,
|
||||||
verbosity: VerbosityLevel::INFO,
|
verbosity: VerbosityLevel::INFO,
|
||||||
show_info_window: false,
|
show_info_window: false,
|
||||||
|
roller: Roller::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +56,7 @@ impl RollatorApp {
|
||||||
|
|
||||||
self.label = old.label;
|
self.label = old.label;
|
||||||
self.value = old.value;
|
self.value = old.value;
|
||||||
|
self.roller = old.roller;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,22 +115,8 @@ impl RollatorApp {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl eframe::App for RollatorApp {
|
|
||||||
/// Called by the frame work to save state before shutdown.
|
|
||||||
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
|
||||||
eframe::set_value(storage, eframe::APP_KEY, self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called each time the UI needs repainting, which may be many times per second.
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
||||||
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
|
|
||||||
// For inspiration and more examples, go to https://emilk.github.io/egui
|
|
||||||
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
|
||||||
// The top panel is often a good place for a menu bar:
|
|
||||||
|
|
||||||
|
fn top_panel(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||||
egui::menu::bar(ui, |ui| {
|
egui::menu::bar(ui, |ui| {
|
||||||
// NOTE: no File->Quit on web pages!
|
// NOTE: no File->Quit on web pages!
|
||||||
let is_web = cfg!(target_arch = "wasm32");
|
let is_web = cfg!(target_arch = "wasm32");
|
||||||
|
@ -131,7 +125,7 @@ impl eframe::App for RollatorApp {
|
||||||
if ui.button("Info").clicked() {
|
if ui.button("Info").clicked() {
|
||||||
self.show_info_window = true;
|
self.show_info_window = true;
|
||||||
}
|
}
|
||||||
ui.add_space(4.0);
|
ui.separator();
|
||||||
if ui.button("Quit").clicked() {
|
if ui.button("Quit").clicked() {
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
}
|
}
|
||||||
|
@ -144,9 +138,18 @@ impl eframe::App for RollatorApp {
|
||||||
|
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
fn roller_panel(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||||
|
ui.heading("Roll");
|
||||||
|
|
||||||
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
self.roller.labels(ui);
|
||||||
|
self.roller.rolling_space(ui);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn meta_panel(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||||
ui.heading(TITLE);
|
ui.heading(TITLE);
|
||||||
|
|
||||||
|
@ -159,18 +162,35 @@ impl eframe::App for RollatorApp {
|
||||||
if ui.button("Increment").clicked() {
|
if ui.button("Increment").clicked() {
|
||||||
self.value += 1.0;
|
self.value += 1.0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ui.separator();
|
impl eframe::App for RollatorApp {
|
||||||
|
/// Called by the frame work to save state before shutdown.
|
||||||
|
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||||
|
eframe::set_value(storage, eframe::APP_KEY, self);
|
||||||
|
}
|
||||||
|
|
||||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
/// Called each time the UI needs repainting, which may be many times per second.
|
||||||
bottom_label(ui);
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
egui::warn_if_debug_build(ui);
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
|
self.top_panel(ui, ctx);
|
||||||
});
|
});
|
||||||
|
egui::TopBottomPanel::top("top_panel2").show(ctx, |ui| {
|
||||||
|
self.meta_panel(ui, ctx);
|
||||||
});
|
});
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading("idk what to put here lol");
|
||||||
|
});
|
||||||
|
egui::SidePanel::right(egui::Id::new("roll_panel")).show(ctx, |ui| {
|
||||||
|
self.roller_panel(ui, ctx);
|
||||||
|
});
|
||||||
|
egui::TopBottomPanel::bottom("bot_panel").show(ctx, bottom_label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bottom_label(ui: &mut egui::Ui) {
|
fn bottom_label(ui: &mut egui::Ui) {
|
||||||
|
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
ui.label(format!("{TITLE} v{}", env!("CARGO_PKG_VERSION")));
|
ui.label(format!("{TITLE} v{}", env!("CARGO_PKG_VERSION")));
|
||||||
|
@ -183,6 +203,8 @@ fn bottom_label(ui: &mut egui::Ui) {
|
||||||
);
|
);
|
||||||
ui.label(".");
|
ui.label(".");
|
||||||
});
|
});
|
||||||
|
egui::warn_if_debug_build(ui);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)] // it is used in the main function
|
#[allow(unused)] // it is used in the main function
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use egui::{epaint::PathStroke, *};
|
||||||
|
use egui::{vec2, Pos2, Rect};
|
||||||
|
use libpt::log::{debug, trace};
|
||||||
|
use rand::Rng;
|
||||||
|
use strum::{EnumIter, IntoEnumIterator};
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
serde::Deserialize,
|
||||||
|
serde::Serialize,
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
EnumIter,
|
||||||
|
Hash,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Copy,
|
||||||
|
)]
|
||||||
|
pub enum Dice {
|
||||||
|
D2,
|
||||||
|
D4,
|
||||||
|
D6,
|
||||||
|
D8,
|
||||||
|
D10,
|
||||||
|
D12,
|
||||||
|
#[default]
|
||||||
|
D20,
|
||||||
|
D100,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Dice> for u8 {
|
||||||
|
fn from(value: Dice) -> Self {
|
||||||
|
match value {
|
||||||
|
Dice::D2 => 2,
|
||||||
|
Dice::D4 => 4,
|
||||||
|
Dice::D6 => 6,
|
||||||
|
Dice::D8 => 8,
|
||||||
|
Dice::D10 => 10,
|
||||||
|
Dice::D12 => 12,
|
||||||
|
Dice::D20 => 20,
|
||||||
|
Dice::D100 => 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Dice {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
Dice::D2 => "D2",
|
||||||
|
Dice::D4 => "D4",
|
||||||
|
Dice::D6 => "D6",
|
||||||
|
Dice::D8 => "D8",
|
||||||
|
Dice::D10 => "D10",
|
||||||
|
Dice::D12 => "D12",
|
||||||
|
Dice::D20 => "D20",
|
||||||
|
Dice::D100 => "D100",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default)]
|
||||||
|
pub struct Roller {
|
||||||
|
selected_dice: Dice,
|
||||||
|
new: bool,
|
||||||
|
value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Roller {
|
||||||
|
pub(crate) fn roll(&mut self) -> u8 {
|
||||||
|
if self.new {
|
||||||
|
self.value = rand::thread_rng().gen_range(1..=self.selected_dice.into());
|
||||||
|
self.new = false;
|
||||||
|
}
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn labels(&mut self, ui: &mut egui::Ui) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
for variant in Dice::iter() {
|
||||||
|
let dice_label =
|
||||||
|
ui.selectable_label(variant == self.selected_dice, variant.to_string());
|
||||||
|
if dice_label.clicked() {
|
||||||
|
self.selected_dice = variant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn rolling_space(&mut self, ui: &mut egui::Ui) {
|
||||||
|
let color = if ui.visuals().dark_mode {
|
||||||
|
Color32::from_additive_luminance(196)
|
||||||
|
} else {
|
||||||
|
Color32::from_black_alpha(240)
|
||||||
|
};
|
||||||
|
|
||||||
|
let button = ui.button("> ROLL <");
|
||||||
|
if button.clicked() {
|
||||||
|
debug!("rolling dice");
|
||||||
|
self.new = true;
|
||||||
|
}
|
||||||
|
ui.add_space(10.0);
|
||||||
|
ui.heading(self.roll().to_string());
|
||||||
|
ui.add_space(10.0);
|
||||||
|
ui.centered_and_justified(|ui| {
|
||||||
|
let _canvas = egui::containers::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
let time = ui.input(|i| i.time);
|
||||||
|
|
||||||
|
let desired_size = ui.available_width() * vec2(1.0, 0.35);
|
||||||
|
let (_id, rect) = ui.allocate_space(desired_size);
|
||||||
|
|
||||||
|
let to_screen = emath::RectTransform::from_to(
|
||||||
|
Rect::from_x_y_ranges(0.0..=1.0, -1.0..=1.0),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
let mut shapes = vec![];
|
||||||
|
|
||||||
|
for &mode in &[2.0, 3.0, 5.0] {
|
||||||
|
let n = 120;
|
||||||
|
let speed = 1.5;
|
||||||
|
|
||||||
|
let points: Vec<Pos2> = (0..=n)
|
||||||
|
.map(|i| {
|
||||||
|
let t = i as f64 / (n as f64);
|
||||||
|
let amp = (time * speed * mode).sin() / mode;
|
||||||
|
let y = amp * (t * std::f64::consts::TAU / 2.0 * mode).sin();
|
||||||
|
to_screen * pos2(t as f32, y as f32)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let thickness = 10.0 / mode as f32;
|
||||||
|
shapes.push(epaint::Shape::line(
|
||||||
|
points,
|
||||||
|
PathStroke::new(thickness, color),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ui.painter().extend(shapes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#![warn(clippy::all, rust_2018_idioms)]
|
#![warn(clippy::all, rust_2018_idioms)]
|
||||||
|
|
||||||
mod app;
|
pub(crate) mod app;
|
||||||
pub use app::RollatorApp;
|
pub use app::RollatorApp;
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -6,26 +6,28 @@ mod app;
|
||||||
// When compiling natively:
|
// When compiling natively:
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn main() -> eframe::Result {
|
fn main() -> eframe::Result {
|
||||||
use libpt::log::debug;
|
use libpt::log::{debug, error};
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
|
||||||
let mut app = app::RollatorApp::new_with_cli();
|
let mut app = app::RollatorApp::new_with_cli();
|
||||||
|
|
||||||
let filter = tracing_subscriber::EnvFilter::builder()
|
let filter = tracing_subscriber::EnvFilter::builder()
|
||||||
.with_default_directive(tracing_subscriber::filter::LevelFilter::WARN.into())
|
.with_default_directive(app.verbosity.level().into())
|
||||||
.from_env()
|
.from_env()
|
||||||
.expect("could not init logger")
|
.expect("could not init logger")
|
||||||
.add_directive(
|
.add_directive(
|
||||||
format!("rollator={}", app.verbosity.level())
|
format!("{}={}", env!("CARGO_PKG_NAME"), app.verbosity.level())
|
||||||
.parse()
|
.parse()
|
||||||
.expect("could not init logger"),
|
.expect("could not init logger"),
|
||||||
);
|
);
|
||||||
|
|
||||||
tracing_subscriber::fmt::Subscriber::builder()
|
tracing_subscriber::fmt::Subscriber::builder()
|
||||||
.with_env_filter(filter)
|
.with_env_filter(filter)
|
||||||
|
.with_max_level(app.verbosity.level())
|
||||||
.finish()
|
.finish()
|
||||||
.set_default();
|
.init();
|
||||||
debug!("logging initialized!");
|
debug!("logging initialized!");
|
||||||
|
debug!("level: {}", app.verbosity.level());
|
||||||
|
|
||||||
let native_options = eframe::NativeOptions {
|
let native_options = eframe::NativeOptions {
|
||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
|
|
Loading…
Reference in New Issue