diff --git a/Cargo.toml b/Cargo.toml index 0543e1f..db45a3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,27 @@ [package] -name = "template" +name = "rollator" version = "0.1.0" edition = "2021" publish = false authors = ["Christoph J. Scherr "] license = "MIT" -description = "No description yet" +description = "GUI toolkit for tabletop games" readme = "README.md" -homepage = "https://git.cscherr.de/PlexSheep/rs-base" -repository = "https://git.cscherr.de/PlexSheep/rs-base" -keywords = ["template"] +homepage = "https://git.cscherr.de/PlexSheep/rollator" +repository = "https://git.cscherr.de/PlexSheep/rollator" +keywords = [] [dependencies] - +egui = "0.28.1" +eframe = { version = "0.28.1", default-features = true, features = [ + "accesskit", # Make egui compatible with screen readers. NOTE: adds a lot of dependencies. + "default_fonts", # Embed the default egui fonts. + "glow", # Use the glow rendering backend. Alternative: "wgpu". + "persistence", # Enable restoring app state when restarting the app. +] } +log = "0.4" +env_logger = "0.10" +serde = "1.0.207" +libpt = { version = "0.6.0", features = ["cli"] } +clap = { version = "4.5.15", features = ["derive"] } diff --git a/README.md b/README.md index a27185f..d5be8ff 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ -# rs-base +# Rollator -Base repository for rust projects \ No newline at end of file +Rollator (pun intended) is a small GUI tool that aids with playing tabletop +games. diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100755 index 0000000..61ad031 Binary files /dev/null and b/assets/favicon.ico differ diff --git a/assets/icon-1024.png b/assets/icon-1024.png new file mode 100644 index 0000000..1b5868a Binary files /dev/null and b/assets/icon-1024.png differ diff --git a/assets/icon-256.png b/assets/icon-256.png new file mode 100644 index 0000000..ae72287 Binary files /dev/null and b/assets/icon-256.png differ diff --git a/assets/icon_ios_touch_192.png b/assets/icon_ios_touch_192.png new file mode 100644 index 0000000..8472802 Binary files /dev/null and b/assets/icon_ios_touch_192.png differ diff --git a/assets/manifest.json b/assets/manifest.json new file mode 100644 index 0000000..2a137fb --- /dev/null +++ b/assets/manifest.json @@ -0,0 +1,28 @@ +{ + "name": "egui Template PWA", + "short_name": "egui-template-pwa", + "icons": [ + { + "src": "./icon-256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "./maskable_icon_x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "./icon-1024.png", + "sizes": "1024x1024", + "type": "image/png" + } + ], + "lang": "en-US", + "id": "/index.html", + "start_url": "./index.html", + "display": "standalone", + "background_color": "white", + "theme_color": "white" +} diff --git a/assets/maskable_icon_x512.png b/assets/maskable_icon_x512.png new file mode 100644 index 0000000..db8df3e Binary files /dev/null and b/assets/maskable_icon_x512.png differ diff --git a/assets/sw.js b/assets/sw.js new file mode 100644 index 0000000..7ecd229 --- /dev/null +++ b/assets/sw.js @@ -0,0 +1,25 @@ +var cacheName = 'egui-template-pwa'; +var filesToCache = [ + './', + './index.html', + './eframe_template.js', + './eframe_template_bg.wasm', +]; + +/* Start the service worker and cache all of the app's content */ +self.addEventListener('install', function (e) { + e.waitUntil( + caches.open(cacheName).then(function (cache) { + return cache.addAll(filesToCache); + }) + ); +}); + +/* Serve cached content when offline */ +self.addEventListener('fetch', function (e) { + e.respondWith( + caches.match(e.request).then(function (response) { + return response || fetch(e.request); + }) + ); +}); diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..71a5779 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,122 @@ +use clap::Parser; +use libpt::cli::args::HELP_TEMPLATE; + +pub const TITLE: &str = "Rollator"; + +/// We derive Deserialize/Serialize so we can persist app state on shutdown. +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Parser)] +#[command(help_template = HELP_TEMPLATE, author, version)] +#[serde(default)] // if we add new fields, give them default values when deserializing old state +pub struct RollatorApp { + // Example stuff: + label: String, + + #[serde(skip)] // This how you opt-out of serialization of a field + value: f32, +} + +impl Default for RollatorApp { + fn default() -> Self { + Self { + label: TITLE.to_owned(), + value: 2.7, + } + } +} + +impl RollatorApp { + /// Called once before the first frame. + pub fn new(cc: &eframe::CreationContext<'_>) -> Self { + // This is also where you can customize the look and feel of egui using + // `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`. + + // Load previous app state (if any). + // Note that you must enable the `persistence` feature for this to work. + if let Some(storage) = cc.storage { + return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); + } + + Default::default() + } + + pub fn new_with_cli(cc: &eframe::CreationContext<'_>) -> Self { + let app = Self::parse(); + + // Load previous app state (if any). + // Note that you must enable the `persistence` feature for this to work. + if let Some(storage) = cc.storage { + return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); + } + + app + } +} + +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: + + 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("Quit").clicked() { + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + }); + ui.add_space(16.0); + } + + egui::widgets::global_dark_light_mode_buttons(ui); + }); + }); + + 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); + }); + }); + } +} + +fn bottom_label(ui: &mut egui::Ui) { + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.hyperlink_to("Source Code ", env!("CARGO_PKG_REPOSITORY")); + 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("."); + }); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6ae8080 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::all, rust_2018_idioms)] + +mod app; +pub use app::RollatorApp; diff --git a/src/main.rs b/src/main.rs index e7a11a9..74fd3a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,29 @@ -fn main() { - println!("Hello, world!"); +#![warn(clippy::all, rust_2018_idioms)] +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release + +mod app; + +// When compiling natively: +#[cfg(not(target_arch = "wasm32"))] +fn main() -> eframe::Result { + env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). + + let native_options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default() + .with_inner_size([400.0, 300.0]) + .with_min_inner_size([300.0, 220.0]) + .with_icon( + // NOTE: Adding an icon is optional + eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..]) + .expect("Failed to load icon"), + ), + ..Default::default() + }; + eframe::run_native( + app::TITLE, + native_options, + Box::new(|cc| Ok(Box::new(app::RollatorApp::new_with_cli(cc)))), + ) } + +// TODO: add a main for compiling to wasm