add markdown preview mode (Cmd+P), zoom in/out (Cmd+=/-/0), performKeyEquivalent forwarding
This commit is contained in:
parent
50fcb9224a
commit
95ecdf3c95
|
|
@ -11,7 +11,7 @@ swiftly-core = { path = "../core" }
|
||||||
iced_wgpu = "0.14"
|
iced_wgpu = "0.14"
|
||||||
iced_graphics = "0.14"
|
iced_graphics = "0.14"
|
||||||
iced_runtime = "0.14"
|
iced_runtime = "0.14"
|
||||||
iced_widget = { version = "0.14", features = ["wgpu"] }
|
iced_widget = { version = "0.14", features = ["wgpu", "markdown"] }
|
||||||
wgpu = "27"
|
wgpu = "27"
|
||||||
raw-window-handle = "0.6"
|
raw-window-handle = "0.6"
|
||||||
pollster = "0.4"
|
pollster = "0.4"
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,8 @@ pub fn push_key_event(
|
||||||
.unwrap_or(keyboard::Key::Unidentified)
|
.unwrap_or(keyboard::Key::Unidentified)
|
||||||
};
|
};
|
||||||
|
|
||||||
let insert_text = if named.is_some() {
|
let has_action_modifier = modifiers.logo() || modifiers.control();
|
||||||
|
let insert_text = if named.is_some() || has_action_modifier {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
text.filter(|s| !s.is_empty()).map(SmolStr::new)
|
text.filter(|s| !s.is_empty()).map(SmolStr::new)
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,43 @@
|
||||||
use iced_wgpu::core::keyboard;
|
use iced_wgpu::core::keyboard;
|
||||||
use iced_wgpu::core::keyboard::key;
|
use iced_wgpu::core::keyboard::key;
|
||||||
use iced_wgpu::core::text::Wrapping;
|
use iced_wgpu::core::text::{Highlight, Wrapping};
|
||||||
use iced_wgpu::core::{
|
use iced_wgpu::core::{
|
||||||
Background, Border, Color, Element, Font, Length, Padding, Shadow, Theme,
|
border, padding, Background, Border, Color, Element, Font, Length, Padding, Shadow, Theme,
|
||||||
};
|
};
|
||||||
use iced_widget::container;
|
use iced_widget::container;
|
||||||
|
use iced_widget::markdown;
|
||||||
use iced_widget::text_editor::{self, Binding, KeyPress, Motion, Status, Style};
|
use iced_widget::text_editor::{self, Binding, KeyPress, Motion, Status, Style};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
EditorAction(text_editor::Action),
|
EditorAction(text_editor::Action),
|
||||||
|
TogglePreview,
|
||||||
|
MarkdownLink(markdown::Uri),
|
||||||
|
ZoomIn,
|
||||||
|
ZoomOut,
|
||||||
|
ZoomReset,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EditorState {
|
pub struct EditorState {
|
||||||
pub content: text_editor::Content<iced_wgpu::Renderer>,
|
pub content: text_editor::Content<iced_wgpu::Renderer>,
|
||||||
pub font_size: f32,
|
pub font_size: f32,
|
||||||
|
pub preview: bool,
|
||||||
|
pub parsed: Vec<markdown::Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn md_style() -> markdown::Style {
|
||||||
|
markdown::Style {
|
||||||
|
font: Font::default(),
|
||||||
|
inline_code_highlight: Highlight {
|
||||||
|
background: Color::from_rgb(0.188, 0.188, 0.259).into(),
|
||||||
|
border: border::rounded(4),
|
||||||
|
},
|
||||||
|
inline_code_padding: padding::left(2).right(2),
|
||||||
|
inline_code_color: Color::from_rgb(0.651, 0.890, 0.631),
|
||||||
|
inline_code_font: Font::MONOSPACE,
|
||||||
|
code_block_font: Font::MONOSPACE,
|
||||||
|
link_color: Color::from_rgb(0.537, 0.706, 0.980),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditorState {
|
impl EditorState {
|
||||||
|
|
@ -22,23 +45,69 @@ impl EditorState {
|
||||||
Self {
|
Self {
|
||||||
content: text_editor::Content::new(),
|
content: text_editor::Content::new(),
|
||||||
font_size: 14.0,
|
font_size: 14.0,
|
||||||
|
preview: false,
|
||||||
|
parsed: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reparse(&mut self) {
|
||||||
|
let text = self.content.text();
|
||||||
|
self.parsed = markdown::parse(&text).collect();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) {
|
pub fn update(&mut self, message: Message) {
|
||||||
match message {
|
match message {
|
||||||
Message::EditorAction(action) => {
|
Message::EditorAction(action) => {
|
||||||
|
let is_edit = action.is_edit();
|
||||||
self.content.perform(action);
|
self.content.perform(action);
|
||||||
|
if is_edit {
|
||||||
|
self.reparse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::TogglePreview => {
|
||||||
|
self.preview = !self.preview;
|
||||||
|
if self.preview {
|
||||||
|
self.reparse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::MarkdownLink(_url) => {}
|
||||||
|
Message::ZoomIn => {
|
||||||
|
self.font_size = (self.font_size + 1.0).min(48.0);
|
||||||
|
}
|
||||||
|
Message::ZoomOut => {
|
||||||
|
self.font_size = (self.font_size - 1.0).max(8.0);
|
||||||
|
}
|
||||||
|
Message::ZoomReset => {
|
||||||
|
self.font_size = 14.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(&self) -> Element<'_, Message, Theme, iced_wgpu::Renderer> {
|
pub fn view(&self) -> Element<'_, Message, Theme, iced_wgpu::Renderer> {
|
||||||
let cursor = self.content.cursor();
|
let main_content: Element<'_, Message, Theme, iced_wgpu::Renderer> = if self.preview {
|
||||||
let line = cursor.position.line + 1;
|
let settings = markdown::Settings::with_text_size(self.font_size, md_style());
|
||||||
let col = cursor.position.column + 1;
|
let preview = markdown::view(&self.parsed, settings)
|
||||||
|
.map(Message::MarkdownLink);
|
||||||
|
|
||||||
let editor = iced_widget::text_editor(&self.content)
|
iced_widget::container(
|
||||||
|
iced_widget::scrollable(
|
||||||
|
iced_widget::container(preview)
|
||||||
|
.padding(Padding { top: 38.0, right: 16.0, bottom: 16.0, left: 16.0 })
|
||||||
|
)
|
||||||
|
.height(Length::Fill)
|
||||||
|
)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.style(|_theme: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(Color::from_rgb(0.08, 0.08, 0.10))),
|
||||||
|
border: Border::default(),
|
||||||
|
text_color: Some(Color::from_rgb(0.804, 0.839, 0.957)),
|
||||||
|
shadow: Shadow::default(),
|
||||||
|
snap: false,
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
iced_widget::text_editor(&self.content)
|
||||||
.on_action(Message::EditorAction)
|
.on_action(Message::EditorAction)
|
||||||
.font(Font::MONOSPACE)
|
.font(Font::MONOSPACE)
|
||||||
.size(self.font_size)
|
.size(self.font_size)
|
||||||
|
|
@ -52,13 +121,23 @@ impl EditorState {
|
||||||
placeholder: Color::from_rgb(0.4, 0.4, 0.4),
|
placeholder: Color::from_rgb(0.4, 0.4, 0.4),
|
||||||
value: Color::WHITE,
|
value: Color::WHITE,
|
||||||
selection: Color::from_rgba(0.3, 0.5, 0.8, 0.4),
|
selection: Color::from_rgba(0.3, 0.5, 0.8, 0.4),
|
||||||
});
|
})
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mode_label = if self.preview { "Preview" } else { "Edit" };
|
||||||
|
let cursor = self.content.cursor();
|
||||||
|
let line = cursor.position.line + 1;
|
||||||
|
let col = cursor.position.column + 1;
|
||||||
|
|
||||||
let status_bar = iced_widget::container(
|
let status_bar = iced_widget::container(
|
||||||
iced_widget::text(format!("Ln {}, Col {}", line, col))
|
iced_widget::row([
|
||||||
|
iced_widget::text(format!("{mode_label} Ln {line}, Col {col}"))
|
||||||
.font(Font::MONOSPACE)
|
.font(Font::MONOSPACE)
|
||||||
.size(11.0)
|
.size(11.0)
|
||||||
.color(Color::from_rgb(0.55, 0.55, 0.55)),
|
.color(Color::from_rgb(0.55, 0.55, 0.55))
|
||||||
|
.into(),
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.padding(Padding { top: 3.0, right: 10.0, bottom: 3.0, left: 10.0 })
|
.padding(Padding { top: 3.0, right: 10.0, bottom: 3.0, left: 10.0 })
|
||||||
|
|
@ -70,7 +149,7 @@ impl EditorState {
|
||||||
snap: false,
|
snap: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
iced_widget::column([editor.into(), status_bar.into()])
|
iced_widget::column([main_content, status_bar.into()])
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +163,18 @@ fn macos_key_binding(key_press: KeyPress) -> Option<Binding<Message>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match key.as_ref() {
|
match key.as_ref() {
|
||||||
|
keyboard::Key::Character("p") if modifiers.logo() => {
|
||||||
|
Some(Binding::Custom(Message::TogglePreview))
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("=" | "+") if modifiers.logo() => {
|
||||||
|
Some(Binding::Custom(Message::ZoomIn))
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("-") if modifiers.logo() => {
|
||||||
|
Some(Binding::Custom(Message::ZoomOut))
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("0") if modifiers.logo() => {
|
||||||
|
Some(Binding::Custom(Message::ZoomReset))
|
||||||
|
}
|
||||||
keyboard::Key::Named(key::Named::Backspace) if modifiers.alt() => {
|
keyboard::Key::Named(key::Named::Backspace) if modifiers.alt() => {
|
||||||
Some(Binding::Sequence(vec![
|
Some(Binding::Sequence(vec![
|
||||||
Binding::Select(Motion::WordLeft),
|
Binding::Select(Motion::WordLeft),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use iced_graphics::{Shell, Viewport};
|
||||||
use iced_runtime::user_interface::{self, UserInterface};
|
use iced_runtime::user_interface::{self, UserInterface};
|
||||||
use iced_wgpu::core::renderer::Style;
|
use iced_wgpu::core::renderer::Style;
|
||||||
use iced_wgpu::core::time::Instant;
|
use iced_wgpu::core::time::Instant;
|
||||||
use iced_wgpu::core::{clipboard, mouse, window, Color, Event, Font, Pixels, Point, Size, Theme};
|
use iced_wgpu::core::{clipboard, keyboard, mouse, window, Color, Event, Font, Pixels, Point, Size, Theme};
|
||||||
use iced_wgpu::Engine;
|
use iced_wgpu::Engine;
|
||||||
use raw_window_handle::{
|
use raw_window_handle::{
|
||||||
AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle,
|
AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||||
|
|
@ -157,6 +157,19 @@ pub fn render(handle: &mut ViewportHandle) {
|
||||||
let mut clipboard = MacClipboard;
|
let mut clipboard = MacClipboard;
|
||||||
let mut messages: Vec<Message> = Vec::new();
|
let mut messages: Vec<Message> = Vec::new();
|
||||||
|
|
||||||
|
for event in &handle.events {
|
||||||
|
if let Event::Keyboard(keyboard::Event::KeyPressed {
|
||||||
|
key: keyboard::Key::Character(c),
|
||||||
|
modifiers,
|
||||||
|
..
|
||||||
|
}) = event
|
||||||
|
{
|
||||||
|
if c.as_str() == "p" && modifiers.logo() {
|
||||||
|
messages.push(Message::TogglePreview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let _ = ui.update(
|
let _ = ui.update(
|
||||||
&handle.events,
|
&handle.events,
|
||||||
handle.cursor,
|
handle.cursor,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue