Skip to content

Commit

Permalink
feat: Add "Create" form modal, Close #2
Browse files Browse the repository at this point in the history
  • Loading branch information
wangeguo committed Dec 29, 2023
1 parent 02d8c24 commit 375dd88
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "desktop"
version = "0.3.4"
version = "0.3.5"
edition = "2021"

[[bin]]
Expand Down
161 changes: 161 additions & 0 deletions src/views/compose.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright 2023 The Amphitheatre Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::widgets::Element;
use crate::widgets::Renderer;
use crate::widgets::Row;
use crate::widgets::{Button, Card, Column, Container, Scrollable, Text, TextInput};
use iced::widget::horizontal_space;
use iced::Alignment;
use iced::{alignment::Horizontal, widget::Component, Length};

pub struct Compose<Message> {
form: Form,
on_change: Box<dyn Fn(Form) -> Message>,
on_cancel: Message,
on_submit: Message,
}

#[derive(Clone)]
pub enum Event {
TitleChanged(String),
DescriptionChanged(String),
RepositoryChanged(String),

CancelButtonPressed,
SubmitButtonPressed,
}

#[derive(Clone, Debug, Default)]
pub struct Form {
title: String,
description: String,
repository: String,
}

impl<Message: Clone> Compose<Message> {
pub fn new(
form: Form,
on_change: impl Fn(Form) -> Message + 'static,
on_cancel: Message,
on_submit: Message,
) -> Self {
Self {
form,
on_change: Box::new(on_change),
on_cancel,
on_submit,
}
}
}

impl<Message: Clone> Component<Message, Renderer> for Compose<Message> {
type State = ();
type Event = Event;

fn update(&mut self, _state: &mut Self::State, event: Self::Event) -> Option<Message> {
match event {
Event::TitleChanged(title) => {
self.form.title = title;
Some((self.on_change)(self.form.clone()))
}
Event::DescriptionChanged(description) => {
self.form.description = description;
Some((self.on_change)(self.form.clone()))
}
Event::RepositoryChanged(repository) => {
self.form.repository = repository;
Some((self.on_change)(self.form.clone()))
}
Event::CancelButtonPressed => Some(self.on_cancel.clone()),
Event::SubmitButtonPressed => Some(self.on_submit.clone()),
}
}

fn view(&self, _state: &Self::State) -> Element<Self::Event> {
let element = Card::new(
Text::new("Compose a new playbook")
.size(20)
.width(Length::Fill)
.horizontal_alignment(Horizontal::Left),
self.form(),
)
.foot(self.actions())
.padding(16.0);

let content = Scrollable::new(element);
Container::new(Column::new().push(content).max_width(480))
.padding(10)
.center_x()
.center_y()
.into()
}
}

impl<Message> Compose<Message> {
fn form(&self) -> Element<Event> {
let title = Column::with_children(vec![
Text::new("Add a title").into(),
TextInput::new("Title", &self.form.title)
.on_input(Event::TitleChanged)
.into(),
])
.into();

let description = Column::with_children(vec![
Text::new("Add a description").into(),
TextInput::new("Add your description here...", &self.form.description)
.on_input(Event::DescriptionChanged)
.into(),
])
.into();

let repository = Column::with_children(vec![
Text::new("Repository").into(),
TextInput::new("An SSH URL, like [email protected]:user/repo.git", &self.form.repository)
.on_input(Event::RepositoryChanged)
.into(),
])
.into();

Column::with_children(vec![title, description, repository])
.spacing(16)
.into()
}

fn actions(&self) -> Element<Event> {
let cancel_button = Button::new(Text::new("Cancel")).on_press(Event::CancelButtonPressed);
let submit_button = Button::new(Text::new("Submit")).on_press(Event::SubmitButtonPressed);

Container::new(
Row::new()
.push(cancel_button)
.push(horizontal_space(Length::Fill))
.push(submit_button)
.width(Length::Fill)
.align_items(Alignment::Center),
)
.padding(16)
.into()
}
}

impl<'a, Message> From<Compose<Message>> for Element<'a, Message>
where
Message: 'a + Clone,
{
fn from(component: Compose<Message>) -> Self {
iced::widget::component(component)
}
}
1 change: 1 addition & 0 deletions src/views/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
// limitations under the License.

pub mod body;
pub mod compose;
pub mod detail;
pub mod sidebar;
58 changes: 48 additions & 10 deletions src/views/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ use iced_aw::{Icon, ICON_FONT};
use crate::cmd::playbook::refresh_playbooks;
use crate::context::Context;
use crate::styles;
use crate::widgets::{Button, Column, Element, Row, Scrollable, Text, TextInput};
use crate::widgets::{Button, Column, Element, Modal, Row, Scrollable, Text, TextInput};

use super::compose::{self, Compose};

#[derive(Debug)]
pub struct Sidebar {
ctx: Arc<Context>,
query: String,
playbooks: Vec<Playbook>,
state: State,
show_modal: bool,
compose_form: compose::Form,
}

impl Default for Sidebar {
Expand All @@ -50,6 +54,10 @@ pub enum Message {
CreateButtonPressed,
TextInputChanged(String),
PlaybookSelected(Playbook),

CloseComposeModal,
ComposeFormChanged(compose::Form),
ComposeFormSubmit,
}

impl Sidebar {
Expand All @@ -59,6 +67,8 @@ impl Sidebar {
query: String::new(),
playbooks: vec![],
state: State::Connecting,
show_modal: false,
compose_form: compose::Form::default(),
}
}

Expand All @@ -72,11 +82,21 @@ impl Sidebar {
self.state = State::Connected;
}
Message::ContextSelectorPressed => {}
Message::CreateButtonPressed => {}
Message::CreateButtonPressed => self.show_modal = true,
Message::TextInputChanged(query) => self.query = query,
Message::PlaybookSelected(playbook) => {
println!("Playbook selected: {:?}", playbook);
}
Message::CloseComposeModal => {
self.show_modal = false;
self.compose_form = compose::Form::default();
}
Message::ComposeFormChanged(form) => self.compose_form = form,
Message::ComposeFormSubmit => {
println!("Form submitted: {:?}", self.compose_form);
self.show_modal = false;
self.compose_form = compose::Form::default();
}
}
Command::none()
}
Expand Down Expand Up @@ -154,17 +174,35 @@ impl Sidebar {
fn omnibox(&self) -> Element<Message> {
Row::new()
.push(TextInput::new("Search", &self.query).on_input(Message::TextInputChanged))
.push(
Button::new(
Text::new(icon_to_char(Icon::Plus).to_string())
.font(ICON_FONT)
.width(Length::Fixed(20.0)),
)
.on_press(Message::CreateButtonPressed),
)
.push(self.button())
.spacing(4)
.into()
}

fn button(&self) -> Element<Message> {
let underlay = Button::new(
Text::new(icon_to_char(Icon::Plus).to_string())
.font(ICON_FONT)
.width(Length::Fixed(20.0)),
)
.on_press(Message::CreateButtonPressed);

let overlay = if self.show_modal {
Some(Compose::new(
self.compose_form.clone(),
Message::ComposeFormChanged,
Message::CloseComposeModal,
Message::ComposeFormSubmit,
))
} else {
None
};

Modal::new(underlay, overlay)
.backdrop(Message::CloseComposeModal)
.on_esc(Message::CloseComposeModal)
.into()
}
}

#[derive(Clone, Debug)]
Expand Down

0 comments on commit 375dd88

Please sign in to comment.