From fb2bdc3b7381949b29908cc2e57174a80c496a06 Mon Sep 17 00:00:00 2001 From: GenericConfluent Date: Mon, 4 Mar 2024 14:34:48 -0700 Subject: [PATCH] feat: more validation and icons --- fonts/icons.ttf | Bin 0 -> 2504 bytes src/course_database.rs | 30 ++++++++++----- src/icons.rs | 37 +++++++++++++++++++ src/lib.rs | 81 +++++++++++++++++++++++++---------------- src/main.rs | 9 +---- 5 files changed, 109 insertions(+), 48 deletions(-) create mode 100644 fonts/icons.ttf create mode 100644 src/icons.rs diff --git a/fonts/icons.ttf b/fonts/icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3209cb1aac90c52c5edda5e1bd40db968abcd7ee GIT binary patch literal 2504 zcmb_eU5pb|6h3EW+UXDO($a2$w9szZ4x;JI_LqXNT?@OyLO{?Z5~GxE+bwCgwH*kX z7;M4=nh+$}HL@GtcvT;K@Wp7>M6*0l^-*685ile^n2?xgLZ_ZPo$j(ICI;`!z2`gM zJ?GB3_ntd5fB;~FCh*`ta$Jr~ewIrC;C5msbCqn(a^3Vc>DQ?qES6^r?bA2E0^t4t z5Drb}vr|$c^A_2DPc<=3gyocACj1fAZPOL?yq5s^HmUKJtGO({Q3#R$ebU!gvgd2K z8OR?kr2A*GmHdwzed_?kePnZ>R&A*KR=yv=dX@Tj0>vs2fY({u(FBe$Yl^5d#LmkT zZ@k(6!tY?$J>X~iJG+=AlN~Ao3e496oZ-(j5%OTwb;jE0qQ~eug(v`C1ru1f_qbcc zz2BAiD@0iI9GG~+@-cC=uzw2DFbZQv?AVKu88YrkmAcrNta^=^?jwyw>Ln+(M`n_! z&ta_7jdYaB&XQqICdl^VX$x|^VER7>dfGsq%Ks#M3BTZr_yZTBy`~}n|1E5StuO@7 zK@@gF24E<*2ZN5FJKzYo1958OK}VEH1zkRj8?8gf?#OP82i=OpkZ4G5u~pO-v4Alw zXp6-nYumTDMO)^uUfTtRV%E%I4i<1>VL>Z)Z$A3W$wEs5)^d8=YLVI^rPb=@roi$= zgnsZ)+!Dx;gb^4A7^b)>V#UEEMcb43n6C3=r}HG^EB$eYK1)h@?A7K9V$2$C-7BY- z5Z2e4)RFdneOpVnmvq7PNM`YH$+9!uTr$l3w-GctEGs9_L8}kZ!BE^0OGE>a0k^|t z4hEDVT1N^-smqy+%zIh;@Gf52^vSYTUnS&Bo%cSx%T57wI{YY|XdAe}PkWi5^9F&z zP%IG{pb?cvApRFN9iNyuGcggzrUA4b_!owG^logfKmuzuwvl=cY83GaI1N*jeIXgc zD2Y-sv1>i4AnYhK_xi_emW$x z)Q&@$WDTZuz78eGL6vM9aE$DVaF+VB6O zOqI64Pqj{CR0xgfW3d>^bXAF|>3wI2$&qzfk5nag7rn4$@?_6mwGZp@S6KG+{zurq W$3GSNZXt&}{l1vcjQUrMnf(PMgsGwc literal 0 HcmV?d00001 diff --git a/src/course_database.rs b/src/course_database.rs index f7a69ca..e5d6509 100644 --- a/src/course_database.rs +++ b/src/course_database.rs @@ -41,15 +41,25 @@ impl FromStr for CourseId { .split_whitespace() .filter(|string| !string.trim_start().is_empty()); + let subject_id = pieces + .next() + .ok_or(anyhow!("Could not find a subject id"))? + .to_uppercase() + .to_string(); + let class_id = pieces + .next() + .ok_or(anyhow!("Could not find the class id"))? + .parse()?; + + if class_id < 100 || 1000 <= class_id { + return Err(anyhow!( + "Class id must be a number, at least 100 and less than 1000" + )); + } + Ok(CourseId { - subject_id: pieces - .next() - .ok_or(anyhow!("Could not find a subject id"))? - .to_string(), - class_id: pieces - .next() - .ok_or(anyhow!("Could not find the class id"))? - .parse()?, + subject_id, + class_id, }) } } @@ -60,7 +70,7 @@ impl std::fmt::Display for CourseId { } } -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone, Debug)] pub struct Course { pub id: CourseId, pub name: String, @@ -83,6 +93,7 @@ impl Course { } } +#[derive(Debug)] pub enum DatabaseNode { Course(Course), Or, @@ -115,6 +126,7 @@ impl fmt::Display for DatabaseNode { /// Abstraction over some way to retrieve course info for simplicity. /// There must only be one entry for each course. +#[derive(Debug)] pub struct CourseDatabase { pub courses: DiGraph, } diff --git a/src/icons.rs b/src/icons.rs new file mode 100644 index 0000000..389ebb4 --- /dev/null +++ b/src/icons.rs @@ -0,0 +1,37 @@ +use iced::alignment; +use iced::font::Font; +use iced::widget::{text, Text}; + +/// See https://fonts.google.com/icons. Just find some online icon font editor +/// to modify the ttf file. +pub enum Icon { + AccountTree, + FullStackedBarChart, + SideNavigation, + DragIndicator, + Settings, + DeleteForever, +} + +impl Icon { + pub fn bytes() -> &'static [u8] { + include_bytes!("../fonts/icons.ttf").as_slice() + } +} + +impl Into> for Icon { + fn into(self) -> Text<'static> { + let ch = match self { + Icon::AccountTree => "A", + Icon::FullStackedBarChart => "B", + Icon::SideNavigation => "C", + Icon::DragIndicator => "D", + Icon::Settings => "E", + Icon::DeleteForever => "F", + }; + text(ch) + .font(Font::with_name("icons")) + .width(20) + .horizontal_alignment(alignment::Horizontal::Center) + } +} diff --git a/src/lib.rs b/src/lib.rs index 14e459e..151c3a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,22 @@ #![allow(dead_code, unused_variables)] +use std::rc::Rc; +use std::sync::Arc; + +use iced::alignment::Horizontal; use iced::theme::Palette; -use iced::widget::{button, column, container, horizontal_rule, row, text, text_input}; -use iced::{executor, Application, Color, Command, Element, Length, Theme}; +use iced::widget::{button, column, container, horizontal_rule, row, text, text_input, Text}; +use iced::{executor, font, Application, Color, Command, Element, Length, Padding, Theme}; use iced_aw::native::Split; -use iced_aw::{modal, split, Card}; +use iced_aw::{modal, split, Card, BOOTSTRAP_FONT_BYTES}; mod course_database; use course_database::{CourseDatabase, CourseId}; +use icons::Icon; mod graph_widget; +mod icons; #[derive(Default)] pub struct FinescaleApp { @@ -26,22 +32,18 @@ struct UiStates { course_input_val: String, } -#[derive(Default, Debug, Clone)] -pub struct CourseGraph; - #[derive(Debug, Clone)] pub enum Message { - LoadedCourses(CourseGraph), + LoadedCourses(Arc>), MainDividerResize(u16), CourseInputEvent(String), CourseInputSubmit, + IconsLoaded(Result<(), font::Error>), ClearError, } -async fn load_courses>(path: P) -> CourseGraph { - //let reader = std::fs::File::open(path).unwrap(); - //let _json: serde_json::Value = serde_json::from_reader(reader).unwrap(); - CourseGraph +async fn load_courses>(path: P) -> Arc> { + CourseDatabase::new("[]").into() } impl Application for FinescaleApp { @@ -53,7 +55,10 @@ impl Application for FinescaleApp { fn new(_: Self::Flags) -> (Self, Command) { ( FinescaleApp::default(), - Command::perform(load_courses("data/courses.json"), Message::LoadedCourses), + Command::batch([ + Command::perform(load_courses("data/courses.ron"), Message::LoadedCourses), + iced::font::load(icons::Icon::bytes()).map(Message::IconsLoaded), + ]), ) } @@ -63,7 +68,6 @@ impl Application for FinescaleApp { fn update(&mut self, _message: Self::Message) -> Command { match _message { - Message::LoadedCourses(_) => {} Message::ClearError => self.ui_states.error_modal = None, // TODO: Limit the divider movement Message::MainDividerResize(amt) => self.ui_states.main_divider_pos = Some(amt), @@ -79,6 +83,7 @@ impl Application for FinescaleApp { } } } + _ => {} } Command::none() @@ -86,16 +91,37 @@ impl Application for FinescaleApp { fn view(&self) -> Element { let mut left = column![ - text("Desired Classes"), + text("Desired Classes") + .width(Length::Fill) + .size(40) + .style(Color::from_rgb(0.5, 0.5, 0.5)) + .horizontal_alignment(Horizontal::Center), text_input("Start typing!", &self.ui_states.course_input_val) + .padding(15) .on_input(Message::CourseInputEvent) .on_submit(Message::CourseInputSubmit), + ] + .spacing(10); + + let mut right = column![ + row![text("Required Classes") + .width(Length::Fill) + .size(40) + .style(Color::from_rgb(0.5, 0.5, 0.5)) + .horizontal_alignment(iced::alignment::Horizontal::Left),], + horizontal_rule(2) ]; - let mut right = column![row![text("Required Classes"),], horizontal_rule(2)]; for course in self.desired_courses.iter() { + left = left.push( + row![ + text(course).width(Length::Fill), + button(Into::::into(Icon::DeleteForever)).padding(10) + ] + .spacing(20) + .align_items(iced::Alignment::Center), + ); right = right.push(text(course)); - left = left.push(text(course)); } // Todo read and push courses. @@ -108,26 +134,19 @@ impl Application for FinescaleApp { ); let overlay = self.ui_states.error_modal.as_ref().map(|err_msg| { - Card::new(text("Error"), text(err_msg)).foot( - container(button("Ok").on_press(Message::ClearError)) - .width(Length::Fill) - .align_x(iced::alignment::Horizontal::Right), - ) + Card::new(text("Error"), text(err_msg)) + .foot( + container(button("Ok").on_press(Message::ClearError)) + .width(Length::Fill) + .align_x(iced::alignment::Horizontal::Right), + ) + .max_width(250.0) }); modal(main_content, overlay).into() } fn theme(&self) -> Self::Theme { - Theme::custom( - "apptheme".to_string(), - Palette { - background: Color::from_rgba8(14, 14, 14, 0.1), - text: Color::WHITE, - primary: Color::WHITE, - success: Color::WHITE, - danger: Color::WHITE, - }, - ) + iced::Theme::Light } } diff --git a/src/main.rs b/src/main.rs index 21b6e69..a796eb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,18 +5,11 @@ fn main() -> iced::Result { id: Some("finescale".into()), window: window::Settings { size: iced::Size::new(800.0, 400.0), - position: window::Position::Centered, visible: true, - decorations: false, - transparent: true, + decorations: true, ..Default::default() }, antialiasing: true, ..Default::default() }) - // let reader = std::fs::File::open("data/courses.json").unwrap(); - // let _json: serde_json::Value = serde_json::from_reader(reader).unwrap(); - // let mut graph = Graph::<&str, &str>::new(); - - // Ok(()) }