diff options
| author | pml68 <contact@pml68.me> | 2024-09-22 23:53:02 +0200 |
|---|---|---|
| committer | pml68 <contact@pml68.me> | 2024-09-23 00:00:45 +0200 |
| commit | f5c35e48c480355036778d26aacde498e5c15e68 (patch) | |
| tree | bf949b406bd8f0719f06095bb14bf9872a7493e5 /iced_builder/src/main.rs | |
| parent | Merge pull request #1 from pml68/feat/codegen (diff) | |
| download | iced-builder-f5c35e48c480355036778d26aacde498e5c15e68.tar.gz | |
feat: restructure project, start drag and drop
Diffstat (limited to 'iced_builder/src/main.rs')
| -rw-r--r-- | iced_builder/src/main.rs | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs new file mode 100644 index 0000000..e966614 --- /dev/null +++ b/iced_builder/src/main.rs @@ -0,0 +1,326 @@ +mod codegen; +mod types; + +use iced::{ + clipboard, executor, + highlighter::{self, Highlighter}, + theme, + widget::{ + button, column, container, + pane_grid::{self, Pane, PaneGrid}, + row, text, text_editor, tooltip, Column, Space, + }, + Alignment, Application, Color, Command, Element, Font, Length, Settings, +}; +use iced_drop::droppable; +use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; + +fn main() -> iced::Result { + App::run(Settings { + fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], + ..Settings::default() + }) +} + +struct App { + is_saved: bool, + current_project: Option<String>, + dark_theme: bool, + pane_state: pane_grid::State<Panes>, + focus: Option<Pane>, + designer_state: DesignerState, + element_list: Vec<types::ElementName>, + editor_content: text_editor::Content, +} + +#[derive(Debug, Clone)] +enum Message { + ToggleTheme, + CopyCode, + Drop(types::ElementName, iced::Point, iced::Rectangle), + HandleZones( + types::ElementName, + Vec<(iced::advanced::widget::Id, iced::Rectangle)>, + ), + Resized(pane_grid::ResizeEvent), + Clicked(pane_grid::Pane), + PaneDragged(pane_grid::DragEvent), +} + +#[derive(Clone, Debug)] +enum Panes { + Designer, + ElementList, +} + +impl Application for App { + type Message = Message; + type Theme = theme::Theme; + type Executor = executor::Default; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command<Message>) { + let state = pane_grid::State::with_configuration(pane_grid::Configuration::Split { + axis: pane_grid::Axis::Vertical, + ratio: 0.8, + a: Box::new(pane_grid::Configuration::Pane(Panes::Designer)), + b: Box::new(pane_grid::Configuration::Pane(Panes::ElementList)), + }); + ( + Self { + is_saved: true, + current_project: None, + dark_theme: true, + pane_state: state, + focus: None, + designer_state: DesignerState { + designer_content: vec![], + designer_page: DesignerPage::Designer, + }, + element_list: types::ElementName::ALL.to_vec(), + editor_content: text_editor::Content::with_text(&RenderedElement::test()), + }, + Command::none(), + ) + } + + fn title(&self) -> String { + let saved_state = if self.is_saved { "" } else { " *" }; + + let project_name = match &self.current_project { + Some(n) => format!(" - {n}"), + None => "".to_owned(), + }; + + format!("iced Builder{project_name}{saved_state}") + } + + fn theme(&self) -> iced::Theme { + if self.dark_theme { + theme::Theme::CatppuccinMocha + } else { + theme::Theme::CatppuccinLatte + } + } + + fn update(&mut self, message: Message) -> Command<Message> { + match message { + Message::ToggleTheme => self.dark_theme = !self.dark_theme, + Message::CopyCode => return clipboard::write(self.editor_content.text()), + Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { + self.pane_state.resize(split, ratio); + } + Message::Clicked(pane) => { + self.focus = Some(pane); + } + Message::Drop(name, point, _) => { + return iced_drop::zones_on_point( + move |zones| Message::HandleZones(name, zones), + point, + None, + None, + ) + .into() + } + Message::HandleZones(name, zones) => { + println!("{:?}\n{name}", zones); + } + Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { + self.pane_state.drop(pane, target); + } + Message::PaneDragged(_) => {} + } + + Command::none() + } + + fn view(&self) -> Element<Message> { + let header = row![button("Toggle Theme") + .on_press(Message::ToggleTheme) + .padding(5)] + .width(200); + let pane_grid = PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { + let is_focused = Some(id) == self.focus; + match pane { + Panes::Designer => match self.designer_state.designer_page { + DesignerPage::Designer => { + let content = container("") + .id(iced::widget::container::Id::new("drop_zone")) + .height(Length::Fill) + .width(Length::Fill); + let title = text("Designer").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new(content) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + DesignerPage::CodeView => { + let title = row![ + text("Generated Code").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }), + Space::with_width(Length::Fill), + tooltip( + button( + container( + text('\u{0e801}').font(Font::with_name("editor-icons")) + ) + .width(30) + .center_x() + ) + .on_press(Message::CopyCode), + "Copy code to clipboard", + tooltip::Position::Left + ) + ]; + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new( + text_editor(&self.editor_content) + .highlight::<Highlighter>( + highlighter::Settings { + theme: highlighter::Theme::Base16Mocha, + extension: "rs".to_string(), + }, + |highlight, _theme| highlight.to_format(), + ) + .height(Length::Fill) + .padding(20), + ) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + }, + Panes::ElementList => { + let items_list = items_list_view(self.element_list.clone()); + let content = column![items_list] + .align_items(Alignment::Center) + .height(Length::Fill) + .width(Length::Fill); + let title = text("Element List").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new(content) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + } + }) + .width(Length::Fill) + .height(Length::Fill) + .spacing(10) + .on_resize(10, Message::Resized) + .on_click(Message::Clicked) + .on_drag(Message::PaneDragged); + + let content = Column::new() + .push(header) + .push(pane_grid) + .spacing(5) + .align_items(Alignment::Center) + .width(Length::Fill); + + container(content).height(Length::Fill).into() + } +} + +const fn from_grayscale(grayscale: f32) -> Color { + Color { + r: grayscale, + g: grayscale, + b: grayscale, + a: 1.0, + } +} + +// #ffffff +const PANE_ID_COLOR_FOCUSED: Color = from_grayscale(1.0); + +// #e8e8e8 +const PANE_ID_COLOR_UNFOCUSED: Color = from_grayscale(0xE8 as f32 / 255.0); + +fn items_list_view(items: Vec<types::ElementName>) -> Element<'static, Message> { + let mut column = Column::new() + .spacing(20) + .align_items(Alignment::Center) + .width(Length::Fill); + + for item in items { + let value = item.clone(); + column = column.push( + droppable(text(value.to_string())) + .on_drop(move |point, rect| Message::Drop(value, point, rect)), + ); + } + + container(column).height(250.0).width(300).into() +} + +mod style { + use iced::widget::container; + use iced::{Border, Theme}; + + pub fn title_bar(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + text_color: Some(palette.background.strong.text), + background: Some(palette.background.strong.color.into()), + ..Default::default() + } + } + + pub fn pane_active(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + background: Some(palette.background.weak.color.into()), + border: Border { + width: 1.0, + color: palette.background.strong.color, + ..Border::default() + }, + ..Default::default() + } + } + + pub fn pane_focused(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + background: Some(palette.background.weak.color.into()), + border: Border { + width: 4.0, + color: palette.background.strong.color, + ..Border::default() + }, + ..Default::default() + } + } +} |
