diff --git a/dev_plugin/src/inline-view.tsx b/dev_plugin/src/inline-view.tsx index cfe0413..f8b4e22 100644 --- a/dev_plugin/src/inline-view.tsx +++ b/dev_plugin/src/inline-view.tsx @@ -1,4 +1,4 @@ -import { Content, Inline } from "@project-gauntlet/api/components"; +import { Content, Icons, Inline } from "@project-gauntlet/api/components"; import { ReactNode } from "react"; export default function InlineView(props: { text: string }): ReactNode | undefined { @@ -15,6 +15,12 @@ export default function InlineView(props: { text: string }): ReactNode | undefin + + + Testing inline view center + + + Testing inline view right diff --git a/js/api/src/gen/components.tsx b/js/api/src/gen/components.tsx index c6aa56d..e641339 100644 --- a/js/api/src/gen/components.tsx +++ b/js/api/src/gen/components.tsx @@ -110,7 +110,9 @@ declare global { ["gauntlet:form"]: { children?: ElementComponent; }; - ["gauntlet:inline_separator"]: {}; + ["gauntlet:inline_separator"]: { + icon?: Icons; + }; ["gauntlet:inline"]: { children?: ElementComponent; }; @@ -597,8 +599,11 @@ Form.Checkbox = Checkbox; Form.DatePicker = DatePicker; Form.Select = Select; Form.Separator = Separator; -export const InlineSeparator: FC = (): ReactNode => { - return ; +export interface InlineSeparatorProps { + icon?: Icons; +} +export const InlineSeparator: FC = (props: InlineSeparatorProps): ReactNode => { + return ; }; export interface InlineProps { children?: ElementComponent; diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 565a870..093aa7d 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -707,7 +707,8 @@ impl AppModel { Command::batch([ reposition(window::Id::MAIN, Position::Centered, Size::new(WINDOW_WIDTH, WINDOW_HEIGHT)), - window::resize(window::Id::MAIN, Size::new(WINDOW_WIDTH, WINDOW_HEIGHT)) + window::resize(window::Id::MAIN, Size::new(WINDOW_WIDTH, WINDOW_HEIGHT)), + focus(self.search_field_id.clone()), ]) } Some(PluginViewData { top_level_view: false, plugin_id, entrypoint_id, .. }) => { diff --git a/rust/client/src/ui/widget.rs b/rust/client/src/ui/widget.rs index 9708574..31f8950 100644 --- a/rust/client/src/ui/widget.rs +++ b/rust/client/src/ui/widget.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; use anyhow::anyhow; -use iced::{Font, Length, Padding}; +use iced::{Alignment, color, Font, Length, Padding}; use iced::alignment::Horizontal; use iced::font::Weight; use iced::widget::{button, checkbox, column, container, horizontal_rule, horizontal_space, image, pick_list, row, scrollable, Space, text, text_input, tooltip, vertical_rule, vertical_space}; @@ -118,6 +118,8 @@ pub enum ComponentRenderContext { H4, H5, H6, + Inline, + GridItem, List { widget_id: NativeUiWidgetId }, @@ -136,6 +138,12 @@ pub enum ComponentRenderContext { } } +impl ComponentRenderContext { + fn is_content_centered(&self) -> bool { + matches!(self, ComponentRenderContext::Inline | ComponentRenderContext::GridItem) + } +} + impl ComponentWidgetWrapper { pub fn widget( id: NativeUiWidgetId, @@ -431,12 +439,19 @@ impl ComponentWidgetWrapper { .into() } ComponentWidget::Paragraph { children } => { - let paragraph: Element<_> = render_children_string(children, context); + let centered = context.is_content_centered(); - container(paragraph) + let paragraph: Element<_> = render_children_string(children, ComponentRenderContext::None); + + let mut content = container(paragraph) .width(Length::Fill) - .padding(Padding::new(5.0)) - .into() + .padding(Padding::new(5.0)); + + if centered { + content = content.center_x() + } + + content.into() } // ComponentWidget::Link { children, href } => { // let content: Element<_> = render_children_string(children, ComponentRenderContext::None); @@ -458,8 +473,19 @@ impl ComponentWidgetWrapper { // } // } ComponentWidget::Image { source } => { - image(Handle::from_memory(source.data.clone())) // FIXME really expensive clone - .into() + let centered = context.is_content_centered(); + + let content: Element<_> = image(Handle::from_memory(source.data.clone())) // FIXME really expensive clone + .into(); + + let mut content = container(content) + .width(Length::Fill); + + if centered { + content = content.center_x() + } + + content.into() } ComponentWidget::H1 { children } => { render_children_string(children, ComponentRenderContext::H1) @@ -509,8 +535,21 @@ impl ComponentWidgetWrapper { // .into() // } ComponentWidget::Content { children } => { - column(render_children(children, ComponentRenderContext::None)) - .into() + let centered = context.is_content_centered(); + + let content: Element<_> = column(render_children(children, context)) + .into(); + + if centered { + container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } else { + content + } } ComponentWidget::Detail { children } => { let ComponentWidgetState::Detail { show_action_panel } = *state else { @@ -745,47 +784,66 @@ impl ComponentWidgetWrapper { render_root(show_action_panel, widget_id, children, content, context) } - ComponentWidget::InlineSeparator => { - vertical_rule(1) - .into() + ComponentWidget::InlineSeparator { icon } => { + match icon { + None => vertical_rule(1).into(), + Some(icon) => { + let top_rule: Element<_> = vertical_rule(1) + .into(); + + let top_rule = container(top_rule) + .center_x() + .into(); + + let icon = text(icon_to_bootstrap(icon)) + .font(icons::BOOTSTRAP_FONT) + .size(30) + .into(); + + let bot_rule: Element<_> = vertical_rule(1) + .into(); + + let bot_rule = container(bot_rule) + .center_x() + .into(); + + column([top_rule, icon, bot_rule]) + .align_items(Alignment::Center) + .into() + } + } } ComponentWidget::Inline { children } => { - let contents: Vec<_> = render_children_by_type(children, |widget| matches!(widget, ComponentWidget::Content { .. }), ComponentRenderContext::None) + let content: Vec> = children .into_iter() - .map(|content_element| { - container(content_element) - .width(Length::FillPortion(3)) - // .padding(Padding::from([5.0, 5.0, 0.0, 5.0])) - .into() + .map(|child| { + let (widget, _) = &*child.get(); + + match widget { + ComponentWidget::InlineSeparator { .. } => { + child.render_widget(ComponentRenderContext::None) + } + ComponentWidget::Content { .. } => { + let element = child.render_widget(ComponentRenderContext::Inline); + + container(element) + .width(Length::Fill) + .into() + } + _ => panic!("unexpected widget kind {:?}", widget) + } }) .collect(); - // let mut separators: Vec<_> = render_children_by_type(children, |widget| matches!(widget, ComponentWidget::InlineSeparator { .. }), ComponentRenderContext::None); - - // let mut left = contents.len(); - - let contents: Vec<_> = contents.into_iter() - .flat_map(|i| { - // if left > 1 { - // left = left - 1; - // if separators.is_empty() { - // let separator = vertical_rule(1).into(); - // vec![i, separator] - // } else { - // let separator = separators.remove(0); - // vec![i, separator] - // } - // } else { - vec![i] - // } - }) - .collect(); - - let content: Element<_> = row(contents) + let content: Element<_> = row(content) .into(); container(content) .padding(Padding::new(5.0)) + .center_x() + .center_y() + .height(100) + .max_height(100) .into() } ComponentWidget::EmptyView { title, description, image } => { @@ -922,7 +980,7 @@ impl ComponentWidgetWrapper { panic!("not supposed to be passed to grid item: {:?}", context) }; - let content: Element<_> = column(render_children(children, ComponentRenderContext::None)) + let content: Element<_> = column(render_children(children, ComponentRenderContext::GridItem)) .into(); let title: Element<_> = text(title) @@ -1211,6 +1269,8 @@ fn render_text_part<'a>(value: &str, context: ComponentRenderContext) -> Element ComponentRenderContext::Root { .. } => panic!("not supposed to be passed to text part"), ComponentRenderContext::ActionPanel { .. } => panic!("not supposed to be passed to text part"), ComponentRenderContext::Action { .. } => panic!("not supposed to be passed to text part"), + ComponentRenderContext::Inline => panic!("not supposed to be passed to text part"), + ComponentRenderContext::GridItem => panic!("not supposed to be passed to text part") }; let mut text = text(value); diff --git a/rust/component_model/src/lib.rs b/rust/component_model/src/lib.rs index 8e65c3f..d1a9150 100644 --- a/rust/component_model/src/lib.rs +++ b/rust/component_model/src/lib.rs @@ -833,7 +833,9 @@ pub fn create_component_model() -> Vec { let inline_separator_component = component( "inline_separator", "InlineSeparator", - [], + [ + property("icon", true, PropertyType::Enum { name: "Icons".to_owned() }), + ], children_none(), );