diff --git a/src/app/body/address/mod.rs b/src/app/body/address/mod.rs index cb4780a..96c4c81 100644 --- a/src/app/body/address/mod.rs +++ b/src/app/body/address/mod.rs @@ -2,8 +2,8 @@ use std::{mem, str::FromStr, sync::Arc}; use druid::{ widget::{ - prelude::*, Controller, CrossAxisAlignment, Either, Flex, Label, Spinner, TextBox, - ViewSwitcher, + prelude::*, Controller, CrossAxisAlignment, Either, Flex, Label, LineBreaking, Spinner, + TextBox, ViewSwitcher, }, Data, Env, EventCtx, Lens, Widget, WidgetExt as _, }; @@ -38,15 +38,18 @@ pub(in crate::app) fn build(parent: WidgetId) -> impl Widget { .controller(AddressController { parent }) .lens(AddressState::uri_lens); - let error_label = theme::error_label_scope(Label::dynamic(|data: &AddressState, _| { - if let Err(err) = data.uri.result() { - err.clone() - } else if let RequestState::ConnectFailed(err) = data.request_state() { - format!("failed to connect: {:?}", err) - } else { - String::default() - } - })); + let error_label = theme::error_label_scope( + Label::dynamic(|data: &AddressState, _| { + if let Err(err) = data.uri.result() { + err.clone() + } else if let RequestState::ConnectFailed(err) = data.request_state() { + format!("failed to connect: {:?}", err) + } else { + String::default() + } + }) + .with_line_break_mode(LineBreaking::WordWrap), + ); let error = Either::new( |data: &AddressState, _| { !data.uri.is_pristine_or_valid() diff --git a/src/app/command.rs b/src/app/command.rs index 7a37f24..94aa1a5 100644 --- a/src/app/command.rs +++ b/src/app/command.rs @@ -1,4 +1,4 @@ -use druid::Selector; +use druid::{Command, FileDialogOptions, Selector}; use prost_reflect::{MethodDescriptor, ServiceDescriptor}; use crate::app::sidebar::service::ServiceOptions; @@ -47,3 +47,8 @@ pub const DISCONNECT: Selector = Selector::new("app.disconnect"); /// Clear request history pub const CLEAR: Selector = Selector::new("app.clear"); + +/// Add services from a file +pub fn add_file() -> Command { + druid::commands::SHOW_OPEN_PANEL.with(FileDialogOptions::new()) +} diff --git a/src/app/menu.rs b/src/app/menu.rs index 19d1f75..8b2f3ec 100644 --- a/src/app/menu.rs +++ b/src/app/menu.rs @@ -1,7 +1,4 @@ -use druid::{ - keyboard_types::Key, menu, Env, FileDialogOptions, LocalizedString, Menu, MenuItem, SysMods, - WindowId, -}; +use druid::{keyboard_types::Key, menu, Env, LocalizedString, Menu, MenuItem, SysMods, WindowId}; use crate::app; @@ -22,7 +19,7 @@ fn file_menu() -> Menu { Menu::new(LocalizedString::new("common-menu-file-menu")) .entry( MenuItem::new(LocalizedString::new("common-menu-file-open")) - .command(druid::commands::SHOW_OPEN_PANEL.with(FileDialogOptions::new())) + .command(app::command::add_file()) .hotkey(SysMods::Cmd, "o"), ) .separator() diff --git a/src/app/sidebar/mod.rs b/src/app/sidebar/mod.rs index dd8334a..032d82e 100644 --- a/src/app/sidebar/mod.rs +++ b/src/app/sidebar/mod.rs @@ -6,12 +6,12 @@ use std::{iter::FromIterator, path::Path}; use anyhow::Result; use druid::{ widget::Scroll, - widget::{List, ListIter}, + widget::{Flex, Label, LineBreaking, List, ListIter, MainAxisAlignment}, Data, Lens, Widget, WidgetExt as _, }; use prost_reflect::ServiceDescriptor; -use crate::{protoc, theme}; +use crate::{app::command, protoc, theme, widget::Icon}; #[derive(Debug, Default, Clone, Data, Lens)] pub(in crate::app) struct State { @@ -26,11 +26,31 @@ pub(in crate::app) struct ServiceListState { } pub(in crate::app) fn build() -> impl Widget { - Scroll::new(List::new(service::build)) - .vertical() - .expand_height() - .background(druid::theme::BACKGROUND_LIGHT) - .env_scope(|env, _| theme::set_contrast(env)) + let add_button = Flex::row() + .with_child(Icon::add().padding(8.0)) + .with_child( + Label::new("Add file") + .with_font(theme::font::HEADER_ONE) + .with_line_break_mode(LineBreaking::Clip), + ) + .must_fill_main_axis(true) + .main_axis_alignment(MainAxisAlignment::Start) + .on_click(|ctx, _: &mut State, _| { + ctx.submit_command(command::add_file()); + }) + .background(theme::hot_or_active_painter(0.0)); + + Scroll::new( + Flex::column() + .with_child(List::new(service::build)) + .with_child(add_button) + .main_axis_alignment(MainAxisAlignment::SpaceBetween), + ) + .vertical() + .content_must_fill(true) + .expand_height() + .background(druid::theme::BACKGROUND_LIGHT) + .env_scope(|env, _| theme::set_contrast(env)) } impl State {