aboutsummaryrefslogtreecommitdiff
path: root/karlgui/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'karlgui/src/main.rs')
-rw-r--r--karlgui/src/main.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/karlgui/src/main.rs b/karlgui/src/main.rs
new file mode 100644
index 0000000..f4f9696
--- /dev/null
+++ b/karlgui/src/main.rs
@@ -0,0 +1,169 @@
+pub mod client;
+
+use crate::client::Client;
+use eframe::CreationContext;
+use egui::{text::LayoutJob, CentralPanel, Color32, Response, TextFormat, Ui, Widget};
+use karlcommon::{socket_path, ClientboundPacket, Condition, Schedule, ServerboundPacket, Task};
+use log::{error, info};
+use std::{
+ ops::{Deref, DerefMut, Range},
+ os::unix::net::UnixStream,
+ process::exit,
+};
+
+fn main() {
+ env_logger::init();
+ eframe::run_native(
+ "karlender",
+ eframe::NativeOptions::default(),
+ Box::new(move |cc| Box::new(App::new(cc))),
+ )
+}
+
+struct App {
+ client: Client,
+ tasks: Vec<ShowOrEdit<Task>>,
+}
+
+impl App {
+ pub fn new(cc: &CreationContext) -> Self {
+ cc.egui_ctx.set_visuals(egui::Visuals::dark());
+
+ let socket = match UnixStream::connect(socket_path()) {
+ Ok(s) => s,
+ Err(e) => {
+ error!("failed to connect to socket: {}", e);
+ exit(1)
+ }
+ };
+
+ let mut client = Client::new(socket);
+ info!("connected");
+
+ client.send(ServerboundPacket::ListTasks);
+ App {
+ client,
+ tasks: vec![],
+ }
+ }
+
+ pub fn update_network(&mut self) {
+ for p in self.client.receiver.try_iter() {
+ match p {
+ ClientboundPacket::TaskList(t) => {
+ self.tasks = t.into_iter().map(|t| ShowOrEdit::new(t, false)).collect()
+ }
+ _ => {}
+ }
+ }
+ }
+}
+
+impl eframe::App for App {
+ fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
+ self.update_network();
+ CentralPanel::default().show(ctx, |ui| {
+ for t in &mut self.tasks {
+ t.ui(ui);
+ ui.separator();
+ }
+ });
+ }
+}
+
+struct ShowOrEdit<T> {
+ pub inner: T,
+ pub edit: bool,
+}
+
+impl<T: EditableWidget> ShowOrEdit<T> {
+ pub fn new(inner: T, edit: bool) -> ShowOrEdit<T> {
+ Self { inner, edit }
+ }
+ pub fn ui(&mut self, ui: &mut Ui) {
+ match self.edit {
+ true => {
+ if ui.button("💾 Save").clicked() {
+ self.edit = false;
+ }
+ }
+ false => {
+ if ui.button("✏ Edit").clicked() {
+ self.edit = true;
+ }
+ }
+ }
+ self.inner.ui(ui, self.edit);
+ }
+}
+
+trait EditableWidget {
+ fn ui(&mut self, ui: &mut Ui, edit: bool);
+}
+
+impl EditableWidget for Task {
+ fn ui(&mut self, ui: &mut Ui, edit: bool) {
+ ui.heading(&self.name);
+ ui.indent((), |ui| {
+ if let Some(d) = &self.description {
+ ui.label(d);
+ }
+ ui.horizontal(|ui| {
+ ui.label("Tags:");
+ for t in &self.tags {
+ ui.colored_label(Color32::LIGHT_GREEN, t);
+ }
+ });
+ self.schedule.ui(ui, edit);
+ });
+ }
+}
+
+impl EditableWidget for Schedule {
+ fn ui(&mut self, ui: &mut Ui, edit: bool) {
+ match self {
+ Schedule::Never => {
+ let mut j = LayoutJob::default();
+ j.append(
+ "No schedule",
+ 0.0,
+ TextFormat {
+ italics: true,
+ ..Default::default()
+ },
+ );
+ ui.label(j);
+ }
+ Schedule::Dynamic {
+ priority,
+ scheduled,
+ duration,
+ condition,
+ } => {
+ let mut j = LayoutJob::default();
+ let f = TextFormat {
+ italics: true,
+ ..Default::default()
+ };
+ j.append("Dynamic with priority", 0.0, f.clone());
+ j.append(&format!(" {} ", priority), 0.0, f.clone());
+
+ ui.label(j);
+ }
+ Schedule::Condition(c) => c.ui(ui, edit),
+ Schedule::Static(t) => t.ui(ui, edit),
+ }
+ }
+}
+
+impl EditableWidget for Condition {
+ fn ui(&mut self, ui: &mut Ui, edit: bool) {
+ ui.label("todo");
+ }
+}
+
+impl EditableWidget for Range<i64> {
+ fn ui(&mut self, ui: &mut Ui, edit: bool) {
+ ui.label("todo");
+ }
+}