mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Rework shortcut selector ui in settings
This commit is contained in:
parent
aacf78a377
commit
ae0f833f15
6 changed files with 416 additions and 290 deletions
|
|
@ -76,7 +76,7 @@ impl PluginWidgetContainer {
|
|||
self.images = images;
|
||||
|
||||
// use new state with values from old state but only widget ids which exists in new state
|
||||
// so we this way we use already existing values but remove state for removed widgets
|
||||
// so this way we use already existing values but remove state for removed widgets
|
||||
let old_state = mem::replace(&mut self.state, create_state(&container));
|
||||
|
||||
for (key, value) in old_state.into_iter() {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
use gauntlet_common::model::EntrypointId;
|
||||
use gauntlet_common::model::PhysicalShortcut;
|
||||
use gauntlet_common::model::PluginId;
|
||||
use gauntlet_common_ui::physical_key_model;
|
||||
use gauntlet_common_ui::shortcut_to_text;
|
||||
use iced::advanced::graphics::core::event;
|
||||
use iced::advanced::graphics::core::keyboard;
|
||||
use iced::advanced::layout;
|
||||
use iced::advanced::mouse;
|
||||
use iced::advanced::overlay;
|
||||
use iced::advanced::renderer;
|
||||
use iced::advanced::widget::tree;
|
||||
use iced::advanced::widget::Tree;
|
||||
|
|
@ -12,95 +15,123 @@ use iced::advanced::Clipboard;
|
|||
use iced::advanced::Layout;
|
||||
use iced::advanced::Shell;
|
||||
use iced::advanced::Widget;
|
||||
use iced::alignment;
|
||||
use iced::keyboard::key::Physical;
|
||||
use iced::mouse::Button;
|
||||
use iced::widget::column;
|
||||
use iced::widget::container;
|
||||
use iced::widget::container::draw_background;
|
||||
use iced::widget::container::layout;
|
||||
use iced::widget::row;
|
||||
use iced::widget::text;
|
||||
use iced::Element;
|
||||
use iced::Alignment;
|
||||
use iced::Event;
|
||||
use iced::Length;
|
||||
use iced::Padding;
|
||||
use iced::Point;
|
||||
use iced::Rectangle;
|
||||
use iced::Renderer;
|
||||
use iced::Size;
|
||||
use iced::Vector;
|
||||
|
||||
pub struct ShortcutSelector<'a, Message, Theme>
|
||||
where
|
||||
Theme: Catalog + text::Catalog + container::Catalog,
|
||||
{
|
||||
padding: Padding,
|
||||
width: Length,
|
||||
height: Length,
|
||||
max_width: f32,
|
||||
max_height: f32,
|
||||
horizontal_alignment: alignment::Horizontal,
|
||||
vertical_alignment: alignment::Vertical,
|
||||
use crate::theme::text::TextStyle;
|
||||
use crate::theme::Element;
|
||||
use crate::theme::GauntletSettingsTheme;
|
||||
|
||||
on_shortcut_captured: Box<dyn Fn(Option<PhysicalShortcut>) -> Message + 'a>,
|
||||
on_capturing_change: Box<dyn Fn(bool) -> Message + 'a>,
|
||||
|
||||
content: Element<'a, Message, Theme>,
|
||||
pub struct ShortcutData {
|
||||
pub shortcut: Option<PhysicalShortcut>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a, Message: 'a, Theme> ShortcutSelector<'a, Message, Theme>
|
||||
pub fn shortcut_selector<'a, 'b: 'a, 'c, Message: 'a, Id: 'a, F>(
|
||||
shortcut_id: Id,
|
||||
current_shortcut: &'b ShortcutData,
|
||||
on_shortcut_captured: F,
|
||||
overlay_class: <GauntletSettingsTheme as container::Catalog>::Class<'a>,
|
||||
) -> Element<'a, Message>
|
||||
where
|
||||
Theme: Catalog + text::Catalog + container::Catalog + 'a,
|
||||
F: 'a + Fn(Id, Option<PhysicalShortcut>) -> Message,
|
||||
Id: Clone,
|
||||
{
|
||||
pub fn new<F, F2>(
|
||||
current_shortcut: &Option<PhysicalShortcut>,
|
||||
Element::new(ShortcutSelector::new::<F>(
|
||||
shortcut_id,
|
||||
current_shortcut,
|
||||
on_shortcut_captured,
|
||||
overlay_class,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ShortcutId {
|
||||
Global,
|
||||
Entrypoint {
|
||||
plugin_id: PluginId,
|
||||
entrypoint_id: EntrypointId,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct ShortcutSelector<'a, 'b, Message, Id>
|
||||
where
|
||||
Id: Clone,
|
||||
{
|
||||
on_shortcut_captured: Box<dyn Fn(Id, Option<PhysicalShortcut>) -> Message + 'a>,
|
||||
|
||||
shortcut_id: Id,
|
||||
current_shortcut: &'b ShortcutData,
|
||||
|
||||
content: Element<'a, Message>,
|
||||
popup: Element<'a, Message>,
|
||||
overlay_class: <GauntletSettingsTheme as container::Catalog>::Class<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, Message: 'a, Id> ShortcutSelector<'a, 'b, Message, Id>
|
||||
where
|
||||
Id: Clone,
|
||||
{
|
||||
pub fn new<F>(
|
||||
shortcut_id: Id,
|
||||
current_shortcut: &'b ShortcutData,
|
||||
on_shortcut_captured: F,
|
||||
on_capturing_change: F2,
|
||||
overlay_class: <GauntletSettingsTheme as container::Catalog>::Class<'a>,
|
||||
) -> Self
|
||||
where
|
||||
F: 'a + Fn(Option<PhysicalShortcut>) -> Message,
|
||||
F2: 'a + Fn(bool) -> Message,
|
||||
F: 'a + Fn(Id, Option<PhysicalShortcut>) -> Message,
|
||||
{
|
||||
let mut content: Vec<Element<Message, Theme>> = vec![];
|
||||
let content = render_shortcut(¤t_shortcut.shortcut);
|
||||
|
||||
if let Some(current_shortcut) = current_shortcut {
|
||||
let (key_name, alt_modifier_text, meta_modifier_text, control_modifier_text, shift_modifier_text) =
|
||||
shortcut_to_text(current_shortcut);
|
||||
let content = container(content)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x(Length::Fill)
|
||||
.center_y(Length::Fill)
|
||||
.into();
|
||||
|
||||
if let Some(meta_modifier_text) = meta_modifier_text {
|
||||
content.push(meta_modifier_text);
|
||||
}
|
||||
let recording_text: Element<_> = text("Recording shortcut...").into();
|
||||
|
||||
if let Some(control_modifier_text) = control_modifier_text {
|
||||
content.push(control_modifier_text);
|
||||
}
|
||||
let backspace_test: Element<_> = text("Backspace - Unset Shortcut").class(TextStyle::Subtitle).into();
|
||||
|
||||
if let Some(shift_modifier_text) = shift_modifier_text {
|
||||
content.push(shift_modifier_text);
|
||||
}
|
||||
let escape_test: Element<_> = text("Escape - Stop Capturing").class(TextStyle::Subtitle).into();
|
||||
|
||||
if let Some(alt_modifier_text) = alt_modifier_text {
|
||||
content.push(alt_modifier_text);
|
||||
}
|
||||
let popup: Element<_> = column(vec![recording_text, backspace_test, escape_test])
|
||||
.align_x(Alignment::Center)
|
||||
.into();
|
||||
|
||||
content.push(key_name);
|
||||
}
|
||||
|
||||
let content: Element<_, _> = row(content).spacing(8.0).into();
|
||||
|
||||
let content = container(content).into();
|
||||
let popup = container(popup)
|
||||
.max_height(80)
|
||||
.center_x(Length::Fill)
|
||||
.center_y(Length::Fill)
|
||||
.max_width(300)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into();
|
||||
|
||||
Self {
|
||||
padding: Padding::ZERO,
|
||||
width: Length::Fill,
|
||||
height: Length::Fill,
|
||||
max_width: f32::INFINITY,
|
||||
max_height: f32::INFINITY,
|
||||
horizontal_alignment: alignment::Horizontal::Center,
|
||||
vertical_alignment: alignment::Vertical::Center,
|
||||
|
||||
on_shortcut_captured: Box::new(on_shortcut_captured),
|
||||
on_capturing_change: Box::new(on_capturing_change),
|
||||
|
||||
shortcut_id,
|
||||
current_shortcut,
|
||||
content,
|
||||
popup,
|
||||
|
||||
overlay_class,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,36 +141,26 @@ struct State {
|
|||
is_capturing: bool,
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme> Widget<Message, Theme, Renderer> for ShortcutSelector<'a, Message, Theme>
|
||||
impl<'a, 'b, Message: 'a, Id> Widget<Message, GauntletSettingsTheme, Renderer> for ShortcutSelector<'a, 'b, Message, Id>
|
||||
where
|
||||
Theme: Catalog + text::Catalog + container::Catalog,
|
||||
Id: Clone,
|
||||
{
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
width: Length::Fill,
|
||||
height: Length::Fill,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
|
||||
layout(
|
||||
limits,
|
||||
self.width,
|
||||
self.height,
|
||||
self.max_width,
|
||||
self.max_height,
|
||||
self.padding,
|
||||
self.horizontal_alignment,
|
||||
self.vertical_alignment,
|
||||
|limits| self.content.as_widget().layout(tree, renderer, limits),
|
||||
)
|
||||
self.content.as_widget().layout(&mut tree.children[0], renderer, limits)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Theme,
|
||||
theme: &GauntletSettingsTheme,
|
||||
renderer_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor: mouse::Cursor,
|
||||
|
|
@ -153,18 +174,16 @@ where
|
|||
Status::Active
|
||||
};
|
||||
|
||||
let style = Catalog::style(theme, &<Theme as Catalog>::default(), style);
|
||||
let style = Catalog::style(theme, &<GauntletSettingsTheme as Catalog>::default(), style);
|
||||
|
||||
draw_background(renderer, &style, layout.bounds());
|
||||
|
||||
self.content.as_widget().draw(
|
||||
tree,
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
&renderer::Style {
|
||||
text_color: renderer_style.text_color,
|
||||
},
|
||||
layout.children().next().unwrap(),
|
||||
renderer_style,
|
||||
layout,
|
||||
cursor,
|
||||
viewport,
|
||||
);
|
||||
|
|
@ -179,11 +198,11 @@ where
|
|||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
self.content.as_widget().children()
|
||||
vec![Tree::new(&self.content), Tree::new(&self.popup)]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
self.content.as_widget().diff(tree);
|
||||
tree.diff_children(&[self.content.as_widget(), self.popup.as_widget()]);
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
@ -211,21 +230,18 @@ where
|
|||
match physical_key {
|
||||
Physical::Code(code) => {
|
||||
match code {
|
||||
keyboard::key::Code::Backspace => {
|
||||
keyboard::key::Code::Backspace if modifiers.is_empty() => {
|
||||
state.is_capturing = false;
|
||||
|
||||
let message = (self.on_capturing_change)(false);
|
||||
shell.publish(message);
|
||||
|
||||
let message = (self.on_shortcut_captured)(None);
|
||||
let message = (self.on_shortcut_captured)(self.shortcut_id.clone(), None);
|
||||
shell.publish(message);
|
||||
|
||||
event::Status::Ignored
|
||||
}
|
||||
keyboard::key::Code::Escape => {
|
||||
keyboard::key::Code::Escape if modifiers.is_empty() => {
|
||||
state.is_capturing = false;
|
||||
|
||||
let message = (self.on_capturing_change)(false);
|
||||
let message = (self.on_shortcut_captured)(self.shortcut_id.clone(), None);
|
||||
shell.publish(message);
|
||||
|
||||
event::Status::Ignored
|
||||
|
|
@ -236,10 +252,10 @@ where
|
|||
Some(shortcut) => {
|
||||
state.is_capturing = false;
|
||||
|
||||
let message = (self.on_capturing_change)(false);
|
||||
shell.publish(message);
|
||||
|
||||
let message = (self.on_shortcut_captured)(Some(shortcut));
|
||||
let message = (self.on_shortcut_captured)(
|
||||
self.shortcut_id.clone(),
|
||||
Some(shortcut),
|
||||
);
|
||||
shell.publish(message);
|
||||
|
||||
event::Status::Captured
|
||||
|
|
@ -263,16 +279,10 @@ where
|
|||
if cursor.is_over(layout.bounds()) {
|
||||
state.is_capturing = true;
|
||||
|
||||
let message = (self.on_capturing_change)(true);
|
||||
shell.publish(message);
|
||||
|
||||
event::Status::Captured
|
||||
} else {
|
||||
state.is_capturing = false;
|
||||
|
||||
let message = (self.on_capturing_change)(false);
|
||||
shell.publish(message);
|
||||
|
||||
event::Status::Ignored
|
||||
}
|
||||
}
|
||||
|
|
@ -297,15 +307,40 @@ where
|
|||
mouse::Interaction::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme> From<ShortcutSelector<'a, Message, Theme>> for Element<'a, Message, Theme>
|
||||
where
|
||||
Message: 'a,
|
||||
Theme: Catalog + text::Catalog + container::Catalog + 'a,
|
||||
{
|
||||
fn from(shortcut_selector: ShortcutSelector<'a, Message, Theme>) -> Self {
|
||||
Self::new(shortcut_selector)
|
||||
fn overlay<'c>(
|
||||
&'c mut self,
|
||||
tree: &'c mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
translation: Vector,
|
||||
) -> Option<overlay::Element<'c, Message, GauntletSettingsTheme, Renderer>> {
|
||||
let state = tree.state.downcast_ref::<State>();
|
||||
|
||||
let mut children = tree.children.iter_mut();
|
||||
|
||||
let content = self
|
||||
.content
|
||||
.as_widget_mut()
|
||||
.overlay(children.next().unwrap(), layout, renderer, translation);
|
||||
|
||||
let popup = if state.is_capturing {
|
||||
Some(overlay::Element::new(Box::new(Overlay {
|
||||
position: layout.position() + translation,
|
||||
popup: &self.popup,
|
||||
state: children.next().unwrap(),
|
||||
content_bounds: layout.bounds(),
|
||||
class: &self.overlay_class,
|
||||
})))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if content.is_some() || popup.is_some() {
|
||||
Some(overlay::Group::with_children(content.into_iter().chain(popup).collect()).overlay())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,3 +357,121 @@ pub trait Catalog {
|
|||
|
||||
fn style(&self, class: &Self::Class<'_>, status: Status) -> container::Style;
|
||||
}
|
||||
|
||||
pub fn render_shortcut<'a, Message: 'a>(shortcut: &Option<PhysicalShortcut>) -> Element<'a, Message> {
|
||||
let mut content: Vec<Element<Message>> = vec![];
|
||||
|
||||
if let Some(current_shortcut) = shortcut {
|
||||
let (key_name, alt_modifier_text, meta_modifier_text, control_modifier_text, shift_modifier_text) =
|
||||
shortcut_to_text(current_shortcut);
|
||||
|
||||
if let Some(meta_modifier_text) = meta_modifier_text {
|
||||
content.push(meta_modifier_text);
|
||||
}
|
||||
|
||||
if let Some(control_modifier_text) = control_modifier_text {
|
||||
content.push(control_modifier_text);
|
||||
}
|
||||
|
||||
if let Some(shift_modifier_text) = shift_modifier_text {
|
||||
content.push(shift_modifier_text);
|
||||
}
|
||||
|
||||
if let Some(alt_modifier_text) = alt_modifier_text {
|
||||
content.push(alt_modifier_text);
|
||||
}
|
||||
|
||||
content.push(key_name);
|
||||
}
|
||||
|
||||
let content: Element<Message> = row(content).spacing(8.0).into();
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
struct Overlay<'a, 'b, Message> {
|
||||
position: Point,
|
||||
popup: &'b Element<'a, Message>,
|
||||
state: &'b mut Tree,
|
||||
content_bounds: Rectangle,
|
||||
class: &'b <GauntletSettingsTheme as container::Catalog>::Class<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, Message> overlay::Overlay<Message, GauntletSettingsTheme, Renderer> for Overlay<'a, 'b, Message> {
|
||||
fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
|
||||
let padding = 2.0;
|
||||
let gap = 10.0;
|
||||
|
||||
let viewport = Rectangle::with_size(bounds);
|
||||
|
||||
let popup_layout = self.popup.as_widget().layout(
|
||||
self.state,
|
||||
renderer,
|
||||
&layout::Limits::new(Size::ZERO, viewport.size()).shrink(Padding::new(padding)),
|
||||
);
|
||||
|
||||
let text_bounds = popup_layout.bounds();
|
||||
let x_center = self.position.x + (self.content_bounds.width - text_bounds.width) / 2.0;
|
||||
|
||||
let mut tooltip_bounds = {
|
||||
let offset = Vector::new(x_center, self.position.y + self.content_bounds.height + gap + padding);
|
||||
|
||||
Rectangle {
|
||||
x: offset.x - padding,
|
||||
y: offset.y - padding,
|
||||
width: text_bounds.width + padding * 2.0,
|
||||
height: text_bounds.height + padding * 2.0,
|
||||
}
|
||||
};
|
||||
|
||||
// snap_within_viewport
|
||||
if tooltip_bounds.x < viewport.x {
|
||||
tooltip_bounds.x = viewport.x;
|
||||
} else if viewport.x + viewport.width < tooltip_bounds.x + tooltip_bounds.width {
|
||||
tooltip_bounds.x = viewport.x + viewport.width - tooltip_bounds.width;
|
||||
}
|
||||
|
||||
if tooltip_bounds.y < viewport.y {
|
||||
tooltip_bounds.y = viewport.y;
|
||||
} else if viewport.y + viewport.height < tooltip_bounds.y + tooltip_bounds.height {
|
||||
tooltip_bounds.y = viewport.y + viewport.height - tooltip_bounds.height;
|
||||
}
|
||||
|
||||
layout::Node::with_children(
|
||||
tooltip_bounds.size(),
|
||||
vec![popup_layout.translate(Vector::new(padding, padding))],
|
||||
)
|
||||
.translate(Vector::new(tooltip_bounds.x, tooltip_bounds.y))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &GauntletSettingsTheme,
|
||||
inherited_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: mouse::Cursor,
|
||||
) {
|
||||
let style = <GauntletSettingsTheme as container::Catalog>::style(theme, self.class);
|
||||
|
||||
draw_background(renderer, &style, layout.bounds());
|
||||
|
||||
let defaults = renderer::Style {
|
||||
text_color: style.text_color.unwrap_or(inherited_style.text_color),
|
||||
};
|
||||
|
||||
self.popup.as_widget().draw(
|
||||
self.state,
|
||||
renderer,
|
||||
theme,
|
||||
&defaults,
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
&Rectangle::with_size(Size::INFINITY),
|
||||
);
|
||||
}
|
||||
|
||||
fn is_over(&self, _layout: Layout<'_>, _renderer: &Renderer, _cursor_position: Point) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ struct ManagementAppModel {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum ManagementAppMsg {
|
||||
pub enum ManagementAppMsg {
|
||||
FontLoaded(Result<(), font::Error>),
|
||||
General(ManagementAppGeneralMsgIn),
|
||||
Plugin(ManagementAppPluginMsgIn),
|
||||
|
|
@ -191,34 +191,16 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task<Man
|
|||
ManagementAppMsg::Plugin(message) => {
|
||||
state.plugins_state.update(message).map(|msg| {
|
||||
match msg {
|
||||
ManagementAppPluginMsgOut::PluginsReloaded(plugins) => {
|
||||
ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::PluginsFetched(plugins))
|
||||
}
|
||||
ManagementAppPluginMsgOut::Noop => ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::Noop),
|
||||
ManagementAppPluginMsgOut::DownloadPlugin { plugin_id } => {
|
||||
ManagementAppMsg::DownloadPlugin { plugin_id }
|
||||
}
|
||||
ManagementAppPluginMsgOut::SelectedItem(selected_item) => {
|
||||
ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::SelectItem(selected_item))
|
||||
}
|
||||
ManagementAppPluginMsgOut::HandleBackendError(err) => ManagementAppMsg::HandleBackendError(err),
|
||||
ManagementAppPluginMsgOut::Inner(msg) => ManagementAppMsg::Plugin(msg),
|
||||
ManagementAppPluginMsgOut::Outer(msg) => msg,
|
||||
}
|
||||
})
|
||||
}
|
||||
ManagementAppMsg::General(message) => {
|
||||
state.general_state.update(message).map(|msg| {
|
||||
match msg {
|
||||
ManagementAppGeneralMsgOut::Noop => ManagementAppMsg::General(ManagementAppGeneralMsgIn::Noop),
|
||||
ManagementAppGeneralMsgOut::HandleBackendError(err) => ManagementAppMsg::HandleBackendError(err),
|
||||
ManagementAppGeneralMsgOut::SetGlobalShortcutResponse {
|
||||
shortcut,
|
||||
shortcut_error,
|
||||
} => {
|
||||
ManagementAppMsg::General(ManagementAppGeneralMsgIn::SetGlobalShortcutResponse {
|
||||
shortcut,
|
||||
shortcut_error,
|
||||
})
|
||||
}
|
||||
ManagementAppGeneralMsgOut::Inner(msg) => ManagementAppMsg::General(msg),
|
||||
ManagementAppGeneralMsgOut::Outer(msg) => msg,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -266,7 +248,7 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task<Man
|
|||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |plugins| {
|
||||
ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::PluginsFetched(plugins))
|
||||
ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::PluginsReloaded(plugins))
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,27 +22,28 @@ use iced::Task;
|
|||
use iced_fonts::Bootstrap;
|
||||
use iced_fonts::BOOTSTRAP_FONT;
|
||||
|
||||
use crate::components::shortcut_selector::ShortcutSelector;
|
||||
use crate::components::shortcut_selector::shortcut_selector;
|
||||
use crate::components::shortcut_selector::ShortcutData;
|
||||
use crate::components::shortcut_selector::ShortcutId;
|
||||
use crate::theme::container::ContainerStyle;
|
||||
use crate::theme::text::TextStyle;
|
||||
use crate::theme::Element;
|
||||
use crate::ui::ManagementAppMsg;
|
||||
|
||||
pub struct ManagementAppGeneralState {
|
||||
backend_api: Option<BackendApi>,
|
||||
theme: SettingsTheme,
|
||||
window_position_mode: WindowPositionMode,
|
||||
current_shortcut: Option<PhysicalShortcut>,
|
||||
current_shortcut_error: Option<String>,
|
||||
currently_capturing: bool,
|
||||
current_shortcut: ShortcutData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ManagementAppGeneralMsgIn {
|
||||
ShortcutCaptured(Option<PhysicalShortcut>),
|
||||
CapturingChanged(bool),
|
||||
ShortcutCaptured(ShortcutId, Option<PhysicalShortcut>),
|
||||
ThemeChanged(SettingsTheme),
|
||||
WindowPositionModeChanged(WindowPositionMode),
|
||||
SetGlobalShortcutResponse {
|
||||
HandleShortcutResponse {
|
||||
id: ShortcutId,
|
||||
shortcut: Option<PhysicalShortcut>,
|
||||
shortcut_error: Option<String>,
|
||||
},
|
||||
|
|
@ -57,12 +58,8 @@ pub enum ManagementAppGeneralMsgIn {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ManagementAppGeneralMsgOut {
|
||||
Noop,
|
||||
SetGlobalShortcutResponse {
|
||||
shortcut: Option<PhysicalShortcut>,
|
||||
shortcut_error: Option<String>,
|
||||
},
|
||||
HandleBackendError(BackendApiError),
|
||||
Inner(ManagementAppGeneralMsgIn),
|
||||
Outer(ManagementAppMsg),
|
||||
}
|
||||
|
||||
impl ManagementAppGeneralState {
|
||||
|
|
@ -71,9 +68,10 @@ impl ManagementAppGeneralState {
|
|||
backend_api,
|
||||
theme: SettingsTheme::AutoDetect,
|
||||
window_position_mode: WindowPositionMode::Static,
|
||||
current_shortcut: None,
|
||||
current_shortcut_error: None,
|
||||
currently_capturing: false,
|
||||
current_shortcut: ShortcutData {
|
||||
shortcut: None,
|
||||
error: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +82,7 @@ impl ManagementAppGeneralState {
|
|||
};
|
||||
|
||||
match message {
|
||||
ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut) => {
|
||||
ManagementAppGeneralMsgIn::ShortcutCaptured(id, shortcut) => {
|
||||
let mut backend_api = backend_api.clone();
|
||||
|
||||
Task::perform(
|
||||
|
|
@ -99,12 +97,14 @@ impl ManagementAppGeneralState {
|
|||
},
|
||||
move |result| {
|
||||
let shortcut = shortcut.clone();
|
||||
let id = id.clone();
|
||||
|
||||
handle_backend_error(result, move |shortcut_error| {
|
||||
ManagementAppGeneralMsgOut::SetGlobalShortcutResponse {
|
||||
ManagementAppGeneralMsgOut::Inner(ManagementAppGeneralMsgIn::HandleShortcutResponse {
|
||||
id,
|
||||
shortcut,
|
||||
shortcut_error,
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
@ -118,13 +118,10 @@ impl ManagementAppGeneralState {
|
|||
} => {
|
||||
self.theme = theme;
|
||||
self.window_position_mode = window_position_mode;
|
||||
self.current_shortcut = shortcut;
|
||||
self.current_shortcut_error = shortcut_error;
|
||||
|
||||
Task::done(ManagementAppGeneralMsgOut::Noop)
|
||||
}
|
||||
ManagementAppGeneralMsgIn::CapturingChanged(capturing) => {
|
||||
self.currently_capturing = capturing;
|
||||
self.current_shortcut = ShortcutData {
|
||||
shortcut,
|
||||
error: shortcut_error,
|
||||
};
|
||||
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -139,7 +136,9 @@ impl ManagementAppGeneralState {
|
|||
|
||||
Ok(())
|
||||
},
|
||||
|result| handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Noop),
|
||||
|result| {
|
||||
handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop))
|
||||
},
|
||||
)
|
||||
}
|
||||
ManagementAppGeneralMsgIn::WindowPositionModeChanged(mode) => {
|
||||
|
|
@ -153,15 +152,20 @@ impl ManagementAppGeneralState {
|
|||
|
||||
Ok(())
|
||||
},
|
||||
|result| handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Noop),
|
||||
|result| {
|
||||
handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop))
|
||||
},
|
||||
)
|
||||
}
|
||||
ManagementAppGeneralMsgIn::SetGlobalShortcutResponse {
|
||||
ManagementAppGeneralMsgIn::HandleShortcutResponse {
|
||||
id,
|
||||
shortcut,
|
||||
shortcut_error,
|
||||
} => {
|
||||
self.current_shortcut = shortcut;
|
||||
self.current_shortcut_error = shortcut_error;
|
||||
self.current_shortcut = ShortcutData {
|
||||
shortcut,
|
||||
error: shortcut_error,
|
||||
};
|
||||
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -169,12 +173,12 @@ impl ManagementAppGeneralState {
|
|||
}
|
||||
|
||||
pub fn view(&self) -> Element<ManagementAppGeneralMsgIn> {
|
||||
let global_shortcut_selector: Element<_> = ShortcutSelector::new(
|
||||
let global_shortcut_selector = shortcut_selector(
|
||||
ShortcutId::Global,
|
||||
&self.current_shortcut,
|
||||
move |value| ManagementAppGeneralMsgIn::ShortcutCaptured(value),
|
||||
move |value| ManagementAppGeneralMsgIn::CapturingChanged(value),
|
||||
)
|
||||
.into();
|
||||
move |id, shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(id, shortcut),
|
||||
ContainerStyle::Box,
|
||||
);
|
||||
|
||||
let global_shortcut_field: Element<_> = container(global_shortcut_selector)
|
||||
.width(Length::Fill)
|
||||
|
|
@ -200,8 +204,6 @@ impl ManagementAppGeneralState {
|
|||
|
||||
let content: Element<_> = container(content).width(Length::Fill).into();
|
||||
|
||||
let content: Element<_> = container(content).width(Length::Fill).into();
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
|
|
@ -290,59 +292,41 @@ impl ManagementAppGeneralState {
|
|||
}
|
||||
|
||||
fn shortcut_capture_after(&self) -> Element<ManagementAppGeneralMsgIn> {
|
||||
if self.currently_capturing {
|
||||
let hint1: Element<_> = text("Backspace - Unset Shortcut")
|
||||
.width(Length::Fill)
|
||||
.class(TextStyle::Subtitle)
|
||||
if let Some(current_shortcut_error) = &self.current_shortcut.error {
|
||||
let error_icon: Element<_> = value(Bootstrap::ExclamationTriangleFill)
|
||||
.font(BOOTSTRAP_FONT)
|
||||
.class(TextStyle::Destructive)
|
||||
.into();
|
||||
|
||||
let hint2: Element<_> = text("Escape - Stop Capturing")
|
||||
.width(Length::Fill)
|
||||
.class(TextStyle::Subtitle)
|
||||
let error_text: Element<_> = text(current_shortcut_error).class(TextStyle::Destructive).into();
|
||||
|
||||
let error_text: Element<_> = container(error_text)
|
||||
.padding(16.0)
|
||||
.max_width(300)
|
||||
.class(ContainerStyle::Box)
|
||||
.into();
|
||||
|
||||
column(vec![hint1, hint2])
|
||||
let tooltip: Element<_> = tooltip(error_icon, error_text, Position::Bottom).into();
|
||||
|
||||
let content = container(tooltip)
|
||||
.width(Length::FillPortion(3))
|
||||
.align_x(Alignment::Center)
|
||||
.align_y(alignment::Vertical::Center)
|
||||
.padding(Padding::from([0.0, 8.0]))
|
||||
.into()
|
||||
.into();
|
||||
|
||||
content
|
||||
} else {
|
||||
if let Some(current_shortcut_error) = &self.current_shortcut_error {
|
||||
let error_icon: Element<_> = value(Bootstrap::ExclamationTriangleFill)
|
||||
.font(BOOTSTRAP_FONT)
|
||||
.class(TextStyle::Destructive)
|
||||
.into();
|
||||
|
||||
let error_text: Element<_> = text(current_shortcut_error).class(TextStyle::Destructive).into();
|
||||
|
||||
let error_text: Element<_> = container(error_text)
|
||||
.padding(16.0)
|
||||
.max_width(300)
|
||||
.class(ContainerStyle::Box)
|
||||
.into();
|
||||
|
||||
let tooltip: Element<_> = tooltip(error_icon, error_text, Position::Bottom).into();
|
||||
|
||||
let content = container(tooltip)
|
||||
.width(Length::FillPortion(3))
|
||||
.align_y(alignment::Vertical::Center)
|
||||
.padding(Padding::from([0.0, 8.0]))
|
||||
.into();
|
||||
|
||||
content
|
||||
} else {
|
||||
Space::with_width(Length::FillPortion(3)).into()
|
||||
}
|
||||
Space::with_width(Length::FillPortion(3)).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_backend_error<T>(
|
||||
fn handle_backend_error<T>(
|
||||
result: Result<T, BackendApiError>,
|
||||
convert: impl FnOnce(T) -> ManagementAppGeneralMsgOut,
|
||||
) -> ManagementAppGeneralMsgOut {
|
||||
match result {
|
||||
Ok(val) => convert(val),
|
||||
Err(err) => ManagementAppGeneralMsgOut::HandleBackendError(err),
|
||||
Err(err) => ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ use iced_fonts::BOOTSTRAP_FONT;
|
|||
use crate::theme::button::ButtonStyle;
|
||||
use crate::theme::text::TextStyle;
|
||||
use crate::theme::Element;
|
||||
use crate::ui::ManagementAppMsg;
|
||||
use crate::views::plugins::preferences::preferences_ui;
|
||||
use crate::views::plugins::preferences::PluginPreferencesMsg;
|
||||
use crate::views::plugins::preferences::SelectItem;
|
||||
use crate::views::plugins::table::PluginTableMsgIn;
|
||||
use crate::views::plugins::table::PluginTableMsgOut;
|
||||
use crate::views::plugins::table::PluginTableState;
|
||||
use crate::views::plugins::table::PluginTableUpdateResult;
|
||||
|
||||
mod preferences;
|
||||
mod table;
|
||||
|
|
@ -48,19 +48,17 @@ pub enum ManagementAppPluginMsgIn {
|
|||
PluginTableMsg(PluginTableMsgIn),
|
||||
PluginPreferenceMsg(PluginPreferencesMsg),
|
||||
FetchPlugins,
|
||||
PluginsFetched(HashMap<PluginId, SettingsPlugin>),
|
||||
PluginsReloaded(HashMap<PluginId, SettingsPlugin>),
|
||||
RemovePlugin { plugin_id: PluginId },
|
||||
ToggleShowEntrypoint { plugin_id: PluginId },
|
||||
DownloadPlugin { plugin_id: PluginId },
|
||||
SelectItem(SelectedItem),
|
||||
Noop,
|
||||
}
|
||||
|
||||
pub enum ManagementAppPluginMsgOut {
|
||||
PluginsReloaded(HashMap<PluginId, SettingsPlugin>),
|
||||
SelectedItem(SelectedItem),
|
||||
DownloadPlugin { plugin_id: PluginId },
|
||||
HandleBackendError(BackendApiError),
|
||||
Noop,
|
||||
Inner(ManagementAppPluginMsgIn),
|
||||
Outer(ManagementAppMsg),
|
||||
}
|
||||
|
||||
pub struct ManagementAppPluginsState {
|
||||
|
|
@ -99,7 +97,7 @@ impl ManagementAppPluginsState {
|
|||
tracing::debug!("Opening selected item: {:?}", select_item);
|
||||
|
||||
Self {
|
||||
backend_api,
|
||||
backend_api: backend_api.clone(),
|
||||
plugin_data: Rc::new(RefCell::new(PluginDataContainer::new())),
|
||||
preference_user_data: HashMap::new(),
|
||||
selected_item: select_item,
|
||||
|
|
@ -115,71 +113,77 @@ impl ManagementAppPluginsState {
|
|||
|
||||
match message {
|
||||
ManagementAppPluginMsgIn::PluginTableMsg(message) => {
|
||||
match self.table_state.update(message) {
|
||||
PluginTableUpdateResult::Command(command) => command.map(|_| ManagementAppPluginMsgOut::Noop),
|
||||
PluginTableUpdateResult::Value(msg) => {
|
||||
match msg {
|
||||
PluginTableMsgOut::SetPluginState { enabled, plugin_id } => {
|
||||
let mut backend_client = backend_api.clone();
|
||||
self.table_state.update(message).then(move |msg| {
|
||||
match msg {
|
||||
PluginTableMsgOut::SetPluginState { enabled, plugin_id } => {
|
||||
let mut backend_client = backend_api.clone();
|
||||
|
||||
Task::perform(
|
||||
async move {
|
||||
backend_client.set_plugin_state(plugin_id, enabled).await?;
|
||||
Task::perform(
|
||||
async move {
|
||||
backend_client.set_plugin_state(plugin_id, enabled).await?;
|
||||
|
||||
let plugins = backend_client.plugins().await?;
|
||||
let plugins = backend_client.plugins().await?;
|
||||
|
||||
Ok(plugins)
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |plugins| {
|
||||
ManagementAppPluginMsgOut::PluginsReloaded(plugins)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
PluginTableMsgOut::SetEntrypointState {
|
||||
enabled,
|
||||
plugin_id,
|
||||
entrypoint_id,
|
||||
} => {
|
||||
let mut backend_client = backend_api.clone();
|
||||
Ok(plugins)
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |plugins| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
))
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
PluginTableMsgOut::SetEntrypointState {
|
||||
enabled,
|
||||
plugin_id,
|
||||
entrypoint_id,
|
||||
} => {
|
||||
let mut backend_client = backend_api.clone();
|
||||
|
||||
Task::perform(
|
||||
async move {
|
||||
backend_client
|
||||
.set_entrypoint_state(plugin_id, entrypoint_id, enabled)
|
||||
.await?;
|
||||
Task::perform(
|
||||
async move {
|
||||
backend_client
|
||||
.set_entrypoint_state(plugin_id, entrypoint_id, enabled)
|
||||
.await?;
|
||||
|
||||
let plugins = backend_client.plugins().await?;
|
||||
let plugins = backend_client.plugins().await?;
|
||||
|
||||
Ok(plugins)
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |plugins| {
|
||||
ManagementAppPluginMsgOut::PluginsReloaded(plugins)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
PluginTableMsgOut::SelectItem(selected_item) => {
|
||||
Task::done(ManagementAppPluginMsgOut::SelectedItem(selected_item))
|
||||
}
|
||||
PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => {
|
||||
let plugins = {
|
||||
let mut plugin_data = self.plugin_data.borrow_mut();
|
||||
let settings_plugin_data = plugin_data.plugins_state.get_mut(&plugin_id).unwrap();
|
||||
settings_plugin_data.show_entrypoints = !settings_plugin_data.show_entrypoints;
|
||||
|
||||
plugin_data.plugins.clone()
|
||||
};
|
||||
|
||||
self.apply_plugin_fetch(plugins);
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Ok(plugins)
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |plugins| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(
|
||||
plugins,
|
||||
))
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
PluginTableMsgOut::SelectItem(selected_item) => {
|
||||
Task::done(ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::SelectItem(
|
||||
selected_item,
|
||||
)))
|
||||
}
|
||||
PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => {
|
||||
Task::done(ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::Noop))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
ManagementAppPluginMsgIn::ToggleShowEntrypoint { plugin_id } => {
|
||||
let plugins = {
|
||||
let mut plugin_data = self.plugin_data.borrow_mut();
|
||||
let settings_plugin_data = plugin_data.plugins_state.get_mut(&plugin_id).unwrap();
|
||||
settings_plugin_data.show_entrypoints = !settings_plugin_data.show_entrypoints;
|
||||
|
||||
plugin_data.plugins.clone()
|
||||
};
|
||||
|
||||
self.apply_plugin_fetch(plugins);
|
||||
|
||||
Task::none()
|
||||
}
|
||||
ManagementAppPluginMsgIn::PluginPreferenceMsg(msg) => {
|
||||
match msg {
|
||||
|
|
@ -204,7 +208,11 @@ impl ManagementAppPluginsState {
|
|||
|
||||
Ok(())
|
||||
},
|
||||
|result| handle_backend_error(result, |()| ManagementAppPluginMsgOut::Noop),
|
||||
|result| {
|
||||
handle_backend_error(result, |()| {
|
||||
ManagementAppPluginMsgOut::Outer(ManagementAppMsg::Noop)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -219,11 +227,13 @@ impl ManagementAppPluginsState {
|
|||
Ok(plugins)
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |plugins| ManagementAppPluginMsgOut::PluginsReloaded(plugins))
|
||||
handle_backend_error(result, |plugins| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(plugins))
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
ManagementAppPluginMsgIn::PluginsFetched(plugins) => {
|
||||
ManagementAppPluginMsgIn::PluginsReloaded(plugins) => {
|
||||
self.apply_plugin_fetch(plugins);
|
||||
|
||||
Task::none()
|
||||
|
|
@ -242,12 +252,16 @@ impl ManagementAppPluginsState {
|
|||
Ok(plugins)
|
||||
},
|
||||
|result| {
|
||||
handle_backend_error(result, |plugins| ManagementAppPluginMsgOut::PluginsReloaded(plugins))
|
||||
handle_backend_error(result, |plugins| {
|
||||
ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded(plugins))
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
ManagementAppPluginMsgIn::DownloadPlugin { plugin_id } => {
|
||||
Task::done(ManagementAppPluginMsgOut::DownloadPlugin { plugin_id })
|
||||
Task::done(ManagementAppPluginMsgOut::Outer(ManagementAppMsg::DownloadPlugin {
|
||||
plugin_id,
|
||||
}))
|
||||
}
|
||||
ManagementAppPluginMsgIn::SelectItem(selected_item) => {
|
||||
self.selected_item = selected_item;
|
||||
|
|
@ -696,6 +710,6 @@ pub fn handle_backend_error<T>(
|
|||
) -> ManagementAppPluginMsgOut {
|
||||
match result {
|
||||
Ok(val) => convert(val),
|
||||
Err(err) => ManagementAppPluginMsgOut::HandleBackendError(err),
|
||||
Err(err) => ManagementAppPluginMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,11 +62,6 @@ pub struct PluginTableState {
|
|||
body: Id,
|
||||
}
|
||||
|
||||
pub enum PluginTableUpdateResult {
|
||||
Command(Task<()>),
|
||||
Value(PluginTableMsgOut),
|
||||
}
|
||||
|
||||
impl PluginTableState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
|
@ -82,22 +77,20 @@ impl PluginTableState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: PluginTableMsgIn) -> PluginTableUpdateResult {
|
||||
pub fn update(&mut self, message: PluginTableMsgIn) -> Task<PluginTableMsgOut> {
|
||||
match message {
|
||||
PluginTableMsgIn::TableSyncHeader(offset) => {
|
||||
PluginTableUpdateResult::Command(scrollable::scroll_to(self.header.clone(), offset))
|
||||
}
|
||||
PluginTableMsgIn::TableSyncHeader(offset) => scrollable::scroll_to(self.header.clone(), offset),
|
||||
PluginTableMsgIn::EnabledToggleItem(item) => {
|
||||
match item {
|
||||
EnabledItem::Plugin { enabled, plugin_id } => {
|
||||
PluginTableUpdateResult::Value(PluginTableMsgOut::SetPluginState { enabled, plugin_id })
|
||||
Task::done(PluginTableMsgOut::SetPluginState { enabled, plugin_id })
|
||||
}
|
||||
EnabledItem::Entrypoint {
|
||||
enabled,
|
||||
plugin_id,
|
||||
entrypoint_id,
|
||||
} => {
|
||||
PluginTableUpdateResult::Value(PluginTableMsgOut::SetEntrypointState {
|
||||
Task::done(PluginTableMsgOut::SetEntrypointState {
|
||||
enabled,
|
||||
plugin_id,
|
||||
entrypoint_id,
|
||||
|
|
@ -105,9 +98,9 @@ impl PluginTableState {
|
|||
}
|
||||
}
|
||||
}
|
||||
PluginTableMsgIn::SelectItem(item) => PluginTableUpdateResult::Value(PluginTableMsgOut::SelectItem(item)),
|
||||
PluginTableMsgIn::SelectItem(item) => Task::done(PluginTableMsgOut::SelectItem(item)),
|
||||
PluginTableMsgIn::ToggleShowEntrypoints { plugin_id } => {
|
||||
PluginTableUpdateResult::Value(PluginTableMsgOut::ToggleShowEntrypoints { plugin_id })
|
||||
Task::done(PluginTableMsgOut::ToggleShowEntrypoints { plugin_id })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue