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"
|
||||
rfd = "0.14.1"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
strum = { version = "0.26.3", features = ["derive"] }
|
||||
emath = "0.28.1"
|
||||
ecolor = "0.28.1"
|
||||
|
|
136
src/app.rs
136
src/app.rs
|
@ -3,6 +3,9 @@ use egui::IconData;
|
|||
use libpt::cli::args::{VerbosityLevel, HELP_TEMPLATE};
|
||||
use libpt::log::trace;
|
||||
|
||||
mod roll;
|
||||
use roll::Roller;
|
||||
|
||||
pub const TITLE: &str = "Rollator";
|
||||
|
||||
/// Placeholder comment that will show in the help in the CLI
|
||||
|
@ -19,6 +22,9 @@ pub struct RollatorApp {
|
|||
#[clap(skip)]
|
||||
value: f32,
|
||||
|
||||
#[clap(skip)]
|
||||
roller: Roller,
|
||||
|
||||
#[serde(skip)]
|
||||
#[command(flatten)]
|
||||
pub(crate) verbosity: VerbosityLevel,
|
||||
|
@ -36,6 +42,7 @@ impl Default for RollatorApp {
|
|||
value: 2.7,
|
||||
verbosity: VerbosityLevel::INFO,
|
||||
show_info_window: false,
|
||||
roller: Roller::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +56,7 @@ impl RollatorApp {
|
|||
|
||||
self.label = old.label;
|
||||
self.value = old.value;
|
||||
self.roller = old.roller;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +115,54 @@ impl RollatorApp {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn top_panel(&mut self, ui: &mut egui::Ui, ctx: &egui::Context) {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
// NOTE: no File->Quit on web pages!
|
||||
let is_web = cfg!(target_arch = "wasm32");
|
||||
if !is_web {
|
||||
ui.menu_button("File", |ui| {
|
||||
if ui.button("Info").clicked() {
|
||||
self.show_info_window = true;
|
||||
}
|
||||
ui.separator();
|
||||
if ui.button("Quit").clicked() {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
});
|
||||
ui.add_space(16.0);
|
||||
}
|
||||
if self.show_info_window {
|
||||
self.info_diag(ctx);
|
||||
}
|
||||
|
||||
egui::widgets::global_dark_light_mode_buttons(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
|
||||
ui.heading(TITLE);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Write something: ");
|
||||
ui.text_edit_singleline(&mut self.label);
|
||||
});
|
||||
|
||||
ui.add(egui::Slider::new(&mut self.value, 0.0..=10.0).text("value"));
|
||||
if ui.button("Increment").clicked() {
|
||||
self.value += 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for RollatorApp {
|
||||
|
@ -117,71 +173,37 @@ impl eframe::App for RollatorApp {
|
|||
|
||||
/// 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:
|
||||
|
||||
egui::menu::bar(ui, |ui| {
|
||||
// NOTE: no File->Quit on web pages!
|
||||
let is_web = cfg!(target_arch = "wasm32");
|
||||
if !is_web {
|
||||
ui.menu_button("File", |ui| {
|
||||
if ui.button("Info").clicked() {
|
||||
self.show_info_window = true;
|
||||
}
|
||||
ui.add_space(4.0);
|
||||
if ui.button("Quit").clicked() {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
});
|
||||
ui.add_space(16.0);
|
||||
}
|
||||
if self.show_info_window {
|
||||
self.info_diag(ctx);
|
||||
}
|
||||
|
||||
egui::widgets::global_dark_light_mode_buttons(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| {
|
||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||
ui.heading(TITLE);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Write something: ");
|
||||
ui.text_edit_singleline(&mut self.label);
|
||||
});
|
||||
|
||||
ui.add(egui::Slider::new(&mut self.value, 0.0..=10.0).text("value"));
|
||||
if ui.button("Increment").clicked() {
|
||||
self.value += 1.0;
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
||||
bottom_label(ui);
|
||||
egui::warn_if_debug_build(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) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.label(format!("{TITLE} v{}", env!("CARGO_PKG_VERSION")));
|
||||
ui.label(" | Powered by ");
|
||||
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
|
||||
ui.label(" and ");
|
||||
ui.hyperlink_to(
|
||||
"eframe",
|
||||
"https://github.com/emilk/egui/tree/master/crates/eframe",
|
||||
);
|
||||
ui.label(".");
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.label(format!("{TITLE} v{}", env!("CARGO_PKG_VERSION")));
|
||||
ui.label(" | Powered by ");
|
||||
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
|
||||
ui.label(" and ");
|
||||
ui.hyperlink_to(
|
||||
"eframe",
|
||||
"https://github.com/emilk/egui/tree/master/crates/eframe",
|
||||
);
|
||||
ui.label(".");
|
||||
});
|
||||
egui::warn_if_debug_build(ui);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
||||
mod app;
|
||||
pub(crate) mod app;
|
||||
pub use app::RollatorApp;
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -6,26 +6,28 @@ mod app;
|
|||
// When compiling natively:
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() -> eframe::Result {
|
||||
use libpt::log::debug;
|
||||
use libpt::log::{debug, error};
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
let mut app = app::RollatorApp::new_with_cli();
|
||||
|
||||
let filter = tracing_subscriber::EnvFilter::builder()
|
||||
.with_default_directive(tracing_subscriber::filter::LevelFilter::WARN.into())
|
||||
.with_default_directive(app.verbosity.level().into())
|
||||
.from_env()
|
||||
.expect("could not init logger")
|
||||
.add_directive(
|
||||
format!("rollator={}", app.verbosity.level())
|
||||
format!("{}={}", env!("CARGO_PKG_NAME"), app.verbosity.level())
|
||||
.parse()
|
||||
.expect("could not init logger"),
|
||||
);
|
||||
|
||||
tracing_subscriber::fmt::Subscriber::builder()
|
||||
.with_env_filter(filter)
|
||||
.with_max_level(app.verbosity.level())
|
||||
.finish()
|
||||
.set_default();
|
||||
.init();
|
||||
debug!("logging initialized!");
|
||||
debug!("level: {}", app.verbosity.level());
|
||||
|
||||
let native_options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
|
|
Loading…
Reference in New Issue