feat: add word wrap toggle functionality
Add the ability to toggle word wrap in the text editor with Alt+Z. Word wrap can be enabled or disabled, affecting how long lines are displayed in edit mode. Changes: - Add `is_word_wrap_on` state field with default value of false - Implement `toggle_word_wrap()` and `is_word_wrap_on()` methods - Add ToggleWordWrap variant to ViewAction enum - Bind Alt+Z keyboard shortcut to toggle word wrap - Refactor editor view to support dynamic wrapping behavior - Use responsive widget when wrap is enabled to constrain width - Apply Wrapping::WordOrGlyph when enabled, Wrapping::None otherwise - Update editor view signature to accept word wrap state parameter - Fix indentation inconsistencies in file.rs and state.rs
This commit is contained in:
+63
-36
@@ -2,58 +2,85 @@ use iced::{
|
|||||||
Background, Element, Length, Theme,
|
Background, Element, Length, Theme,
|
||||||
border::{self},
|
border::{self},
|
||||||
highlighter,
|
highlighter,
|
||||||
widget::{container, markdown, row, scrollable, text_editor},
|
widget::{container, markdown, responsive, row, scrollable, text::Wrapping, text_editor},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{constants, file::File, message::Message, state::Mode};
|
use crate::{constants, file::File, message::Message, state::Mode};
|
||||||
|
|
||||||
pub fn view<'a>(file: &'a File, mode: Mode, font_size: u32) -> Element<'a, Message> {
|
pub fn view<'a>(
|
||||||
|
file: &'a File,
|
||||||
|
mode: Mode,
|
||||||
|
font_size: u32,
|
||||||
|
is_word_wrap_on: bool,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
match mode {
|
match mode {
|
||||||
Mode::Edit => {
|
Mode::Edit => {
|
||||||
let editor = text_editor(file.content())
|
let wrapping = if is_word_wrap_on {
|
||||||
.highlight(
|
Wrapping::WordOrGlyph
|
||||||
file.extension().unwrap_or("txt"),
|
} else {
|
||||||
highlighter::Theme::Base16Ocean,
|
Wrapping::None
|
||||||
)
|
};
|
||||||
.padding(10)
|
|
||||||
.size(font_size)
|
|
||||||
.style(|theme: &Theme, status: text_editor::Status| {
|
|
||||||
let base = text_editor::default(theme, status);
|
|
||||||
|
|
||||||
text_editor::Style {
|
let create_editor = move || {
|
||||||
border: border::Border {
|
text_editor(file.content())
|
||||||
width: 0.0,
|
.wrapping(wrapping)
|
||||||
..Default::default()
|
.highlight(
|
||||||
},
|
file.extension().unwrap_or("txt"),
|
||||||
..base
|
highlighter::Theme::Base16Ocean,
|
||||||
}
|
)
|
||||||
})
|
.padding(10)
|
||||||
.on_action(Message::Edit);
|
.size(font_size)
|
||||||
|
.style(|theme: &Theme, status: text_editor::Status| {
|
||||||
|
let base = text_editor::default(theme, status);
|
||||||
|
|
||||||
scrollable(container(editor).height(Length::Fill))
|
text_editor::Style {
|
||||||
.auto_scroll(true)
|
border: border::Border {
|
||||||
.direction(scrollable::Direction::Both {
|
width: 0.0,
|
||||||
vertical: scrollable::Scrollbar::default(),
|
..Default::default()
|
||||||
horizontal: scrollable::Scrollbar::default(),
|
},
|
||||||
|
..base
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_action(Message::Edit)
|
||||||
|
};
|
||||||
|
|
||||||
|
let create_scrollable = |content: Element<'a, Message>| {
|
||||||
|
scrollable(content)
|
||||||
|
.auto_scroll(true)
|
||||||
|
.direction(scrollable::Direction::Both {
|
||||||
|
vertical: scrollable::Scrollbar::default(),
|
||||||
|
horizontal: scrollable::Scrollbar::default(),
|
||||||
|
})
|
||||||
|
.style(|theme: &Theme, status: scrollable::Status| {
|
||||||
|
let mut style = scrollable::default(theme, status);
|
||||||
|
style.horizontal_rail.background = Some(Background::Color(theme.palette().background));
|
||||||
|
style.horizontal_rail.scroller.background = Background::Color(theme.palette().primary);
|
||||||
|
style.vertical_rail.background = Some(Background::Color(theme.palette().background));
|
||||||
|
style.vertical_rail.scroller.background = Background::Color(theme.palette().primary);
|
||||||
|
style
|
||||||
|
})
|
||||||
|
.anchor_bottom()
|
||||||
|
.height(Length::Fill)
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_word_wrap_on {
|
||||||
|
responsive(move |size| {
|
||||||
|
let editor = create_editor();
|
||||||
|
create_scrollable(container(editor).width(size.width).into()).into()
|
||||||
})
|
})
|
||||||
.style(|theme: &Theme, status: scrollable::Status| {
|
|
||||||
let mut style = scrollable::default(theme, status);
|
|
||||||
style.horizontal_rail.background = Some(Background::Color(theme.palette().background));
|
|
||||||
style.horizontal_rail.scroller.background = Background::Color(theme.palette().primary);
|
|
||||||
style.vertical_rail.background = Some(Background::Color(theme.palette().background));
|
|
||||||
style.vertical_rail.scroller.background = Background::Color(theme.palette().primary);
|
|
||||||
style
|
|
||||||
})
|
|
||||||
.anchor_bottom()
|
|
||||||
.height(Length::Fill)
|
|
||||||
.into()
|
.into()
|
||||||
|
} else {
|
||||||
|
let editor = create_editor();
|
||||||
|
create_scrollable(container(editor).height(Length::Fill).into())
|
||||||
|
.width(Length::Fill)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Mode::Preview => {
|
Mode::Preview => {
|
||||||
let mut style: markdown::Style = Theme::Dark.into();
|
let mut style: markdown::Style = Theme::Dark.into();
|
||||||
style.font = constants::CUSTOM_FONT;
|
style.font = constants::CUSTOM_FONT;
|
||||||
|
|
||||||
let settings = markdown::Settings::with_text_size(font_size, style);
|
let settings = markdown::Settings::with_text_size(font_size, style);
|
||||||
|
|
||||||
let markdown_preview = markdown::view(file.markdown(), settings).map(Message::LinkClicked);
|
let markdown_preview = markdown::view(file.markdown(), settings).map(Message::LinkClicked);
|
||||||
|
|
||||||
scrollable(row![markdown_preview].padding(10))
|
scrollable(row![markdown_preview].padding(10))
|
||||||
|
|||||||
+3
-3
@@ -94,11 +94,11 @@ impl File {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn needs_saving(&self) -> bool {
|
pub fn needs_saving(&self) -> bool {
|
||||||
self.needs_saving
|
self.needs_saving
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_needs_saving(&mut self, state: bool) {
|
pub fn set_needs_saving(&mut self, state: bool) {
|
||||||
self.needs_saving = state
|
self.needs_saving = state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ pub fn view_action(state: &mut State, action: ViewAction) -> Task<Message> {
|
|||||||
ViewAction::Decrease => state.decrease_font(),
|
ViewAction::Decrease => state.decrease_font(),
|
||||||
ViewAction::Reset => state.reset_font(),
|
ViewAction::Reset => state.reset_font(),
|
||||||
ViewAction::TogglePreview => state.toggle_preview(),
|
ViewAction::TogglePreview => state.toggle_preview(),
|
||||||
|
ViewAction::ToggleWordWrap => state.toggle_word_wrap(),
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
|
|||||||
@@ -65,4 +65,9 @@ pub const ALL: &[Keybinding] = &[
|
|||||||
modifiers: Modifiers::CTRL,
|
modifiers: Modifiers::CTRL,
|
||||||
message: Message::ViewActionSelected(ViewAction::Reset),
|
message: Message::ViewActionSelected(ViewAction::Reset),
|
||||||
},
|
},
|
||||||
|
Keybinding {
|
||||||
|
key: "z",
|
||||||
|
modifiers: Modifiers::ALT,
|
||||||
|
message: Message::ViewActionSelected(ViewAction::ToggleWordWrap),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
+7
-2
@@ -12,7 +12,7 @@ mod state;
|
|||||||
use iced::theme::Palette;
|
use iced::theme::Palette;
|
||||||
use iced::widget::column;
|
use iced::widget::column;
|
||||||
use iced::window::icon;
|
use iced::window::icon;
|
||||||
use iced::{Color, Theme};
|
use iced::{Color, Length, Theme};
|
||||||
use iced::{Element, Subscription, Task, event};
|
use iced::{Element, Subscription, Task, event};
|
||||||
use iced::{keyboard, window};
|
use iced::{keyboard, window};
|
||||||
|
|
||||||
@@ -84,7 +84,12 @@ fn view(state: &State, _id: iced::window::Id) -> Element<'_, Message> {
|
|||||||
column![
|
column![
|
||||||
components::tabs::view(state.files(), state.current_file_index()),
|
components::tabs::view(state.files(), state.current_file_index()),
|
||||||
components::action_bar::view(state.selected_file_action(), state.selected_view_action()),
|
components::action_bar::view(state.selected_file_action(), state.selected_view_action()),
|
||||||
components::editor::view(current_file, state.mode(), state.font_size()),
|
components::editor::view(
|
||||||
|
current_file,
|
||||||
|
state.mode(),
|
||||||
|
state.font_size(),
|
||||||
|
state.is_word_wrap_on()
|
||||||
|
),
|
||||||
components::status_bar::view(current_file),
|
components::status_bar::view(current_file),
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ pub enum ViewAction {
|
|||||||
Decrease,
|
Decrease,
|
||||||
Reset,
|
Reset,
|
||||||
TogglePreview,
|
TogglePreview,
|
||||||
|
ToggleWordWrap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewAction {
|
impl ViewAction {
|
||||||
@@ -47,6 +48,7 @@ impl ViewAction {
|
|||||||
ViewAction::Decrease,
|
ViewAction::Decrease,
|
||||||
ViewAction::Reset,
|
ViewAction::Reset,
|
||||||
ViewAction::TogglePreview,
|
ViewAction::TogglePreview,
|
||||||
|
ViewAction::ToggleWordWrap,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +59,7 @@ impl Display for ViewAction {
|
|||||||
ViewAction::Increase => write!(f, "Increase font"),
|
ViewAction::Increase => write!(f, "Increase font"),
|
||||||
ViewAction::Reset => write!(f, "Reset font"),
|
ViewAction::Reset => write!(f, "Reset font"),
|
||||||
ViewAction::TogglePreview => write!(f, "Toggle preview"),
|
ViewAction::TogglePreview => write!(f, "Toggle preview"),
|
||||||
|
ViewAction::ToggleWordWrap => write!(f, "Toggle word wrap"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -22,6 +22,7 @@ pub struct State {
|
|||||||
editor_font_size: u32,
|
editor_font_size: u32,
|
||||||
selected_file_action: Option<FileAction>,
|
selected_file_action: Option<FileAction>,
|
||||||
selected_view_action: Option<ViewAction>,
|
selected_view_action: Option<ViewAction>,
|
||||||
|
is_word_wrap_on: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@@ -33,6 +34,7 @@ impl State {
|
|||||||
files: vec![default_file],
|
files: vec![default_file],
|
||||||
current_file: 0,
|
current_file: 0,
|
||||||
editor_font_size: constants::DEFAULT_EDITOR_FONT_SIZE,
|
editor_font_size: constants::DEFAULT_EDITOR_FONT_SIZE,
|
||||||
|
is_word_wrap_on: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +44,9 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_edit(&mut self, action: text_editor::Action) {
|
pub fn apply_edit(&mut self, action: text_editor::Action) {
|
||||||
self.files[self.current_file].content_mut().perform(action.clone());
|
self.files[self.current_file]
|
||||||
|
.content_mut()
|
||||||
|
.perform(action.clone());
|
||||||
|
|
||||||
if matches!(action, text_editor::Action::Edit(_)) {
|
if matches!(action, text_editor::Action::Edit(_)) {
|
||||||
self.files[self.current_file].set_needs_saving(true);
|
self.files[self.current_file].set_needs_saving(true);
|
||||||
@@ -129,6 +133,14 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toggle_word_wrap(&mut self) {
|
||||||
|
self.is_word_wrap_on = !self.is_word_wrap_on;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_word_wrap_on(&self) -> bool {
|
||||||
|
self.is_word_wrap_on
|
||||||
|
}
|
||||||
|
|
||||||
pub fn files(&self) -> &[file::File] {
|
pub fn files(&self) -> &[file::File] {
|
||||||
&self.files
|
&self.files
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user