Add complete keyboard navigation.
This commit is contained in:
@@ -238,6 +238,7 @@ pub enum Message {
|
||||
// Settings
|
||||
SetOfflineMode(bool),
|
||||
SetUiLocale(UiLocale),
|
||||
KeyboardNavigation(crate::components::keyboard::Navigation),
|
||||
ToggleLocaleDropdown,
|
||||
ToggleProjectDropdown,
|
||||
|
||||
@@ -1457,6 +1458,10 @@ impl BdsApp {
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match message {
|
||||
Message::KeyboardNavigation(navigation) => match navigation {
|
||||
crate::components::keyboard::Navigation::Next => iced::widget::focus_next(),
|
||||
crate::components::keyboard::Navigation::Previous => iced::widget::focus_previous(),
|
||||
},
|
||||
Message::WindowCloseRequested => {
|
||||
self.persist_project_ui_state();
|
||||
flush_embeddings_and_exit(std::process::exit)
|
||||
@@ -3869,7 +3874,11 @@ impl BdsApp {
|
||||
) && (action != MenuAction::DisconnectServer || self.remote_client.is_some())
|
||||
});
|
||||
|
||||
native_edit::native_edit(content, Arc::clone(&self.native_edit_commands)).into()
|
||||
crate::components::keyboard::scope(native_edit::native_edit(
|
||||
content,
|
||||
Arc::clone(&self.native_edit_commands),
|
||||
))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn subscription(&self) -> Subscription<Message> {
|
||||
@@ -3894,6 +3903,15 @@ impl BdsApp {
|
||||
_ => None,
|
||||
});
|
||||
let window_close_sub = window::close_requests().map(|_| Message::WindowCloseRequested);
|
||||
let keyboard_navigation_sub = iced::event::listen_with(|event, status, _id| {
|
||||
let iced::Event::Keyboard(iced::keyboard::Event::KeyPressed { key, modifiers, .. }) =
|
||||
event
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
crate::components::keyboard::tab_navigation(&key, modifiers, status)
|
||||
.map(Message::KeyboardNavigation)
|
||||
});
|
||||
|
||||
// Global mouse tracking for sidebar resize dragging.
|
||||
// The 4px drag handle mouse_area only fires on_press; move/release
|
||||
@@ -3950,6 +3968,7 @@ impl BdsApp {
|
||||
toast_tick,
|
||||
file_drop_sub,
|
||||
window_close_sub,
|
||||
keyboard_navigation_sub,
|
||||
drag_sub,
|
||||
menu_interaction_sub,
|
||||
menu_expand_tick,
|
||||
|
||||
@@ -5,6 +5,8 @@ use iced::widget::{
|
||||
};
|
||||
use iced::{Alignment, Background, Border, Color, Element, Length, Shadow, Theme, Vector};
|
||||
|
||||
use super::keyboard;
|
||||
|
||||
/// Standard form field label color.
|
||||
pub const LABEL_COLOR: Color = rgb8(0xB5, 0xBA, 0xC4);
|
||||
pub const SECTION_COLOR: Color = rgb8(0x9D, 0xA5, 0xB4);
|
||||
@@ -243,10 +245,13 @@ where
|
||||
.size(12)
|
||||
.color(LABEL_COLOR)
|
||||
.shaping(Shaping::Advanced),
|
||||
pick_list(list, selected.cloned(), on_select)
|
||||
.padding([8, 10])
|
||||
.width(Length::Fill)
|
||||
.style(select_style),
|
||||
keyboard::focusable(
|
||||
pick_list(list, selected.cloned(), on_select)
|
||||
.padding([8, 10])
|
||||
.width(Length::Fill)
|
||||
.style(select_style),
|
||||
true,
|
||||
),
|
||||
]
|
||||
.spacing(6)
|
||||
.width(Length::Fill)
|
||||
@@ -259,11 +264,14 @@ pub fn labeled_checkbox<'a, Message: Clone + 'a>(
|
||||
is_checked: bool,
|
||||
on_toggle: impl Fn(bool) -> Message + 'a,
|
||||
) -> Element<'a, Message> {
|
||||
checkbox(label, is_checked)
|
||||
.on_toggle(on_toggle)
|
||||
.size(16)
|
||||
.text_size(14)
|
||||
.into()
|
||||
keyboard::focusable(
|
||||
checkbox(label, is_checked)
|
||||
.on_toggle(on_toggle)
|
||||
.size(16)
|
||||
.text_size(14),
|
||||
true,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// A section header with optional separator line.
|
||||
|
||||
1329
crates/bds-ui/src/components/keyboard.rs
Normal file
1329
crates/bds-ui/src/components/keyboard.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
pub mod inputs;
|
||||
pub mod keyboard;
|
||||
pub mod native_edit;
|
||||
pub mod popover;
|
||||
pub mod webview;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use iced::widget::{
|
||||
Column, button, column, container, horizontal_space, mouse_area, row, scrollable, text,
|
||||
Column, column, container, horizontal_space, mouse_area, row, scrollable, text,
|
||||
};
|
||||
use iced::{
|
||||
Alignment, Background, Border, Color, Element, Length, Shadow, Subscription, Theme, Vector,
|
||||
@@ -10,7 +10,7 @@ use muda::accelerator::{Accelerator, CMD_OR_CTRL, Code, Modifiers};
|
||||
use muda::{Menu, MenuEvent, MenuId, MenuItem, PredefinedMenuItem, Submenu};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::components::{inputs, popover};
|
||||
use crate::components::{inputs, keyboard, popover};
|
||||
use crate::state::tabs::TabType;
|
||||
use bds_core::i18n::{UiLocale, translate};
|
||||
|
||||
@@ -640,9 +640,12 @@ fn window_menu_popup<'a>(
|
||||
]
|
||||
.align_y(Alignment::Center)
|
||||
.spacing(16);
|
||||
let mut item = button(content).padding([6, 8]).width(Length::Fill).style(
|
||||
move |theme, status| menu_item_style(selected == Some(action), theme, status),
|
||||
);
|
||||
let mut item = keyboard::button(content)
|
||||
.padding([6, 8])
|
||||
.width(Length::Fill)
|
||||
.style(move |theme, status| {
|
||||
menu_item_style(selected == Some(action), theme, status)
|
||||
});
|
||||
if is_enabled {
|
||||
item = item.on_press(Message::WindowMenu(WindowMenuEvent::Action(action)));
|
||||
}
|
||||
@@ -692,7 +695,7 @@ pub fn window_menu_view<'a>(
|
||||
label
|
||||
};
|
||||
let trigger = mouse_area(
|
||||
button(text(label).size(12))
|
||||
keyboard::button(text(label).size(12))
|
||||
.padding([5, 8])
|
||||
.style(move |theme, status| menu_button_style(active, theme, status))
|
||||
.on_press(Message::WindowMenu(WindowMenuEvent::Toggle(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Column, Space, button, column, container, svg, text, tooltip};
|
||||
use iced::widget::{Column, Space, column, container, svg, text, tooltip};
|
||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -28,6 +30,21 @@ fn icon_svg(view: SidebarView) -> &'static [u8] {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn access_key(view: SidebarView) -> char {
|
||||
match view {
|
||||
SidebarView::Posts => '1',
|
||||
SidebarView::Pages => '2',
|
||||
SidebarView::Media => '3',
|
||||
SidebarView::Scripts => '4',
|
||||
SidebarView::Templates => '5',
|
||||
SidebarView::Tags => '6',
|
||||
SidebarView::Chat => '7',
|
||||
SidebarView::Import => '8',
|
||||
SidebarView::Git => '9',
|
||||
SidebarView::Settings => '0',
|
||||
}
|
||||
}
|
||||
|
||||
/// Top group of activity items.
|
||||
const TOP_ACTIVITIES: &[SidebarView] = &[
|
||||
SidebarView::Posts,
|
||||
@@ -108,7 +125,7 @@ pub fn view(
|
||||
.height(Length::Fixed(24.0))
|
||||
.opacity(if is_active { 1.0_f32 } else { 0.4_f32 });
|
||||
|
||||
let btn = button(
|
||||
let btn = iced::widget::button(
|
||||
container(icon)
|
||||
.center_x(Length::Fixed(48.0))
|
||||
.center_y(Length::Fixed(48.0)),
|
||||
@@ -140,13 +157,18 @@ pub fn view(
|
||||
|
||||
// Wrap in tooltip per layout.allium ActivityButton.label_key
|
||||
let tip_text = t(locale, view.i18n_key());
|
||||
tooltip(
|
||||
btn_row,
|
||||
text(tip_text).size(12).shaping(Shaping::Advanced),
|
||||
tooltip::Position::Right,
|
||||
keyboard::focusable(
|
||||
tooltip(
|
||||
btn_row,
|
||||
text(tip_text).size(12).shaping(Shaping::Advanced),
|
||||
tooltip::Position::Right,
|
||||
)
|
||||
.gap(4)
|
||||
.style(inputs::tooltip_style),
|
||||
true,
|
||||
)
|
||||
.gap(4)
|
||||
.style(inputs::tooltip_style)
|
||||
.hotkey(access_key(view))
|
||||
.hint_position(keyboard::HintPosition::BottomTrailing)
|
||||
.into()
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::components::keyboard;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::f32::consts::{FRAC_PI_2, TAU};
|
||||
|
||||
@@ -7,9 +8,7 @@ use bds_core::engine::chat_surfaces::{
|
||||
};
|
||||
use bds_core::i18n::UiLocale;
|
||||
use iced::widget::canvas::{self, Path, Stroke, path};
|
||||
use iced::widget::{
|
||||
Space, button, checkbox, column, container, row, scrollable, text, text_editor,
|
||||
};
|
||||
use iced::widget::{Space, checkbox, column, container, row, scrollable, text, text_editor};
|
||||
use iced::{
|
||||
Alignment, Color, Element, Length, Point, Radians, Rectangle, Renderer, Size, Theme, mouse,
|
||||
};
|
||||
@@ -76,7 +75,7 @@ fn surface_view<'a>(
|
||||
}
|
||||
if dismissible {
|
||||
header = header.push(
|
||||
button(text(t(locale, "chat.surface.dismiss")))
|
||||
keyboard::button(text(t(locale, "chat.surface.dismiss")))
|
||||
.on_press(Message::ChatSurfaceDismissed(surface.id.clone()))
|
||||
.padding([4, 8])
|
||||
.style(inputs::secondary_button),
|
||||
@@ -113,7 +112,7 @@ fn surface_content<'a>(
|
||||
.iter()
|
||||
.fold(row![].spacing(8), |actions, item| {
|
||||
actions.push(
|
||||
button(text(item.label.clone()))
|
||||
keyboard::button(text(item.label.clone()))
|
||||
.on_press(Message::ChatSurfaceAction {
|
||||
surface_id: surface.id.clone(),
|
||||
action: item.action.clone(),
|
||||
@@ -196,15 +195,18 @@ fn form<'a>(
|
||||
let surface_id = surface.id.clone();
|
||||
let key = field.key.clone();
|
||||
let control: Element<'a, Message> = match field.input_type {
|
||||
FormInputType::Checkbox => checkbox(label, field.value.as_bool().unwrap_or(false))
|
||||
.on_toggle(move |value| Message::ChatSurfaceFieldChanged {
|
||||
surface_id: surface_id.clone(),
|
||||
field: key.clone(),
|
||||
value: value.into(),
|
||||
})
|
||||
.size(16)
|
||||
.text_size(13)
|
||||
.into(),
|
||||
FormInputType::Checkbox => keyboard::focusable(
|
||||
checkbox(label, field.value.as_bool().unwrap_or(false))
|
||||
.on_toggle(move |value| Message::ChatSurfaceFieldChanged {
|
||||
surface_id: surface_id.clone(),
|
||||
field: key.clone(),
|
||||
value: value.into(),
|
||||
})
|
||||
.size(16)
|
||||
.text_size(13),
|
||||
true,
|
||||
)
|
||||
.into(),
|
||||
FormInputType::Select => {
|
||||
let selected = field.options.iter().find(|option| {
|
||||
field
|
||||
@@ -278,7 +280,7 @@ fn form<'a>(
|
||||
}
|
||||
if let Some(action) = &surface.submit_action {
|
||||
children.push(
|
||||
button(text(
|
||||
keyboard::button(text(
|
||||
surface
|
||||
.submit_label
|
||||
.clone()
|
||||
@@ -437,7 +439,7 @@ fn tabs<'a>(
|
||||
.enumerate()
|
||||
.fold(row![].spacing(6), |controls, (index, tab)| {
|
||||
controls.push(
|
||||
button(text(tab.label.clone()))
|
||||
keyboard::button(text(tab.label.clone()))
|
||||
.on_press(Message::ChatSurfaceTabSelected {
|
||||
surface_id: surface.id.clone(),
|
||||
index,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::components::keyboard;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -8,7 +9,7 @@ use bds_core::i18n::UiLocale;
|
||||
use bds_core::model::{ChatConversation, ChatMessage, ChatRole};
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{
|
||||
Space, button, column, container, markdown, row, scrollable, text, text_editor, text_input,
|
||||
Space, column, container, markdown, row, scrollable, text, text_editor, text_input,
|
||||
};
|
||||
use iced::{Alignment, Color, Element, Length};
|
||||
|
||||
@@ -212,7 +213,7 @@ pub fn view<'a>(
|
||||
text(t(locale, "chat.unavailable.guidance"))
|
||||
.size(13)
|
||||
.color(inputs::SECTION_COLOR),
|
||||
button(text(t(locale, "chat.unavailable.openSettings")))
|
||||
keyboard::button(text(t(locale, "chat.unavailable.openSettings")))
|
||||
.on_press(Message::OpenSettingsSection(
|
||||
crate::views::settings_view::SettingsSection::AI,
|
||||
))
|
||||
@@ -254,11 +255,11 @@ pub fn view<'a>(
|
||||
.size(18)
|
||||
.padding([7, 9])
|
||||
.style(inputs::field_style),
|
||||
button(text(t(locale, "chat.rename.action")))
|
||||
keyboard::button(text(t(locale, "chat.rename.action")))
|
||||
.on_press(Message::ChatRename)
|
||||
.padding([8, 12])
|
||||
.style(inputs::secondary_button),
|
||||
button(text(t(locale, "common.delete")))
|
||||
keyboard::button(text(t(locale, "common.delete")))
|
||||
.on_press(Message::ChatDelete(state.conversation.id.clone()))
|
||||
.padding([8, 12])
|
||||
.style(inputs::danger_button),
|
||||
@@ -349,13 +350,14 @@ pub fn view<'a>(
|
||||
} else {
|
||||
t(locale, "chat.send")
|
||||
};
|
||||
let mut send_button = button(text(send_label))
|
||||
.padding([8, 16])
|
||||
.style(if state.streaming {
|
||||
inputs::danger_button
|
||||
} else {
|
||||
inputs::primary_button
|
||||
});
|
||||
let mut send_button =
|
||||
keyboard::button(text(send_label))
|
||||
.padding([8, 16])
|
||||
.style(if state.streaming {
|
||||
inputs::danger_button
|
||||
} else {
|
||||
inputs::primary_button
|
||||
});
|
||||
if state.streaming {
|
||||
send_button = send_button.on_press(Message::ChatCancel);
|
||||
} else if !state.input.text().trim().is_empty() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use iced::widget::{Space, button, column, container, row, scrollable, text, tooltip};
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::{Space, column, container, row, scrollable, text, tooltip};
|
||||
use iced::{Alignment, Background, Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -339,7 +340,7 @@ fn recent_posts<'a>(posts: &'a [DashboardRecentPost], locale: UiLocale) -> Eleme
|
||||
.map(|post| {
|
||||
container(
|
||||
row![
|
||||
button(
|
||||
keyboard::button(
|
||||
column![
|
||||
text(post.title.clone()).size(14).color(Color::WHITE),
|
||||
text(post.date.clone())
|
||||
@@ -357,7 +358,7 @@ fn recent_posts<'a>(posts: &'a [DashboardRecentPost], locale: UiLocale) -> Eleme
|
||||
}))
|
||||
.width(Length::Fill),
|
||||
status_badge(&post.status),
|
||||
button(text(t(locale, "dashboard.pin")).size(12))
|
||||
keyboard::button(text(t(locale, "dashboard.pin")).size(12))
|
||||
.on_press(Message::OpenTab(Tab {
|
||||
id: post.post_id.clone(),
|
||||
tab_type: TabType::Post,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::components::keyboard;
|
||||
use std::collections::{HashMap, hash_map::DefaultHasher};
|
||||
use std::fs;
|
||||
use std::hash::{Hash, Hasher};
|
||||
@@ -5,7 +6,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::{Duration, Instant, UNIX_EPOCH};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
use iced::widget::{button, column, container, markdown, row, scrollable, text};
|
||||
use iced::widget::{column, container, markdown, row, scrollable, text};
|
||||
use iced::{Element, Length};
|
||||
|
||||
use crate::app::Message;
|
||||
@@ -170,7 +171,7 @@ pub fn view(state: &DocumentationState, locale: UiLocale) -> Element<'_, Message
|
||||
.into(),
|
||||
],
|
||||
vec![
|
||||
button(text(t(locale, "common.refresh")).size(13))
|
||||
keyboard::button(text(t(locale, "common.refresh")).size(13))
|
||||
.on_press(Message::DocumentationRefresh(state.kind))
|
||||
.padding([6, 16])
|
||||
.style(inputs::secondary_button)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::components::keyboard;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bds_core::engine::embedding::DuplicateSearchResult;
|
||||
use bds_core::i18n::UiLocale;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, checkbox, column, container, row, scrollable, text};
|
||||
use iced::widget::{Space, checkbox, column, container, row, scrollable, text};
|
||||
use iced::{Color, Element, Length};
|
||||
|
||||
use crate::app::Message;
|
||||
@@ -23,19 +24,20 @@ pub struct DuplicatesState {
|
||||
|
||||
pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
||||
let refresh = if state.is_loading {
|
||||
button(text(t(locale, "duplicates.searching")).size(13)).style(inputs::secondary_button)
|
||||
keyboard::button(text(t(locale, "duplicates.searching")).size(13))
|
||||
.style(inputs::secondary_button)
|
||||
} else {
|
||||
button(text(t(locale, "common.refresh")).size(13))
|
||||
keyboard::button(text(t(locale, "common.refresh")).size(13))
|
||||
.on_press(Message::DuplicatesRefresh)
|
||||
.style(inputs::secondary_button)
|
||||
}
|
||||
.padding([6, 16]);
|
||||
|
||||
let dismiss_checked = if state.selected.is_empty() || state.is_loading {
|
||||
button(text(t(locale, "duplicates.dismissChecked")).size(13))
|
||||
keyboard::button(text(t(locale, "duplicates.dismissChecked")).size(13))
|
||||
.style(inputs::secondary_button)
|
||||
} else {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(tw(
|
||||
locale,
|
||||
"duplicates.dismissCheckedCount",
|
||||
@@ -64,12 +66,12 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
||||
.into(),
|
||||
],
|
||||
vec![
|
||||
button(text(t(locale, "duplicates.checkAll")).size(13))
|
||||
keyboard::button(text(t(locale, "duplicates.checkAll")).size(13))
|
||||
.on_press(Message::DuplicatesCheckAll)
|
||||
.padding([6, 12])
|
||||
.style(inputs::secondary_button)
|
||||
.into(),
|
||||
button(text(t(locale, "duplicates.uncheckAll")).size(13))
|
||||
keyboard::button(text(t(locale, "duplicates.uncheckAll")).size(13))
|
||||
.on_press(Message::DuplicatesUncheckAll)
|
||||
.padding([6, 12])
|
||||
.style(inputs::secondary_button)
|
||||
@@ -119,14 +121,17 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
||||
};
|
||||
pairs = pairs.push(inputs::card(
|
||||
row![
|
||||
checkbox("", checked)
|
||||
.on_toggle({
|
||||
let a = pair.post_id_a.clone();
|
||||
let b = pair.post_id_b.clone();
|
||||
move |_| Message::DuplicatesToggle(a.clone(), b.clone())
|
||||
})
|
||||
.size(16),
|
||||
button(
|
||||
keyboard::focusable(
|
||||
checkbox("", checked)
|
||||
.on_toggle({
|
||||
let a = pair.post_id_a.clone();
|
||||
let b = pair.post_id_b.clone();
|
||||
move |_| Message::DuplicatesToggle(a.clone(), b.clone())
|
||||
})
|
||||
.size(16),
|
||||
true,
|
||||
),
|
||||
keyboard::button(
|
||||
text(pair.title_a.clone())
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -135,7 +140,7 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
||||
.padding([5, 8])
|
||||
.style(inputs::disclosure_button),
|
||||
text("→").size(14).color(inputs::LABEL_COLOR),
|
||||
button(
|
||||
keyboard::button(
|
||||
text(pair.title_b.clone())
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -149,7 +154,7 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
||||
} else {
|
||||
Color::from_rgb(0.55, 0.76, 0.92)
|
||||
}),
|
||||
button(text(t(locale, "duplicates.dismiss")).size(12))
|
||||
keyboard::button(text(t(locale, "duplicates.dismiss")).size(12))
|
||||
.on_press(Message::DuplicatesDismiss(
|
||||
pair.post_id_a.clone(),
|
||||
pair.post_id_b.clone()
|
||||
@@ -163,7 +168,7 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
||||
}
|
||||
if state.result.has_more {
|
||||
pairs = pairs.push(
|
||||
button(text(t(locale, "duplicates.showMore")).size(13))
|
||||
keyboard::button(text(t(locale, "duplicates.showMore")).size(13))
|
||||
.on_press(Message::DuplicatesShowMore)
|
||||
.padding([7, 16])
|
||||
.style(inputs::secondary_button),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::components::keyboard;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use bds_core::engine::git::{
|
||||
@@ -6,7 +7,7 @@ use bds_core::engine::git::{
|
||||
};
|
||||
use bds_core::i18n::UiLocale;
|
||||
use iced::widget::text::{Shaping, Wrapping};
|
||||
use iced::widget::{Space, button, column, container, row, scrollable, text, text_input, tooltip};
|
||||
use iced::widget::{Space, column, container, row, scrollable, text, text_input, tooltip};
|
||||
use iced::{Alignment, Background, Color, Element, Font, Length};
|
||||
|
||||
use crate::app::Message;
|
||||
@@ -152,7 +153,7 @@ pub fn sidebar_view(
|
||||
text(t(locale, "git.notRepository"))
|
||||
.size(12)
|
||||
.color(Color::from_rgb(0.6, 0.6, 0.65)),
|
||||
button(text(t(locale, "git.initialize")).size(12))
|
||||
keyboard::button(text(t(locale, "git.initialize")).size(12))
|
||||
.on_press(Message::GitInitialize)
|
||||
.padding([5, 8])
|
||||
.style(inputs::primary_button),
|
||||
@@ -180,7 +181,7 @@ pub fn sidebar_view(
|
||||
let network_running = state.network_run.is_some();
|
||||
let network_button =
|
||||
|key: &'static str, icon: &'static str, message: Message| -> Element<'static, Message> {
|
||||
let mut control = button(text(icon).size(16).shaping(Shaping::Advanced))
|
||||
let mut control = keyboard::button(text(icon).size(16).shaping(Shaping::Advanced))
|
||||
.width(Length::Fixed(30.0))
|
||||
.height(Length::Fixed(28.0))
|
||||
.padding(0)
|
||||
@@ -189,13 +190,16 @@ pub fn sidebar_view(
|
||||
control = control.on_press(message);
|
||||
}
|
||||
|
||||
tooltip(
|
||||
control,
|
||||
text(t(locale, key)).size(12),
|
||||
tooltip::Position::Bottom,
|
||||
keyboard::focusable(
|
||||
tooltip(
|
||||
control,
|
||||
text(t(locale, key)).size(12),
|
||||
tooltip::Position::Bottom,
|
||||
)
|
||||
.gap(4)
|
||||
.style(inputs::tooltip_style),
|
||||
!offline_mode && !network_running,
|
||||
)
|
||||
.gap(4)
|
||||
.style(inputs::tooltip_style)
|
||||
.into()
|
||||
};
|
||||
let actions = row![
|
||||
@@ -240,7 +244,7 @@ pub fn sidebar_view(
|
||||
.padding([5, 7])
|
||||
.style(inputs::field_style),
|
||||
{
|
||||
let commit = button(text(t(locale, "git.commit")).size(11))
|
||||
let commit = keyboard::button(text(t(locale, "git.commit")).size(11))
|
||||
.padding([5, 7])
|
||||
.style(inputs::primary_button);
|
||||
if state.files.is_empty() || state.commit_message.trim().is_empty() {
|
||||
@@ -265,7 +269,7 @@ pub fn sidebar_view(
|
||||
content.extend(state.history.iter().take(20).map(history_button));
|
||||
}
|
||||
content.push(
|
||||
button(text(t(locale, "git.pruneLfs")).size(11))
|
||||
keyboard::button(text(t(locale, "git.pruneLfs")).size(11))
|
||||
.on_press(Message::GitPruneLfs)
|
||||
.padding([4, 7])
|
||||
.style(inputs::secondary_button)
|
||||
@@ -303,7 +307,7 @@ fn error_view(error: Option<&str>) -> Element<'static, Message> {
|
||||
|
||||
fn status_button(file: &GitFileStatus) -> Element<'static, Message> {
|
||||
let path = file.path.clone();
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text(path.clone()).size(11),
|
||||
Space::with_width(Length::Fill),
|
||||
@@ -322,7 +326,7 @@ fn history_button(commit: &GitCommit) -> Element<'static, Message> {
|
||||
let hash = commit.hash.clone();
|
||||
let subject = commit.subject.clone().unwrap_or_else(|| hash.clone());
|
||||
let short = hash.chars().take(7).collect::<String>();
|
||||
button(
|
||||
keyboard::button(
|
||||
column![
|
||||
text(subject.clone()).size(11),
|
||||
row![
|
||||
@@ -399,7 +403,7 @@ fn network_output(run: &GitNetworkRunState, locale: UiLocale) -> Element<'static
|
||||
.wrapping(Wrapping::Word)
|
||||
)
|
||||
.padding(6),
|
||||
button(text(t(locale, "common.cancel")).size(11))
|
||||
keyboard::button(text(t(locale, "common.cancel")).size(11))
|
||||
.on_press(Message::CancelTask(
|
||||
crate::state::navigation::TaskSource::Local,
|
||||
run.task_id,
|
||||
@@ -445,7 +449,7 @@ pub fn diff_view(
|
||||
.iter()
|
||||
.map(|change| {
|
||||
let selected = state.selected_path.as_deref() == Some(&change.path);
|
||||
let button = button(text(change.path.clone()).size(11))
|
||||
let button = keyboard::button(text(change.path.clone()).size(11))
|
||||
.padding([4, 7])
|
||||
.style(if selected {
|
||||
inputs::primary_button
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::components::keyboard;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -5,7 +6,7 @@ use bds_core::model::{
|
||||
ImportCandidate, ImportDefinition, ImportExecutionResult, ImportItemKind, ImportItemStatus,
|
||||
ImportPhase, ImportProgress, ImportReport, ImportResolution, TaxonomyKind,
|
||||
};
|
||||
use iced::widget::{Space, button, column, container, progress_bar, row, scrollable, text};
|
||||
use iced::widget::{Space, column, container, progress_bar, row, scrollable, text};
|
||||
use iced::{Alignment, Color, Element, Length};
|
||||
|
||||
use crate::app::Message;
|
||||
@@ -50,6 +51,25 @@ impl ImportEditorState {
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn section_is_visible(&self, section: ImportSection) -> bool {
|
||||
let Some(report) = &self.report else {
|
||||
return false;
|
||||
};
|
||||
match section {
|
||||
ImportSection::Conflicts => report
|
||||
.posts
|
||||
.iter()
|
||||
.chain(&report.pages)
|
||||
.chain(&report.media)
|
||||
.any(|item| item.status == ImportItemStatus::Conflict),
|
||||
ImportSection::Posts => !report.posts.is_empty(),
|
||||
ImportSection::Pages => !report.pages.is_empty(),
|
||||
ImportSection::Media => !report.media.is_empty(),
|
||||
ImportSection::Taxonomy => !report.taxonomies.is_empty(),
|
||||
ImportSection::Macros => !report.macros.is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -62,6 +82,32 @@ pub enum ImportSection {
|
||||
Macros,
|
||||
}
|
||||
|
||||
impl ImportSection {
|
||||
pub fn access_key(self) -> char {
|
||||
match self {
|
||||
Self::Conflicts => 'c',
|
||||
Self::Posts => 'o',
|
||||
Self::Pages => 'a',
|
||||
Self::Media => 'm',
|
||||
Self::Taxonomy => 't',
|
||||
Self::Macros => 'r',
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_access_key(key: char) -> Option<Self> {
|
||||
[
|
||||
Self::Conflicts,
|
||||
Self::Posts,
|
||||
Self::Pages,
|
||||
Self::Media,
|
||||
Self::Taxonomy,
|
||||
Self::Macros,
|
||||
]
|
||||
.into_iter()
|
||||
.find(|section| section.access_key() == key)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ImportEditorMsg {
|
||||
NameChanged(String),
|
||||
@@ -120,13 +166,13 @@ pub fn view<'a>(state: &'a ImportEditorState, locale: UiLocale) -> Element<'a, M
|
||||
column![
|
||||
row![
|
||||
name,
|
||||
button(text(t(locale, "modal.confirmDelete.delete")))
|
||||
keyboard::button(text(t(locale, "modal.confirmDelete.delete")))
|
||||
.on_press_maybe(
|
||||
(!busy).then_some(Message::ImportEditor(ImportEditorMsg::DeleteRequested,))
|
||||
)
|
||||
.padding([8, 12])
|
||||
.style(inputs::danger_button),
|
||||
button(text(t(locale, "import.analyze")))
|
||||
keyboard::button(text(t(locale, "import.analyze")))
|
||||
.on_press_maybe(
|
||||
(!busy && state.definition.wxr_file_path.is_some())
|
||||
.then_some(Message::ImportEditor(ImportEditorMsg::Analyze),)
|
||||
@@ -290,7 +336,7 @@ fn path_row<'a>(
|
||||
]
|
||||
.spacing(5)
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "common.open")))
|
||||
keyboard::button(text(t(locale, "common.open")))
|
||||
.on_press(Message::ImportEditor(action))
|
||||
.padding([7, 12])
|
||||
.style(inputs::secondary_button),
|
||||
@@ -457,7 +503,7 @@ fn execute_toolbar<'a>(
|
||||
.into(),
|
||||
],
|
||||
vec![
|
||||
button(text(t(locale, "import.autoMap")))
|
||||
keyboard::button(text(t(locale, "import.autoMap")))
|
||||
.on_press_maybe(
|
||||
(!state.is_analyzing && !state.is_executing)
|
||||
.then_some(Message::ImportEditor(ImportEditorMsg::AutoMapTaxonomy)),
|
||||
@@ -465,7 +511,7 @@ fn execute_toolbar<'a>(
|
||||
.padding([8, 12])
|
||||
.style(inputs::secondary_button)
|
||||
.into(),
|
||||
button(text(tw(
|
||||
keyboard::button(text(tw(
|
||||
locale,
|
||||
"import.execute",
|
||||
&[("count", &count.to_string())],
|
||||
@@ -514,7 +560,7 @@ fn section<'a>(
|
||||
) -> Vec<Element<'a, Message>> {
|
||||
let expanded = state.expanded.contains(§ion);
|
||||
let header = inputs::card(
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text(if expanded { "▾" } else { "▸" }).size(12),
|
||||
text(t(locale, title_key)).size(13),
|
||||
@@ -524,6 +570,7 @@ fn section<'a>(
|
||||
.on_press(Message::ImportEditor(ImportEditorMsg::ToggleSection(
|
||||
section,
|
||||
)))
|
||||
.hotkey(section.access_key())
|
||||
.padding([6, 8])
|
||||
.width(Length::Fill)
|
||||
.style(inputs::disclosure_button),
|
||||
@@ -673,7 +720,7 @@ fn taxonomy_rows<'a>(
|
||||
}),
|
||||
))
|
||||
.width(Length::Fixed(260.0)),
|
||||
button(text(t(locale, "common.clear")))
|
||||
keyboard::button(text(t(locale, "common.clear")))
|
||||
.on_press_maybe(item.mapped_to.is_some().then_some(Message::ImportEditor(
|
||||
ImportEditorMsg::SetTaxonomyMapping {
|
||||
kind,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::components::keyboard;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, column, container, image, row, scrollable, text};
|
||||
use iced::widget::{Space, column, container, image, row, scrollable, text};
|
||||
use iced::{Color, Element, Length};
|
||||
|
||||
use bds_core::i18n::{self, UiLocale};
|
||||
@@ -271,7 +272,7 @@ pub fn view<'a>(
|
||||
.style(status_bar::dropdown_bg)
|
||||
.into();
|
||||
let quick_actions_button: Element<'a, Message> =
|
||||
button(text(t(locale, "editor.quickActions")).size(13))
|
||||
keyboard::button(text(t(locale, "editor.quickActions")).size(13))
|
||||
.on_press_maybe(
|
||||
ai_enabled.then_some(Message::MediaEditor(MediaEditorMsg::ToggleQuickActions)),
|
||||
)
|
||||
@@ -290,17 +291,17 @@ pub fn view<'a>(
|
||||
vec![text(state.original_name.clone()).size(18).into()],
|
||||
vec![
|
||||
quick_actions,
|
||||
button(text(t(locale, "editor.replaceFile")).size(13))
|
||||
keyboard::button(text(t(locale, "editor.replaceFile")).size(13))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::ReplaceFile))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.into(),
|
||||
button(text(t(locale, "common.save")).size(13))
|
||||
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::Save))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16])
|
||||
.into(),
|
||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::Delete))
|
||||
.style(inputs::danger_button)
|
||||
.padding([6, 16])
|
||||
@@ -324,7 +325,7 @@ pub fn view<'a>(
|
||||
} else {
|
||||
Color::from_rgb(0.55, 0.58, 0.65)
|
||||
};
|
||||
button(text(label).size(12).shaping(Shaping::Advanced).color(color))
|
||||
keyboard::button(text(label).size(12).shaping(Shaping::Advanced).color(color))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::SwitchLanguage(
|
||||
flag.language.clone(),
|
||||
)))
|
||||
@@ -430,7 +431,7 @@ pub fn view<'a>(
|
||||
.size(12)
|
||||
.color(Color::from_rgb(0.55, 0.58, 0.65)),
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(t(locale, "editor.linkToPost")).size(12))
|
||||
keyboard::button(text(t(locale, "editor.linkToPost")).size(12))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::TogglePostPicker))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([4, 10]),
|
||||
@@ -457,7 +458,7 @@ pub fn view<'a>(
|
||||
.post_picker_results
|
||||
.iter()
|
||||
.map(|post| {
|
||||
button(text(post.title.clone()).size(12))
|
||||
keyboard::button(text(post.title.clone()).size(12))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::LinkPost(
|
||||
post.post_id.clone(),
|
||||
)))
|
||||
@@ -497,13 +498,13 @@ pub fn view<'a>(
|
||||
.iter()
|
||||
.map(|post| {
|
||||
row![
|
||||
button(text(post.title.clone()).size(12))
|
||||
keyboard::button(text(post.title.clone()).size(12))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::OpenLinkedPost(
|
||||
post.post_id.clone()
|
||||
)))
|
||||
.padding([4, 0]),
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(t(locale, "editor.unlinkMedia")).size(11))
|
||||
keyboard::button(text(t(locale, "editor.unlinkMedia")).size(11))
|
||||
.on_press(Message::MediaEditor(MediaEditorMsg::UnlinkPost(
|
||||
post.post_id.clone()
|
||||
)))
|
||||
@@ -569,7 +570,7 @@ fn quick_action_item<'a>(
|
||||
msg: MediaEditorMsg,
|
||||
enabled: bool,
|
||||
) -> Element<'a, Message> {
|
||||
button(text(label).size(12).shaping(Shaping::Advanced))
|
||||
keyboard::button(text(label).size(12).shaping(Shaping::Advanced))
|
||||
.on_press_maybe(enabled.then_some(Message::MediaEditor(msg)))
|
||||
.padding([6, 12])
|
||||
.style(status_bar::dropdown_item)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use std::collections::HashSet;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use bds_core::engine::menu::{MenuItem, MenuItemKind};
|
||||
use bds_core::i18n::UiLocale;
|
||||
use iced::widget::{
|
||||
Space, button, column, container, mouse_area, row, scrollable, svg, text, text_input, tooltip,
|
||||
Space, column, container, mouse_area, row, scrollable, svg, text, text_input, tooltip,
|
||||
};
|
||||
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Point, Theme};
|
||||
use uuid::Uuid;
|
||||
@@ -810,19 +812,22 @@ fn toolbar_icon(
|
||||
} else {
|
||||
secondary_button
|
||||
};
|
||||
let mut control = button(text(glyph).size(18))
|
||||
let mut control = keyboard::button(text(glyph).size(18))
|
||||
.width(Length::Fixed(38.0))
|
||||
.height(Length::Fixed(34.0))
|
||||
.style(style);
|
||||
if enabled {
|
||||
control = control.on_press(Message::MenuEditor(message));
|
||||
}
|
||||
tooltip(
|
||||
control,
|
||||
text(t(locale, key)).size(12),
|
||||
tooltip::Position::Bottom,
|
||||
keyboard::focusable(
|
||||
tooltip(
|
||||
control,
|
||||
text(t(locale, key)).size(12),
|
||||
tooltip::Position::Bottom,
|
||||
)
|
||||
.gap(4),
|
||||
enabled,
|
||||
)
|
||||
.gap(4)
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -856,7 +861,7 @@ fn tree_item<'a>(
|
||||
let collapsed = state.collapsed.contains(&item.id);
|
||||
let id = item.id.clone();
|
||||
let toggle: Element<'_, Message> = if is_submenu {
|
||||
button(text(if collapsed { "▸" } else { "▾" }).size(14))
|
||||
keyboard::button(text(if collapsed { "▸" } else { "▾" }).size(14))
|
||||
.on_press(Message::MenuEditor(MenuEditorMsg::ToggleExpanded(
|
||||
id.clone(),
|
||||
)))
|
||||
@@ -884,7 +889,7 @@ fn tree_item<'a>(
|
||||
} else {
|
||||
item.label.clone()
|
||||
};
|
||||
let label_button = button(
|
||||
let label_button = keyboard::button(
|
||||
row![
|
||||
kind_icon(&item.kind),
|
||||
text(label).size(14),
|
||||
@@ -1016,7 +1021,7 @@ fn draft_editor<'a>(
|
||||
.filter(|page| page_matches_query(page, &draft.query))
|
||||
{
|
||||
choices = choices.push(
|
||||
button(text(page.title.clone()))
|
||||
keyboard::button(text(page.title.clone()))
|
||||
.on_press(Message::MenuEditor(MenuEditorMsg::ChoosePage(
|
||||
page.id.clone(),
|
||||
)))
|
||||
@@ -1032,7 +1037,7 @@ fn draft_editor<'a>(
|
||||
.filter(|name| category_matches_query(name, &draft.query))
|
||||
{
|
||||
choices = choices.push(
|
||||
button(text(category.clone()))
|
||||
keyboard::button(text(category.clone()))
|
||||
.on_press(Message::MenuEditor(MenuEditorMsg::ChooseCategory(
|
||||
category.clone(),
|
||||
)))
|
||||
@@ -1047,10 +1052,10 @@ fn draft_editor<'a>(
|
||||
DraftKind::Category => "menuEditor.useCategory",
|
||||
};
|
||||
let actions = row![
|
||||
button(text(t(locale, submit_label)))
|
||||
keyboard::button(text(t(locale, submit_label)))
|
||||
.on_press(Message::MenuEditor(MenuEditorMsg::SubmitDraft))
|
||||
.style(primary_button),
|
||||
button(text(t(locale, "common.cancel")))
|
||||
keyboard::button(text(t(locale, "common.cancel")))
|
||||
.on_press(Message::MenuEditor(MenuEditorMsg::CancelDraft))
|
||||
.style(secondary_button),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
||||
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||
use iced::{Color, Element, Length};
|
||||
|
||||
use bds_core::engine::metadata_diff::{DiffReport, RepairDirection};
|
||||
@@ -18,10 +19,11 @@ pub struct MetadataDiffState {
|
||||
}
|
||||
|
||||
pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, Message> {
|
||||
let run = button(text(t(locale, "metadataDiff.run")).size(13))
|
||||
let run = keyboard::button(text(t(locale, "metadataDiff.run")).size(13))
|
||||
.on_press_maybe(
|
||||
(!state.is_running && !state.is_repairing).then_some(Message::RunMetadataDiff),
|
||||
)
|
||||
.hotkey('r')
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]);
|
||||
let mut content = column![
|
||||
@@ -65,7 +67,7 @@ pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, M
|
||||
)
|
||||
});
|
||||
let actions = row![
|
||||
button(text(t(locale, "metadataDiff.fileToDb")).size(12))
|
||||
keyboard::button(text(t(locale, "metadataDiff.fileToDb")).size(12))
|
||||
.on_press_maybe((!state.is_repairing).then_some(
|
||||
Message::RepairMetadataDiffItem {
|
||||
index,
|
||||
@@ -74,7 +76,7 @@ pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, M
|
||||
))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([5, 10]),
|
||||
button(text(t(locale, "metadataDiff.dbToFile")).size(12))
|
||||
keyboard::button(text(t(locale, "metadataDiff.dbToFile")).size(12))
|
||||
.on_press_maybe((!state.is_repairing).then_some(
|
||||
Message::RepairMetadataDiffItem {
|
||||
index,
|
||||
@@ -116,7 +118,7 @@ pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, M
|
||||
.align_y(iced::Alignment::Center);
|
||||
if orphan.reason == "file_without_db_entry" {
|
||||
item = item.push(
|
||||
button(text(t(locale, "metadataDiff.importOrphan")).size(12))
|
||||
keyboard::button(text(t(locale, "metadataDiff.importOrphan")).size(12))
|
||||
.on_press_maybe(
|
||||
(!state.is_repairing)
|
||||
.then_some(Message::ImportMetadataOrphan(index)),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use std::path::Path;
|
||||
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{
|
||||
Space, button, checkbox, column, container, image, row, scrollable, text, text_input,
|
||||
};
|
||||
use iced::widget::{Space, checkbox, column, container, image, row, scrollable, text, text_input};
|
||||
use iced::{Alignment, Background, Border, Color, Element, Length, Shadow, Theme, Vector};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -282,7 +282,7 @@ pub fn view(
|
||||
let selected = selected_project_id.as_deref() == Some(project.id.as_str());
|
||||
let marker = if selected { "●" } else { "○" };
|
||||
project_rows = project_rows.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text(marker).size(12),
|
||||
text(project.name.clone()).size(13),
|
||||
@@ -307,12 +307,12 @@ pub fn view(
|
||||
content = content.push(project_rows);
|
||||
}
|
||||
|
||||
let cancel = button(text(t(locale, "remoteConnection.cancel")).size(13))
|
||||
let cancel = keyboard::button(text(t(locale, "remoteConnection.cancel")).size(13))
|
||||
.on_press(Message::DismissModal)
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style);
|
||||
let action = if !connected {
|
||||
let button = button(
|
||||
let button = keyboard::button(
|
||||
text(if connecting {
|
||||
t(locale, "remoteConnection.connecting")
|
||||
} else {
|
||||
@@ -328,7 +328,7 @@ pub fn view(
|
||||
button.on_press(Message::RemoteConnectRequested)
|
||||
}
|
||||
} else {
|
||||
let button = button(text(t(locale, "remoteConnection.open")).size(13))
|
||||
let button = keyboard::button(text(t(locale, "remoteConnection.open")).size(13))
|
||||
.padding([6, 16])
|
||||
.style(confirm_button_style);
|
||||
if selected_project_id.is_some() {
|
||||
@@ -393,7 +393,7 @@ pub fn view(
|
||||
|
||||
let on_confirm_clone = on_confirm.clone();
|
||||
let buttons = row![
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "modal.confirmDelete.cancel"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -402,7 +402,7 @@ pub fn view(
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style),
|
||||
Space::with_width(Length::Fill),
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "modal.confirmDelete.delete"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -438,7 +438,7 @@ pub fn view(
|
||||
|
||||
let on_confirm_clone = on_confirm.clone();
|
||||
let buttons = row![
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "modal.confirm.cancel"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -447,7 +447,7 @@ pub fn view(
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style),
|
||||
Space::with_width(Length::Fill),
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "modal.confirm.confirm"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -474,12 +474,12 @@ pub fn view(
|
||||
|
||||
ModalState::SearchIndexRepair => {
|
||||
let buttons = row![
|
||||
button(text(t(locale, "searchIndexRepair.later")).size(13))
|
||||
keyboard::button(text(t(locale, "searchIndexRepair.later")).size(13))
|
||||
.on_press(Message::DismissModal)
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style),
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(t(locale, "searchIndexRepair.rebuildNow")).size(13))
|
||||
keyboard::button(text(t(locale, "searchIndexRepair.rebuildNow")).size(13))
|
||||
.on_press(Message::ConfirmModal(ConfirmAction::RebuildSearchIndex))
|
||||
.padding([6, 16])
|
||||
.style(confirm_button_style),
|
||||
@@ -520,7 +520,7 @@ pub fn view(
|
||||
);
|
||||
}
|
||||
let mut actions = row![
|
||||
button(text(t(locale, "find.next")))
|
||||
keyboard::button(text(t(locale, "find.next")))
|
||||
.on_press(Message::FindNext)
|
||||
.style(inputs::primary_button),
|
||||
]
|
||||
@@ -528,12 +528,12 @@ pub fn view(
|
||||
if show_replace {
|
||||
actions = actions
|
||||
.push(
|
||||
button(text(t(locale, "find.replace")))
|
||||
keyboard::button(text(t(locale, "find.replace")))
|
||||
.on_press(Message::ReplaceCurrent)
|
||||
.style(inputs::secondary_button),
|
||||
)
|
||||
.push(
|
||||
button(text(t(locale, "find.replaceAll")))
|
||||
keyboard::button(text(t(locale, "find.replaceAll")))
|
||||
.on_press(Message::ReplaceAll)
|
||||
.style(inputs::secondary_button),
|
||||
);
|
||||
@@ -582,7 +582,7 @@ pub fn view(
|
||||
} else {
|
||||
content.push(Space::with_height(16.0)).push(row![
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(t(locale, "tasks.cancelTask")).size(13))
|
||||
keyboard::button(text(t(locale, "tasks.cancelTask")).size(13))
|
||||
.on_press(Message::CancelTask(
|
||||
crate::state::navigation::TaskSource::Local,
|
||||
task_id,
|
||||
@@ -607,7 +607,7 @@ pub fn view(
|
||||
external_url,
|
||||
external_text,
|
||||
} => {
|
||||
let internal_tab = button(
|
||||
let internal_tab = keyboard::button(
|
||||
text(t(locale, "modal.postInsertLink.tabInternal"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -623,7 +623,7 @@ pub fn view(
|
||||
.padding([8, 16])
|
||||
.style(cancel_button_style);
|
||||
|
||||
let external_tab = button(
|
||||
let external_tab = keyboard::button(
|
||||
text(t(locale, "modal.postInsertLink.tabExternal"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -666,7 +666,7 @@ pub fn view(
|
||||
let mut column = column![search_input, Space::with_height(12.0)];
|
||||
for link in results {
|
||||
column = column.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
column![
|
||||
text(link.title.clone())
|
||||
@@ -749,7 +749,7 @@ pub fn view(
|
||||
Space::with_height(12.0),
|
||||
row![
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(t(locale, "modal.postInsertLink.insert")))
|
||||
keyboard::button(text(t(locale, "modal.postInsertLink.insert")))
|
||||
.on_press(Message::PostEditor(
|
||||
PostEditorMsg::PostInsertLinkExternalInsert
|
||||
))
|
||||
@@ -760,7 +760,7 @@ pub fn view(
|
||||
.spacing(8)
|
||||
.into();
|
||||
|
||||
let create_post_btn: Element<'static, Message> = button(
|
||||
let create_post_btn: Element<'static, Message> = keyboard::button(
|
||||
text(t(locale, "modal.postInsertLink.createPost"))
|
||||
.size(12)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -792,7 +792,7 @@ pub fn view(
|
||||
};
|
||||
|
||||
let buttons = row![
|
||||
button(cancel_text)
|
||||
keyboard::button(cancel_text)
|
||||
.on_press(Message::DismissModal)
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style),
|
||||
@@ -903,7 +903,7 @@ pub fn view(
|
||||
.spacing(4)
|
||||
.align_x(Alignment::Center);
|
||||
|
||||
let btn = button(media_col)
|
||||
let btn = keyboard::button(media_col)
|
||||
.on_press(Message::PostEditor(PostEditorMsg::PostInsertMediaSelected(
|
||||
m.id.clone(),
|
||||
)))
|
||||
@@ -963,7 +963,7 @@ pub fn view(
|
||||
.shaping(Shaping::Advanced);
|
||||
|
||||
let buttons = row![
|
||||
button(cancel_text)
|
||||
keyboard::button(cancel_text)
|
||||
.on_press(Message::DismissModal)
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style),
|
||||
@@ -1045,7 +1045,7 @@ pub fn view(
|
||||
.spacing(4)
|
||||
.align_x(Alignment::Center);
|
||||
|
||||
let btn = button(media_col)
|
||||
let btn = keyboard::button(media_col)
|
||||
.on_press(Message::PostEditor(
|
||||
PostEditorMsg::PostGalleryImageSelected(index),
|
||||
))
|
||||
@@ -1082,7 +1082,7 @@ pub fn view(
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced);
|
||||
|
||||
let close_button = button(close_text)
|
||||
let close_button = keyboard::button(close_text)
|
||||
.on_press(Message::DismissModal)
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style);
|
||||
@@ -1106,7 +1106,7 @@ pub fn view(
|
||||
.width(Length::Fill)
|
||||
.center_x(Length::Fill),
|
||||
row![
|
||||
button(text("<"))
|
||||
keyboard::button(text("<"))
|
||||
.on_press(Message::PostEditor(PostEditorMsg::PostGalleryPrevious))
|
||||
.padding([6, 12])
|
||||
.style(cancel_button_style),
|
||||
@@ -1115,12 +1115,12 @@ pub fn view(
|
||||
.size(12)
|
||||
.shaping(Shaping::Advanced),
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(">"))
|
||||
keyboard::button(text(">"))
|
||||
.on_press(Message::PostEditor(PostEditorMsg::PostGalleryNext))
|
||||
.padding([6, 12])
|
||||
.style(cancel_button_style),
|
||||
Space::with_width(12.0),
|
||||
button(text(t(locale, "modal.postGallery.backToGrid")))
|
||||
keyboard::button(text(t(locale, "modal.postGallery.backToGrid")))
|
||||
.on_press(Message::PostEditor(
|
||||
PostEditorMsg::PostGalleryCloseLightbox
|
||||
))
|
||||
@@ -1166,13 +1166,15 @@ pub fn view(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, field)| {
|
||||
let toggle =
|
||||
let toggle = keyboard::focusable(
|
||||
checkbox(field.label.clone(), field.accepted)
|
||||
.on_toggle_maybe((!field.locked).then_some(move |value| {
|
||||
Message::ToggleAiSuggestionField(index, value)
|
||||
}))
|
||||
.size(16)
|
||||
.text_size(13);
|
||||
.text_size(13),
|
||||
!field.locked,
|
||||
);
|
||||
container(
|
||||
column![
|
||||
toggle,
|
||||
@@ -1211,12 +1213,12 @@ pub fn view(
|
||||
.collect::<Vec<Element<'static, Message>>>();
|
||||
|
||||
let buttons = row![
|
||||
button(text(t(locale, "common.cancel")).size(13))
|
||||
keyboard::button(text(t(locale, "common.cancel")).size(13))
|
||||
.on_press(Message::DismissModal)
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style),
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(t(locale, "modal.aiSuggestions.applySelected")).size(13))
|
||||
keyboard::button(text(t(locale, "modal.aiSuggestions.applySelected")).size(13))
|
||||
.on_press(Message::ApplyAiSuggestions(target, fields))
|
||||
.padding([6, 16])
|
||||
.style(confirm_button_style),
|
||||
@@ -1275,7 +1277,7 @@ pub fn view(
|
||||
),
|
||||
};
|
||||
let status = language.existing_status.clone().unwrap_or_default();
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text(format!("{} {}", language.flag_emoji, language.name))
|
||||
.size(13)
|
||||
@@ -1302,7 +1304,7 @@ pub fn view(
|
||||
Space::with_height(12.0),
|
||||
column(rows).spacing(6),
|
||||
Space::with_height(16.0),
|
||||
button(text(t(locale, "common.cancel")).size(13))
|
||||
keyboard::button(text(t(locale, "common.cancel")).size(13))
|
||||
.on_press(Message::DismissModal)
|
||||
.padding([6, 16])
|
||||
.style(cancel_button_style),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, column, container, progress_bar, row, scrollable, text};
|
||||
use iced::widget::{Space, column, container, progress_bar, row, scrollable, text};
|
||||
use iced::{Alignment, Background, Border, Color, Element, Font, Length, Theme};
|
||||
|
||||
use bds_core::engine::git::GitCommit;
|
||||
@@ -80,7 +82,7 @@ fn task_row(
|
||||
rows.push(
|
||||
row![
|
||||
Space::with_width(Length::Fill),
|
||||
button(text(t(locale, "tasks.cancelTask")).size(10))
|
||||
keyboard::button(text(t(locale, "tasks.cancelTask")).size(10))
|
||||
.on_press(Message::CancelTask(snapshot.source, snapshot.id))
|
||||
.padding([3, 8])
|
||||
.style(inputs::secondary_button),
|
||||
@@ -267,7 +269,7 @@ pub fn view(
|
||||
// Tab header — per layout.allium: tasks, output, post_links (only when
|
||||
// active editor tab is a post), git_log (only when active tab is post or
|
||||
// media).
|
||||
let tasks_btn = button(
|
||||
let tasks_btn = keyboard::button(
|
||||
text(t(locale, "common.tasks"))
|
||||
.size(12)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -280,7 +282,7 @@ pub fn view(
|
||||
tab_inactive
|
||||
});
|
||||
|
||||
let output_btn = button(
|
||||
let output_btn = keyboard::button(
|
||||
text(t(locale, "panel.output"))
|
||||
.size(12)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -293,7 +295,7 @@ pub fn view(
|
||||
tab_inactive
|
||||
});
|
||||
|
||||
let close_btn = button(text("\u{2715}").size(12).shaping(Shaping::Advanced))
|
||||
let close_btn = keyboard::button(text("\u{2715}").size(12).shaping(Shaping::Advanced))
|
||||
.on_press(Message::TogglePanel)
|
||||
.padding([4, 6])
|
||||
.style(close_btn_style);
|
||||
@@ -301,7 +303,7 @@ pub fn view(
|
||||
let mut tab_row: Vec<Element<'static, Message>> = vec![tasks_btn.into(), output_btn.into()];
|
||||
|
||||
if active_tab_is_post {
|
||||
let post_links_btn = button(
|
||||
let post_links_btn = keyboard::button(
|
||||
text(t(locale, "panel.postLinks"))
|
||||
.size(12)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -317,7 +319,7 @@ pub fn view(
|
||||
}
|
||||
|
||||
if active_tab_is_post_or_media {
|
||||
let git_log_btn = button(
|
||||
let git_log_btn = keyboard::button(
|
||||
text(t(locale, "panel.gitLog"))
|
||||
.size(12)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -372,7 +374,7 @@ pub fn view(
|
||||
let collapsed = collapsed_task_groups.contains(group_id);
|
||||
let group_name = snapshot.group_name.as_deref().unwrap_or(group_id);
|
||||
items.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text(if collapsed { "\u{25b8}" } else { "\u{25be}" }).size(11),
|
||||
text(format!("{} ({})", group_name, members.len()))
|
||||
@@ -524,7 +526,7 @@ pub fn view(
|
||||
let hash = commit.hash.clone();
|
||||
let subject = commit.subject.clone().unwrap_or_else(|| hash.clone());
|
||||
let short = hash.chars().take(7).collect::<String>();
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text(short).size(11).font(iced::Font::MONOSPACE),
|
||||
text(subject.clone()).size(11),
|
||||
@@ -560,7 +562,7 @@ pub fn view(
|
||||
}
|
||||
|
||||
fn post_link_button(locale: UiLocale, link: &ResolvedPostLink) -> Element<'static, Message> {
|
||||
button(text(link.title.clone()).size(11).shaping(Shaping::Advanced))
|
||||
keyboard::button(text(link.title.clone()).size(11).shaping(Shaping::Advanced))
|
||||
.on_press(Message::OpenTab(Tab {
|
||||
id: link.post_id.clone(),
|
||||
title: if link.title.is_empty() {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use iced::widget::text::{Shaping, Wrapping};
|
||||
use iced::widget::{Column, Space, button, column, container, row, scrollable, text, text_input};
|
||||
use iced::widget::{Column, Space, column, container, row, scrollable, text, text_input};
|
||||
use iced::{Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::{self, UiLocale};
|
||||
@@ -489,7 +491,7 @@ pub fn view<'a>(
|
||||
format!("{}{}", truncate_header_title(&state.title), dirty_indicator)
|
||||
};
|
||||
|
||||
let quick_actions_button: Element<'a, Message> = button(
|
||||
let quick_actions_button: Element<'a, Message> = keyboard::button(
|
||||
text(t(locale, "editor.quickActions"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -569,7 +571,7 @@ pub fn view<'a>(
|
||||
let mut header_action_items: Vec<Element<'a, Message>> = vec![
|
||||
status_badge(locale, &state.status),
|
||||
quick_actions,
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "common.save"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -581,7 +583,7 @@ pub fn view<'a>(
|
||||
];
|
||||
if state.status == PostStatus::Draft {
|
||||
header_action_items.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "editor.publish"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -594,7 +596,7 @@ pub fn view<'a>(
|
||||
}
|
||||
if !on_translation && state.status == PostStatus::Draft && state.published_at.is_some() {
|
||||
header_action_items.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "editor.discard"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -606,7 +608,7 @@ pub fn view<'a>(
|
||||
);
|
||||
}
|
||||
header_action_items.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "modal.confirmDelete.delete"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -643,13 +645,14 @@ pub fn view<'a>(
|
||||
} else {
|
||||
format!("\u{25B6} {}", t(locale, "editor.metadata"))
|
||||
};
|
||||
let meta_toggle = button(
|
||||
let meta_toggle = keyboard::button(
|
||||
text(meta_toggle_label)
|
||||
.size(12)
|
||||
.color(inputs::SECTION_COLOR)
|
||||
.shaping(Shaping::Advanced),
|
||||
)
|
||||
.on_press(Message::PostEditor(PostEditorMsg::ToggleMetadata))
|
||||
.hotkey('m')
|
||||
.padding([8, 10])
|
||||
.width(Length::Fill)
|
||||
.style(inputs::disclosure_button);
|
||||
@@ -663,7 +666,7 @@ pub fn view<'a>(
|
||||
for flag in &flags {
|
||||
let lang = flag.language.clone();
|
||||
let label = flag.flag_emoji.to_string();
|
||||
let btn = button(text(label).size(14).shaping(Shaping::Advanced))
|
||||
let btn = keyboard::button(text(label).size(14).shaping(Shaping::Advanced))
|
||||
.on_press(Message::PostEditor(PostEditorMsg::SwitchLanguage(lang)))
|
||||
.padding([2, 4])
|
||||
.style(if flag.is_active {
|
||||
@@ -756,7 +759,7 @@ pub fn view<'a>(
|
||||
.align_y(iced::Alignment::Center);
|
||||
for tag in semantic_suggestions {
|
||||
chips = chips.push(
|
||||
button(text(format!("+ {tag}")).size(11))
|
||||
keyboard::button(text(format!("+ {tag}")).size(11))
|
||||
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(
|
||||
tag.to_string(),
|
||||
)))
|
||||
@@ -784,7 +787,7 @@ pub fn view<'a>(
|
||||
.align_y(iced::Alignment::Center);
|
||||
for tag in matching_suggestions {
|
||||
chips = chips.push(
|
||||
button(text(tag).size(11))
|
||||
keyboard::button(text(tag).size(11))
|
||||
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(
|
||||
tag.to_string(),
|
||||
)))
|
||||
@@ -795,10 +798,12 @@ pub fn view<'a>(
|
||||
if query_addable {
|
||||
let query = state.tags_input.trim().to_string();
|
||||
chips = chips.push(
|
||||
button(text(tw(locale, "editor.createTag", &[("name", &query)])).size(11))
|
||||
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(query)))
|
||||
.padding([4, 8])
|
||||
.style(inputs::secondary_button),
|
||||
keyboard::button(
|
||||
text(tw(locale, "editor.createTag", &[("name", &query)])).size(11),
|
||||
)
|
||||
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(query)))
|
||||
.padding([4, 8])
|
||||
.style(inputs::secondary_button),
|
||||
);
|
||||
}
|
||||
chips.wrap().into()
|
||||
@@ -860,7 +865,7 @@ pub fn view<'a>(
|
||||
Column::with_children(items).spacing(2).into()
|
||||
};
|
||||
|
||||
let link_existing_button: Element<'a, Message> = button(
|
||||
let link_existing_button: Element<'a, Message> = keyboard::button(
|
||||
text(t(locale, "editor.linkExistingMedia"))
|
||||
.size(11)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -916,14 +921,14 @@ pub fn view<'a>(
|
||||
]
|
||||
.spacing(2)
|
||||
.width(Length::Fill),
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "common.open"))
|
||||
.size(11)
|
||||
.shaping(Shaping::Advanced)
|
||||
)
|
||||
.on_press(Message::PostEditor(PostEditorMsg::OpenLinkedMedia(open_id)))
|
||||
.padding([4, 10]),
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "editor.unlinkMedia"))
|
||||
.size(11)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -981,13 +986,14 @@ pub fn view<'a>(
|
||||
} else {
|
||||
format!("\u{25B6} {}", t(locale, "editor.excerpt"))
|
||||
};
|
||||
let excerpt_toggle = button(
|
||||
let excerpt_toggle = keyboard::button(
|
||||
text(excerpt_toggle_label)
|
||||
.size(12)
|
||||
.color(inputs::SECTION_COLOR)
|
||||
.shaping(Shaping::Advanced),
|
||||
)
|
||||
.on_press(Message::PostEditor(PostEditorMsg::ToggleExcerpt))
|
||||
.hotkey('x')
|
||||
.padding([8, 10])
|
||||
.width(Length::Fill)
|
||||
.style(inputs::disclosure_button);
|
||||
@@ -1036,7 +1042,7 @@ pub fn view<'a>(
|
||||
],
|
||||
vec![
|
||||
if show_content_actions {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "editor.insertLink"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -1049,7 +1055,7 @@ pub fn view<'a>(
|
||||
Space::new(0, 0).into()
|
||||
},
|
||||
if show_content_actions {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "editor.insertMedia"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -1062,7 +1068,7 @@ pub fn view<'a>(
|
||||
Space::new(0, 0).into()
|
||||
},
|
||||
if show_content_actions {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "editor.gallery"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -1209,7 +1215,7 @@ fn chip_input_field<'a>(
|
||||
.map(|chip| {
|
||||
let label = format!("{} \u{2715}", chip);
|
||||
let chip_val = chip.clone();
|
||||
button(text(label).size(11).shaping(Shaping::Advanced))
|
||||
keyboard::button(text(label).size(11).shaping(Shaping::Advanced))
|
||||
.on_press(on_remove(chip_val))
|
||||
.padding([2, 6])
|
||||
.style(chip_button_style)
|
||||
@@ -1266,7 +1272,7 @@ fn mode_button<'a>(
|
||||
"preview" => t(locale, "editor.modePreview"),
|
||||
_ => t(locale, "editor.modeMarkdown"),
|
||||
};
|
||||
button(text(label).size(12).shaping(Shaping::Advanced))
|
||||
keyboard::button(text(label).size(12).shaping(Shaping::Advanced))
|
||||
.on_press(message)
|
||||
.padding([4, 10])
|
||||
.style(if active_mode == mode {
|
||||
@@ -1284,7 +1290,7 @@ fn quick_action_item<'a>(
|
||||
enabled: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let _ = locale;
|
||||
button(text(label).size(12).shaping(Shaping::Advanced))
|
||||
keyboard::button(text(label).size(12).shaping(Shaping::Advanced))
|
||||
.on_press_maybe(enabled.then_some(Message::PostEditor(msg)))
|
||||
.padding([6, 12])
|
||||
.style(status_bar::dropdown_item)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Column, Space, button, container, row, svg, text};
|
||||
use iced::widget::{Column, Space, container, row, svg, text};
|
||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -148,7 +150,7 @@ pub fn view(
|
||||
};
|
||||
|
||||
items.push(
|
||||
button(label)
|
||||
keyboard::button(label)
|
||||
.on_press(Message::SwitchProject(id))
|
||||
.padding([4, 8])
|
||||
.width(Length::Fill)
|
||||
@@ -170,7 +172,7 @@ pub fn view(
|
||||
|
||||
// Open and create project actions
|
||||
items.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text("↗")
|
||||
.size(14)
|
||||
@@ -190,7 +192,7 @@ pub fn view(
|
||||
);
|
||||
|
||||
items.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text("+")
|
||||
.size(14)
|
||||
@@ -256,7 +258,7 @@ pub fn trigger_button(project_name: &str) -> Element<'static, Message> {
|
||||
.shaping(Shaping::Advanced)
|
||||
.color(Color::from_rgb(0.55, 0.55, 0.60));
|
||||
|
||||
button(
|
||||
keyboard::button(
|
||||
row![folder_icon, name, chevron]
|
||||
.spacing(4)
|
||||
.align_y(iced::Alignment::Center),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
||||
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||
use iced::{Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -125,22 +126,22 @@ pub fn view<'a>(state: &'a ScriptEditorState, locale: UiLocale) -> Element<'a, M
|
||||
status_badge(&state.status),
|
||||
],
|
||||
vec![
|
||||
button(text(t(locale, "common.save")).size(13))
|
||||
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::Save))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16])
|
||||
.into(),
|
||||
button(text(t(locale, "editor.run")).size(13))
|
||||
keyboard::button(text(t(locale, "editor.run")).size(13))
|
||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::Run))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.into(),
|
||||
button(text(t(locale, "editor.checkSyntax")).size(13))
|
||||
keyboard::button(text(t(locale, "editor.checkSyntax")).size(13))
|
||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::CheckSyntax))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.into(),
|
||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::Delete))
|
||||
.style(inputs::danger_button)
|
||||
.padding([6, 16])
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{button, column, container, row, scrollable, text, text_editor, text_input};
|
||||
use iced::widget::{column, container, row, scrollable, text, text_editor, text_input};
|
||||
use iced::{Alignment, Color, Element, Length};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -49,6 +50,34 @@ pub enum SettingsSection {
|
||||
MCP,
|
||||
}
|
||||
|
||||
impl SettingsSection {
|
||||
pub fn access_key(&self) -> char {
|
||||
match self {
|
||||
Self::Project => 'p',
|
||||
Self::Editor => 'r',
|
||||
Self::AI => 'a',
|
||||
Self::Technology => 't',
|
||||
Self::Publishing => 'u',
|
||||
Self::Data => 'd',
|
||||
Self::MCP => 'c',
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_access_key(key: char) -> Option<Self> {
|
||||
[
|
||||
Self::Project,
|
||||
Self::Editor,
|
||||
Self::AI,
|
||||
Self::Technology,
|
||||
Self::Publishing,
|
||||
Self::Data,
|
||||
Self::MCP,
|
||||
]
|
||||
.into_iter()
|
||||
.find(|section| section.access_key() == key)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SettingsCategoryRow {
|
||||
pub name: String,
|
||||
@@ -261,6 +290,13 @@ impl Default for SettingsViewState {
|
||||
}
|
||||
|
||||
impl SettingsViewState {
|
||||
pub fn section_is_visible(&self, section: &SettingsSection, locale: UiLocale) -> bool {
|
||||
self.search_query.is_empty()
|
||||
|| t(locale, section.i18n_key())
|
||||
.to_lowercase()
|
||||
.contains(&self.search_query.to_lowercase())
|
||||
}
|
||||
|
||||
pub fn focus_section(&mut self, section: SettingsSection) {
|
||||
self.collapsed = SettingsSection::all()
|
||||
.iter()
|
||||
@@ -369,12 +405,10 @@ pub fn view<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a, M
|
||||
.padding([8, 10])
|
||||
.style(inputs::field_style);
|
||||
|
||||
let query_lower = state.search_query.to_lowercase();
|
||||
|
||||
let mut section_items = Vec::new();
|
||||
for section in state.ordered_sections() {
|
||||
let label = t(locale, section.i18n_key());
|
||||
if !query_lower.is_empty() && !label.to_lowercase().contains(&query_lower) {
|
||||
if !state.section_is_visible(§ion, locale) {
|
||||
continue;
|
||||
}
|
||||
let collapsed = state.collapsed.contains(§ion);
|
||||
@@ -387,7 +421,7 @@ pub fn view<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a, M
|
||||
text(t(locale, "common.noResults"))
|
||||
.size(14)
|
||||
.color(Color::from_rgb(0.7, 0.72, 0.78)),
|
||||
button(text(t(locale, "common.clear")).size(13))
|
||||
keyboard::button(text(t(locale, "common.clear")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::SearchChanged(String::new())))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 12]),
|
||||
@@ -423,7 +457,7 @@ fn render_section<'a>(
|
||||
locale: UiLocale,
|
||||
) -> Element<'a, Message> {
|
||||
let toggle_char = if collapsed { "\u{25B6}" } else { "\u{25BC}" };
|
||||
let header = button(
|
||||
let header = keyboard::button(
|
||||
row![
|
||||
text(toggle_char).size(12),
|
||||
text(label.to_string()).size(14).color(Color::WHITE),
|
||||
@@ -434,6 +468,7 @@ fn render_section<'a>(
|
||||
.on_press(Message::Settings(SettingsMsg::ToggleSection(
|
||||
section.clone(),
|
||||
)))
|
||||
.hotkey(section.access_key())
|
||||
.padding([6, 8])
|
||||
.width(Length::Fill)
|
||||
.style(inputs::disclosure_button);
|
||||
@@ -478,11 +513,11 @@ fn section_project<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
||||
inputs::labeled_input(&t(locale, "settings.dataPath"), "", &state.data_path, |s| {
|
||||
Message::Settings(SettingsMsg::DataPathChanged(s))
|
||||
},),
|
||||
button(text(t(locale, "settings.browse")).size(12))
|
||||
keyboard::button(text(t(locale, "settings.browse")).size(12))
|
||||
.on_press(Message::Settings(SettingsMsg::BrowseDataPath))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 12]),
|
||||
button(text(t(locale, "settings.reset")).size(12))
|
||||
keyboard::button(text(t(locale, "settings.reset")).size(12))
|
||||
.on_press(Message::Settings(SettingsMsg::ResetDataPath))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 12]),
|
||||
@@ -559,11 +594,11 @@ fn section_project<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
||||
|row| Message::Settings(SettingsMsg::BlogmarkCategoryChanged(row.name)),
|
||||
);
|
||||
let copy_blogmark_bookmarklet =
|
||||
button(text(t(locale, "settings.copyBlogmarkBookmarklet")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.copyBlogmarkBookmarklet")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::CopyBlogmarkBookmarklet))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16]);
|
||||
let save = button(text(t(locale, "common.save")).size(13))
|
||||
let save = keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::SaveProject))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]);
|
||||
@@ -619,7 +654,7 @@ fn section_editor<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element
|
||||
state.hide_unchanged_regions,
|
||||
|b| Message::Settings(SettingsMsg::HideUnchangedRegionsChanged(b)),
|
||||
);
|
||||
let save = button(text(t(locale, "common.save")).size(13))
|
||||
let save = keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::SaveEditor))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]);
|
||||
@@ -656,11 +691,11 @@ fn section_ai<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a,
|
||||
]
|
||||
.spacing(4);
|
||||
let btns = row![
|
||||
button(text(t(locale, "common.save")).size(13))
|
||||
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::SaveAi))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]),
|
||||
button(text(t(locale, "settings.resetToDefault")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.resetToDefault")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::ResetSystemPrompt))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16]),
|
||||
@@ -727,7 +762,7 @@ fn ai_mode_block<'a>(
|
||||
Message::Settings(SettingsMsg::AiEndpointUrlChanged(kind, value))
|
||||
}
|
||||
),
|
||||
button(text(t(locale, "settings.refreshModels")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.refreshModels")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RefreshAiModels(kind)))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16]),
|
||||
@@ -772,7 +807,7 @@ fn ai_mode_block<'a>(
|
||||
state.image_supports_vision,
|
||||
move |value| Message::Settings(SettingsMsg::AiVisionChanged(kind, value)),
|
||||
),
|
||||
button(text(t(locale, "settings.testChat")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.testChat")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::TestAi(kind)))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16]),
|
||||
@@ -815,11 +850,11 @@ fn section_publishing<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Ele
|
||||
|s| Message::Settings(SettingsMsg::SshRemotePathChanged(s)),
|
||||
);
|
||||
let btns = row![
|
||||
button(text(t(locale, "common.save")).size(13))
|
||||
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::SavePublishing))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]),
|
||||
button(text(t(locale, "settings.clear")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.clear")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::ClearPublishing))
|
||||
.style(inputs::danger_button)
|
||||
.padding([6, 16]),
|
||||
@@ -834,37 +869,37 @@ fn section_publishing<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Ele
|
||||
|
||||
fn section_data<'a>(locale: UiLocale) -> Element<'a, Message> {
|
||||
let rebuild_btns = column![
|
||||
button(text(t(locale, "settings.rebuildPosts")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.rebuildPosts")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RebuildPosts))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "settings.rebuildMedia")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.rebuildMedia")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RebuildMedia))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "settings.rebuildScripts")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.rebuildScripts")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RebuildScripts))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "settings.rebuildTemplates")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.rebuildTemplates")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RebuildTemplates))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "settings.rebuildLinks")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.rebuildLinks")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RebuildLinks))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "settings.rebuildSearchIndex")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.rebuildSearchIndex")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RebuildSearchIndex))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "settings.regenerateThumbnails")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.regenerateThumbnails")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::RegenerateThumbnails))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
@@ -872,12 +907,12 @@ fn section_data<'a>(locale: UiLocale) -> Element<'a, Message> {
|
||||
]
|
||||
.spacing(4);
|
||||
|
||||
let open = button(text(t(locale, "settings.openDataFolder")).size(13))
|
||||
let open = keyboard::button(text(t(locale, "settings.openDataFolder")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::OpenDataFolder))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16]);
|
||||
|
||||
let install_cli = button(text(t(locale, "settings.installCli")).size(13))
|
||||
let install_cli = keyboard::button(text(t(locale, "settings.installCli")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::InstallCli))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16]);
|
||||
@@ -900,14 +935,17 @@ fn section_mcp<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a
|
||||
inputs::LABEL_COLOR
|
||||
};
|
||||
let server = column![
|
||||
iced::widget::checkbox(t(locale, "settings.mcpEnable"), state.mcp_enabled)
|
||||
.on_toggle(|value| Message::Settings(SettingsMsg::McpEnabledChanged(value))),
|
||||
keyboard::focusable(
|
||||
iced::widget::checkbox(t(locale, "settings.mcpEnable"), state.mcp_enabled)
|
||||
.on_toggle(|value| Message::Settings(SettingsMsg::McpEnabledChanged(value))),
|
||||
true,
|
||||
),
|
||||
row![
|
||||
text(status).size(13).color(status_color),
|
||||
text(state.mcp_endpoint.clone())
|
||||
.size(12)
|
||||
.color(inputs::LABEL_COLOR),
|
||||
button(text(t(locale, "settings.mcpRefresh")).size(12))
|
||||
keyboard::button(text(t(locale, "settings.mcpRefresh")).size(12))
|
||||
.on_press(Message::Settings(SettingsMsg::McpRefresh))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([5, 10]),
|
||||
@@ -940,13 +978,13 @@ fn section_mcp<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a
|
||||
]
|
||||
.spacing(2)
|
||||
.width(Length::Fill),
|
||||
button(text(t(locale, "settings.mcpApprove")).size(12))
|
||||
keyboard::button(text(t(locale, "settings.mcpApprove")).size(12))
|
||||
.on_press(Message::Settings(SettingsMsg::McpProposalAccepted(
|
||||
proposal.id.clone()
|
||||
)))
|
||||
.style(inputs::primary_button)
|
||||
.padding([5, 10]),
|
||||
button(text(t(locale, "settings.mcpReject")).size(12))
|
||||
keyboard::button(text(t(locale, "settings.mcpReject")).size(12))
|
||||
.on_press(Message::Settings(SettingsMsg::McpProposalRejected(
|
||||
proposal.id.clone()
|
||||
)))
|
||||
@@ -964,10 +1002,13 @@ fn section_mcp<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a
|
||||
let agents = column(state.mcp_agents.iter().map(|agent| {
|
||||
let configured = agent.configured;
|
||||
column![
|
||||
iced::widget::checkbox(agent.label.clone(), configured).on_toggle({
|
||||
let agent = agent.agent;
|
||||
move |_| Message::Settings(SettingsMsg::McpAgentToggled(agent))
|
||||
}),
|
||||
keyboard::focusable(
|
||||
iced::widget::checkbox(agent.label.clone(), configured).on_toggle({
|
||||
let agent = agent.agent;
|
||||
move |_| Message::Settings(SettingsMsg::McpAgentToggled(agent))
|
||||
}),
|
||||
true,
|
||||
),
|
||||
text(agent.config_path.clone())
|
||||
.size(11)
|
||||
.color(inputs::LABEL_COLOR),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, column, container, image, row, scrollable, text, text_input};
|
||||
use iced::widget::{Space, column, container, image, row, scrollable, text, text_input};
|
||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -301,10 +303,10 @@ fn row_delete_style(_theme: &Theme, status: button::Status) -> button::Style {
|
||||
/// Per sidebar_views.allium *ListItemEntry RowLayout: right-aligned, visible
|
||||
/// only on row hover, routed to the row's delete message.
|
||||
fn with_row_delete(
|
||||
open_button: iced::widget::Button<'static, Message>,
|
||||
open_button: impl Into<Element<'static, Message>>,
|
||||
on_delete: Message,
|
||||
) -> Element<'static, Message> {
|
||||
let delete_button = button(
|
||||
let delete_button = keyboard::button(
|
||||
text("\u{2715}") // ✕
|
||||
.size(11)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -313,7 +315,7 @@ fn with_row_delete(
|
||||
.padding([2, 6])
|
||||
.style(row_delete_style);
|
||||
iced::widget::hover(
|
||||
open_button,
|
||||
open_button.into(),
|
||||
container(delete_button)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
@@ -362,7 +364,7 @@ fn calendar_widget(
|
||||
} else {
|
||||
calendar_style
|
||||
};
|
||||
let year_btn = button(text(label).size(11).shaping(Shaping::Advanced))
|
||||
let year_btn = keyboard::button(text(label).size(11).shaping(Shaping::Advanced))
|
||||
.on_press(if year_selected {
|
||||
on_year_clone(None)
|
||||
} else {
|
||||
@@ -384,7 +386,7 @@ fn calendar_widget(
|
||||
} else {
|
||||
calendar_style
|
||||
};
|
||||
let month_btn = button(text(label).size(10).shaping(Shaping::Advanced))
|
||||
let month_btn = keyboard::button(text(label).size(10).shaping(Shaping::Advanced))
|
||||
.on_press(if month_selected {
|
||||
on_month_clone(None)
|
||||
} else {
|
||||
@@ -428,7 +430,7 @@ fn chip_selector(
|
||||
} else {
|
||||
chip_style
|
||||
};
|
||||
let chip = button(text(tag.clone()).size(10).shaping(Shaping::Advanced))
|
||||
let chip = keyboard::button(text(tag.clone()).size(10).shaping(Shaping::Advanced))
|
||||
.on_press(on_toggle_clone(tag_clone))
|
||||
.padding([2, 6])
|
||||
.style(style_fn);
|
||||
@@ -476,7 +478,7 @@ fn single_select_chip_selector(
|
||||
};
|
||||
let value = value.clone();
|
||||
let on_toggle = on_toggle.clone();
|
||||
button(text(display.clone()).size(10).shaping(Shaping::Advanced))
|
||||
keyboard::button(text(display.clone()).size(10).shaping(Shaping::Advanced))
|
||||
.on_press(if is_selected {
|
||||
on_toggle(None)
|
||||
} else {
|
||||
@@ -599,7 +601,7 @@ fn post_filter_panel(
|
||||
// Clear all filters button
|
||||
if filter.has_active_filters() {
|
||||
sections.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "sidebar.filter.clearAll"))
|
||||
.size(10)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -645,7 +647,7 @@ fn media_filter_panel(filter: &MediaFilter, locale: UiLocale) -> Element<'static
|
||||
|
||||
if filter.has_active_filters() {
|
||||
sections.push(
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "sidebar.filter.clearAll"))
|
||||
.size(10)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -711,7 +713,7 @@ pub fn view(
|
||||
row![
|
||||
header,
|
||||
Space::with_width(Length::Fill),
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "common.add"))
|
||||
.size(11)
|
||||
.shaping(Shaping::Advanced)
|
||||
@@ -752,10 +754,11 @@ pub fn view(
|
||||
} else {
|
||||
"\u{25BC}" // ▼ toggle icon
|
||||
};
|
||||
let filter_toggle = button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
||||
.on_press(Message::TogglePostFilterPanel)
|
||||
.padding([4, 6])
|
||||
.style(toggle_style);
|
||||
let filter_toggle =
|
||||
keyboard::button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
||||
.on_press(Message::TogglePostFilterPanel)
|
||||
.padding([4, 6])
|
||||
.style(toggle_style);
|
||||
|
||||
top_items.push(row![search, filter_toggle].spacing(4).into());
|
||||
|
||||
@@ -817,7 +820,7 @@ pub fn view(
|
||||
} else {
|
||||
item_style
|
||||
};
|
||||
button(
|
||||
keyboard::button(
|
||||
container(column![label_text, date_text].spacing(1))
|
||||
.width(Length::Fill)
|
||||
.clip(true),
|
||||
@@ -901,10 +904,11 @@ pub fn view(
|
||||
} else {
|
||||
"\u{25BC}" // ▼ toggle icon
|
||||
};
|
||||
let filter_toggle = button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
||||
.on_press(Message::ToggleMediaFilterPanel)
|
||||
.padding([4, 6])
|
||||
.style(toggle_style);
|
||||
let filter_toggle =
|
||||
keyboard::button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
||||
.on_press(Message::ToggleMediaFilterPanel)
|
||||
.padding([4, 6])
|
||||
.style(toggle_style);
|
||||
|
||||
top_items.push(row![search, filter_toggle].spacing(4).into());
|
||||
|
||||
@@ -995,7 +999,7 @@ pub fn view(
|
||||
.spacing(8)
|
||||
.align_y(iced::Alignment::Center);
|
||||
|
||||
button(container(content).width(Length::Fill).clip(true))
|
||||
keyboard::button(container(content).width(Length::Fill).clip(true))
|
||||
.on_press(Message::OpenTab(Tab {
|
||||
id: m.id.clone(),
|
||||
tab_type: TabType::Media,
|
||||
@@ -1046,7 +1050,7 @@ pub fn view(
|
||||
} else {
|
||||
item_style
|
||||
};
|
||||
let open_button = button(
|
||||
let open_button = keyboard::button(
|
||||
container(column![label_text, date_text].spacing(1))
|
||||
.width(Length::Fill)
|
||||
.clip(true),
|
||||
@@ -1097,7 +1101,7 @@ pub fn view(
|
||||
} else {
|
||||
item_style
|
||||
};
|
||||
let open_button = button(
|
||||
let open_button = keyboard::button(
|
||||
container(column![label_text, date_text].spacing(1))
|
||||
.width(Length::Fill)
|
||||
.clip(true),
|
||||
@@ -1145,7 +1149,7 @@ pub fn view(
|
||||
.size(10)
|
||||
.shaping(Shaping::Advanced)
|
||||
.color(muted);
|
||||
let open_button = button(
|
||||
let open_button = keyboard::button(
|
||||
container(
|
||||
column![
|
||||
text(definition_name).size(12).shaping(Shaping::Advanced),
|
||||
@@ -1197,7 +1201,7 @@ pub fn view(
|
||||
.size(10)
|
||||
.shaping(Shaping::Advanced)
|
||||
.color(muted);
|
||||
let open_button = button(
|
||||
let open_button = keyboard::button(
|
||||
container(
|
||||
column![
|
||||
text(conversation.title.clone())
|
||||
@@ -1257,7 +1261,7 @@ pub fn view(
|
||||
is_dirty: false,
|
||||
})
|
||||
};
|
||||
button(container(label_text).width(Length::Fill))
|
||||
keyboard::button(container(label_text).width(Length::Fill))
|
||||
.on_press(msg)
|
||||
.padding([5, 8])
|
||||
.width(Length::Fill)
|
||||
@@ -1291,7 +1295,7 @@ pub fn view(
|
||||
.map(|(key, section)| {
|
||||
let label = t(locale, key);
|
||||
let label_text = text(label).size(12).shaping(Shaping::Advanced);
|
||||
button(container(label_text).width(Length::Fill))
|
||||
keyboard::button(container(label_text).width(Length::Fill))
|
||||
.on_press(Message::OpenTagsSection(*section))
|
||||
.padding([5, 8])
|
||||
.width(Length::Fill)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{button, column, container, row, scrollable, text};
|
||||
use iced::widget::{column, container, row, scrollable, text};
|
||||
use iced::{Background, Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -21,7 +22,7 @@ pub struct SiteValidationState {
|
||||
|
||||
pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a, Message> {
|
||||
let run_button = if state.is_running {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "siteValidation.running"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -29,12 +30,13 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16])
|
||||
} else {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "siteValidation.run"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
)
|
||||
.on_press(Message::RunSiteValidation)
|
||||
.hotkey('r')
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16])
|
||||
};
|
||||
@@ -42,7 +44,7 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
||||
|| !state.extra_files.is_empty()
|
||||
|| !state.stale_files.is_empty();
|
||||
let apply_button = if state.is_applying {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "siteValidation.applying"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
@@ -50,16 +52,17 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
} else if !state.is_running && state.error_message.is_none() && has_issues {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "siteValidation.apply"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
)
|
||||
.on_press(Message::ApplySiteValidation)
|
||||
.hotkey('a')
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16])
|
||||
} else {
|
||||
button(
|
||||
keyboard::button(
|
||||
text(t(locale, "siteValidation.apply"))
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, container, row, text};
|
||||
use iced::widget::{Space, container, row, text};
|
||||
use iced::{Alignment, Background, Border, Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::engine::task::TaskStatus;
|
||||
@@ -235,7 +237,7 @@ pub fn view(
|
||||
);
|
||||
|
||||
// Airplane mode toggle — ✈ icon
|
||||
let airplane_btn = button(text("\u{2708}").size(13).shaping(Shaping::Advanced))
|
||||
let airplane_btn = keyboard::button(text("\u{2708}").size(13).shaping(Shaping::Advanced))
|
||||
.on_press(Message::SetOfflineMode(!offline_mode))
|
||||
.padding([2, 4])
|
||||
.style(if offline_mode {
|
||||
@@ -249,7 +251,7 @@ pub fn view(
|
||||
.size(14)
|
||||
.shaping(Shaping::Advanced);
|
||||
|
||||
let locale_trigger = button(trigger_flag)
|
||||
let locale_trigger = keyboard::button(trigger_flag)
|
||||
.on_press(Message::ToggleLocaleDropdown)
|
||||
.padding([1, 4])
|
||||
.style(dropdown_trigger);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Column, Space, button, column, container, row, scrollable, text};
|
||||
use iced::widget::{Column, Space, column, container, row, scrollable, text};
|
||||
use iced::{Background, Border, Color, Element, Length, Radians, Theme, gradient};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -220,7 +222,7 @@ fn swatch(background: Background, width_portion: u16) -> Element<'static, Messag
|
||||
fn theme_button<'a>(theme: &StyleTheme, selected_theme: &str) -> Element<'a, Message> {
|
||||
let selected = theme.name == selected_theme;
|
||||
let theme_name = theme.name.to_string();
|
||||
button(
|
||||
keyboard::button(
|
||||
column![
|
||||
row![
|
||||
swatch(theme_accent_background(theme), 2),
|
||||
@@ -318,13 +320,13 @@ pub fn view<'a>(
|
||||
.size(13)
|
||||
.shaping(Shaping::Advanced);
|
||||
let apply_button: Element<'a, Message> = if state.can_apply() {
|
||||
button(apply_label)
|
||||
keyboard::button(apply_label)
|
||||
.on_press(Message::Style(StyleMsg::Apply))
|
||||
.padding([8, 16])
|
||||
.style(inputs::primary_button)
|
||||
.into()
|
||||
} else {
|
||||
button(apply_label)
|
||||
keyboard::button(apply_label)
|
||||
.padding([8, 16])
|
||||
.style(inputs::primary_button)
|
||||
.into()
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use iced::widget::scrollable::Direction;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::tooltip::Position;
|
||||
use iced::widget::{Space, button, container, row, scrollable, text, tooltip};
|
||||
use iced::widget::{Space, container, row, scrollable, text, tooltip};
|
||||
use iced::{Background, Border, Color, Element, Font, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -177,9 +179,20 @@ pub fn view(tabs: &[Tab], active_tab: Option<&str>, locale: UiLocale) -> Element
|
||||
.width(Length::Fill)
|
||||
.clip(true);
|
||||
|
||||
// Keep the tooltip around non-interactive title content so it does
|
||||
// not hide the tab and close controls from widget operations.
|
||||
let tooltip_text = build_tooltip_text(tab, locale);
|
||||
let title_area = tooltip(
|
||||
title_area,
|
||||
text(tooltip_text).size(11).shaping(Shaping::Advanced),
|
||||
Position::Bottom,
|
||||
)
|
||||
.gap(4)
|
||||
.style(inputs::tooltip_style);
|
||||
|
||||
let label = row![
|
||||
title_area,
|
||||
button(text("\u{2715}").size(10).shaping(Shaping::Advanced))
|
||||
keyboard::button(text("\u{2715}").size(10).shaping(Shaping::Advanced))
|
||||
.on_press(Message::CloseTab(close_id))
|
||||
.padding(2)
|
||||
.style(close_style),
|
||||
@@ -188,24 +201,12 @@ pub fn view(tabs: &[Tab], active_tab: Option<&str>, locale: UiLocale) -> Element
|
||||
.align_y(iced::Alignment::Center);
|
||||
|
||||
// tabs.allium: tab_min_width=100, tab_max_width=160
|
||||
let tab_btn = button(label)
|
||||
keyboard::button(label)
|
||||
.on_press(Message::SelectTab(tab_id))
|
||||
.padding([6, 8])
|
||||
.width(Length::Fixed(TAB_WIDTH))
|
||||
.style(if is_active { tab_active } else { tab_inactive });
|
||||
|
||||
// tabs.allium tooltip: title + "(Preview)" if transient + "* Modified" if dirty
|
||||
let tooltip_text = build_tooltip_text(tab, locale);
|
||||
let tip: Element<'static, Message> = tooltip(
|
||||
tab_btn,
|
||||
text(tooltip_text).size(11).shaping(Shaping::Advanced),
|
||||
Position::Bottom,
|
||||
)
|
||||
.gap(4)
|
||||
.style(inputs::tooltip_style)
|
||||
.into();
|
||||
|
||||
tip
|
||||
.style(if is_active { tab_active } else { tab_inactive })
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use iced::widget::{
|
||||
Space, button, checkbox, column, container, pick_list, row, scrollable, text, text_input,
|
||||
Space, checkbox, column, container, pick_list, row, scrollable, text, text_input,
|
||||
};
|
||||
use iced::{Alignment, Background, Color, Element, Length, Theme};
|
||||
|
||||
@@ -247,69 +249,79 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
||||
}));
|
||||
let name = category.name.clone();
|
||||
cells.push(
|
||||
container(
|
||||
container(keyboard::focusable(
|
||||
checkbox("", category.render_in_lists).on_toggle(move |value| {
|
||||
Message::Settings(SettingsMsg::CategoryRenderInListsChanged(
|
||||
name.clone(),
|
||||
value,
|
||||
))
|
||||
}),
|
||||
)
|
||||
true,
|
||||
))
|
||||
.width(Length::Fixed(TOGGLE_WIDTH))
|
||||
.into(),
|
||||
);
|
||||
let name = category.name.clone();
|
||||
cells.push(
|
||||
container(checkbox("", category.show_title).on_toggle(move |value| {
|
||||
Message::Settings(SettingsMsg::CategoryShowTitleChanged(name.clone(), value))
|
||||
}))
|
||||
container(keyboard::focusable(
|
||||
checkbox("", category.show_title).on_toggle(move |value| {
|
||||
Message::Settings(SettingsMsg::CategoryShowTitleChanged(name.clone(), value))
|
||||
}),
|
||||
true,
|
||||
))
|
||||
.width(Length::Fixed(TOGGLE_WIDTH))
|
||||
.into(),
|
||||
);
|
||||
let name = category.name.clone();
|
||||
cells.push(
|
||||
pick_list(
|
||||
template_options.clone(),
|
||||
Some(category.post_template_slug.clone()),
|
||||
move |value| {
|
||||
Message::Settings(SettingsMsg::CategoryPostTemplateChanged(
|
||||
name.clone(),
|
||||
value,
|
||||
))
|
||||
},
|
||||
keyboard::focusable(
|
||||
pick_list(
|
||||
template_options.clone(),
|
||||
Some(category.post_template_slug.clone()),
|
||||
move |value| {
|
||||
Message::Settings(SettingsMsg::CategoryPostTemplateChanged(
|
||||
name.clone(),
|
||||
value,
|
||||
))
|
||||
},
|
||||
)
|
||||
.padding([7, 9])
|
||||
.style(inputs::select_style)
|
||||
.width(Length::Fixed(TEMPLATE_WIDTH)),
|
||||
true,
|
||||
)
|
||||
.padding([7, 9])
|
||||
.style(inputs::select_style)
|
||||
.width(Length::Fixed(TEMPLATE_WIDTH))
|
||||
.into(),
|
||||
);
|
||||
let name = category.name.clone();
|
||||
cells.push(
|
||||
pick_list(
|
||||
template_options.clone(),
|
||||
Some(category.list_template_slug.clone()),
|
||||
move |value| {
|
||||
Message::Settings(SettingsMsg::CategoryListTemplateChanged(
|
||||
name.clone(),
|
||||
value,
|
||||
))
|
||||
},
|
||||
keyboard::focusable(
|
||||
pick_list(
|
||||
template_options.clone(),
|
||||
Some(category.list_template_slug.clone()),
|
||||
move |value| {
|
||||
Message::Settings(SettingsMsg::CategoryListTemplateChanged(
|
||||
name.clone(),
|
||||
value,
|
||||
))
|
||||
},
|
||||
)
|
||||
.padding([7, 9])
|
||||
.style(inputs::select_style)
|
||||
.width(Length::Fixed(TEMPLATE_WIDTH)),
|
||||
true,
|
||||
)
|
||||
.padding([7, 9])
|
||||
.style(inputs::select_style)
|
||||
.width(Length::Fixed(TEMPLATE_WIDTH))
|
||||
.into(),
|
||||
);
|
||||
cells.push(
|
||||
container(
|
||||
row![
|
||||
button(text(t(locale, "common.save")).size(12))
|
||||
keyboard::button(text(t(locale, "common.save")).size(12))
|
||||
.on_press(Message::Settings(SettingsMsg::SaveCategory(
|
||||
category.name.clone(),
|
||||
)))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 10]),
|
||||
button(text(t(locale, "common.remove")).size(12))
|
||||
keyboard::button(text(t(locale, "common.remove")).size(12))
|
||||
.on_press_maybe((!category.is_protected).then(|| Message::Settings(
|
||||
SettingsMsg::RemoveCategory(category.name.clone()),
|
||||
)))
|
||||
@@ -336,11 +348,11 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
||||
&state.new_category_name,
|
||||
|value| Message::Settings(SettingsMsg::AddCategoryNameChanged(value)),
|
||||
),
|
||||
button(text(t(locale, "common.add")).size(13))
|
||||
keyboard::button(text(t(locale, "common.add")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::AddCategory))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 12]),
|
||||
button(text(t(locale, "settings.resetCategories")).size(13))
|
||||
keyboard::button(text(t(locale, "settings.resetCategories")).size(13))
|
||||
.on_press(Message::Settings(SettingsMsg::ResetCategoriesToDefaults))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 12]),
|
||||
@@ -382,7 +394,7 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
||||
}
|
||||
|
||||
fn section_tab<'a>(label: &str, active: bool, section: TagsSection) -> Element<'a, Message> {
|
||||
button(text(label.to_string()).size(13))
|
||||
keyboard::button(text(label.to_string()).size(13))
|
||||
.on_press(Message::Tags(TagsMsg::SetSection(section)))
|
||||
.padding([6, 12])
|
||||
.style(if active {
|
||||
@@ -443,7 +455,7 @@ fn view_cloud<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Mes
|
||||
.selected_tags
|
||||
.iter()
|
||||
.any(|selected_id| selected_id == &tag.id);
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
text(&tag.name).size(font_size).color(Color::WHITE),
|
||||
text(post_count.to_string())
|
||||
@@ -488,7 +500,7 @@ fn view_cloud<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Mes
|
||||
))
|
||||
.size(12)
|
||||
.color(Color::from_rgb(0.75, 0.77, 0.82)),
|
||||
button(text(t(locale, "tags.clearSelection")).size(12))
|
||||
keyboard::button(text(t(locale, "tags.clearSelection")).size(12))
|
||||
.on_press(Message::Tags(TagsMsg::ClearSelection))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([4, 8]),
|
||||
@@ -533,7 +545,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
||||
|value| Message::Tags(TagsMsg::CreateColorChanged(value)),
|
||||
),
|
||||
color_swatches(locale, true),
|
||||
button(text(t(locale, "tags.createButton")).size(13))
|
||||
keyboard::button(text(t(locale, "tags.createButton")).size(13))
|
||||
.on_press_maybe(
|
||||
(!state.create_name.trim().is_empty()).then_some(Message::Tags(TagsMsg::CreateTag))
|
||||
)
|
||||
@@ -573,7 +585,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
||||
.get(&tag.name.to_lowercase())
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
button(
|
||||
keyboard::button(
|
||||
row![
|
||||
container(Space::new(12, 12)).style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(color)),
|
||||
@@ -620,7 +632,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
||||
.iter()
|
||||
.find(|option| option.slug == editing.template_slug);
|
||||
let delete_button: Element<'a, Message> =
|
||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
.on_press(Message::Tags(TagsMsg::DeleteTag(editing.id.clone())))
|
||||
.style(inputs::danger_button)
|
||||
.padding([6, 16])
|
||||
@@ -645,7 +657,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
||||
|choice| Message::Tags(TagsMsg::EditTagTemplate(choice)),
|
||||
),
|
||||
row![
|
||||
button(text(t(locale, "common.save")).size(13))
|
||||
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::Tags(TagsMsg::SaveTag))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]),
|
||||
@@ -744,7 +756,7 @@ fn view_merge<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Mes
|
||||
.padding([4, 0])
|
||||
.into()
|
||||
},
|
||||
button(text(t(locale, "tags.merge")).size(13))
|
||||
keyboard::button(text(t(locale, "tags.merge")).size(13))
|
||||
.on_press_maybe(
|
||||
state
|
||||
.merge_target
|
||||
@@ -768,7 +780,7 @@ fn view_discover<'a>(_state: &'a TagsViewState, locale: UiLocale) -> Element<'a,
|
||||
text(t(locale, "tags.discoverDescription"))
|
||||
.size(12)
|
||||
.color(Color::from_rgb(0.60, 0.60, 0.65)),
|
||||
button(text(t(locale, "tags.discoverButton")).size(13))
|
||||
keyboard::button(text(t(locale, "tags.discoverButton")).size(13))
|
||||
.on_press(Message::Tags(TagsMsg::SyncTags))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]),
|
||||
@@ -819,7 +831,7 @@ fn color_swatches<'a>(locale: UiLocale, create_mode: bool) -> Element<'a, Messag
|
||||
} else {
|
||||
TagsMsg::EditTagColor((*hex).to_string())
|
||||
};
|
||||
button(Space::new(18, 18))
|
||||
keyboard::button(Space::new(18, 18))
|
||||
.on_press(Message::Tags(msg))
|
||||
.padding(0)
|
||||
.style(move |_theme: &Theme, _status| button::Style {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
||||
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||
use iced::{Color, Element, Length, Theme};
|
||||
|
||||
use bds_core::i18n::UiLocale;
|
||||
@@ -114,17 +115,17 @@ pub fn view<'a>(state: &'a TemplateEditorState, locale: UiLocale) -> Element<'a,
|
||||
status_badge(&state.status),
|
||||
],
|
||||
vec![
|
||||
button(text(t(locale, "common.save")).size(13))
|
||||
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||
.on_press(Message::TemplateEditor(TemplateEditorMsg::Save))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16])
|
||||
.into(),
|
||||
button(text(t(locale, "editor.validate")).size(13))
|
||||
keyboard::button(text(t(locale, "editor.validate")).size(13))
|
||||
.on_press(Message::TemplateEditor(TemplateEditorMsg::Validate))
|
||||
.style(inputs::secondary_button)
|
||||
.padding([6, 16])
|
||||
.into(),
|
||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||
.on_press(Message::TemplateEditor(TemplateEditorMsg::Delete))
|
||||
.style(inputs::danger_button)
|
||||
.padding([6, 16])
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::button;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, container, row, text};
|
||||
use iced::widget::{Space, container, row, text};
|
||||
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
@@ -62,7 +64,7 @@ pub fn view(toasts: &[Toast]) -> Option<Element<'static, Message>> {
|
||||
.iter()
|
||||
.map(|toast| {
|
||||
let level = toast.level;
|
||||
let dismiss = button(text("\u{2715}").size(11).shaping(Shaping::Advanced))
|
||||
let dismiss = keyboard::button(text("\u{2715}").size(11).shaping(Shaping::Advanced))
|
||||
.on_press(Message::DismissToast(toast.id))
|
||||
.padding([2, 4])
|
||||
.style(dismiss_btn);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::components::keyboard;
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
||||
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||
use iced::{Color, Element, Length};
|
||||
|
||||
use bds_core::engine::validate_translations::{
|
||||
@@ -19,7 +20,7 @@ pub struct TranslationValidationState {
|
||||
}
|
||||
|
||||
pub fn view<'a>(state: &'a TranslationValidationState, locale: UiLocale) -> Element<'a, Message> {
|
||||
let run = button(text(t(locale, "translationValidation.run")).size(13))
|
||||
let run = keyboard::button(text(t(locale, "translationValidation.run")).size(13))
|
||||
.on_press_maybe((!state.is_running).then_some(Message::ValidateTranslations))
|
||||
.style(inputs::primary_button)
|
||||
.padding([6, 16]);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::components::keyboard;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use iced::widget::text::Shaping;
|
||||
use iced::widget::{Space, button, column, container, mouse_area, row, stack, text};
|
||||
use iced::widget::{Space, column, container, mouse_area, row, stack, text};
|
||||
use iced::{Alignment, Background, Color, Element, Length, Padding, Theme};
|
||||
|
||||
use bds_core::engine::git::GitCommit;
|
||||
@@ -305,7 +306,7 @@ pub fn view<'a>(
|
||||
.map(|&l| {
|
||||
let flag_text = text(l.flag_emoji()).size(16).shaping(Shaping::Advanced);
|
||||
|
||||
button(flag_text)
|
||||
keyboard::button(flag_text)
|
||||
.on_press(Message::SetUiLocale(l))
|
||||
.padding([4, 8])
|
||||
.style(status_bar::dropdown_item)
|
||||
@@ -371,15 +372,32 @@ pub fn view<'a>(
|
||||
None
|
||||
};
|
||||
|
||||
// Only the top blocking layer participates in keyboard traversal/access keys.
|
||||
let modal_is_active = active_modal.is_some();
|
||||
let blocking_overlay = modal_is_active || overlay.is_some();
|
||||
let base_layout: Element<'a, Message> = if blocking_overlay {
|
||||
keyboard::suspend(base_layout).into()
|
||||
} else {
|
||||
base_layout
|
||||
};
|
||||
|
||||
// Collect overlays: dropdowns and toasts
|
||||
let mut overlays: Vec<Element<'a, Message>> = Vec::new();
|
||||
|
||||
if let Some(toast_overlay) = toast::view(toasts) {
|
||||
overlays.push(toast_overlay);
|
||||
overlays.push(if blocking_overlay {
|
||||
keyboard::suspend(toast_overlay).into()
|
||||
} else {
|
||||
toast_overlay
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(overlay) = overlay {
|
||||
overlays.push(overlay);
|
||||
overlays.push(if modal_is_active {
|
||||
keyboard::suspend(overlay).into()
|
||||
} else {
|
||||
overlay
|
||||
});
|
||||
}
|
||||
|
||||
// Modal overlay (highest z-index)
|
||||
|
||||
Reference in New Issue
Block a user