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_graphics = "0.14"
|
||||
iced_runtime = "0.14"
|
||||
iced_widget = { version = "0.14", features = ["wgpu"] }
|
||||
iced_widget = { version = "0.14", features = ["wgpu", "markdown"] }
|
||||
wgpu = "27"
|
||||
raw-window-handle = "0.6"
|
||||
pollster = "0.4"
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ pub fn push_key_event(
|
|||
.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
|
||||
} else {
|
||||
text.filter(|s| !s.is_empty()).map(SmolStr::new)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,43 @@
|
|||
use iced_wgpu::core::keyboard;
|
||||
use iced_wgpu::core::keyboard::key;
|
||||
use iced_wgpu::core::text::Wrapping;
|
||||
use iced_wgpu::core::text::{Highlight, Wrapping};
|
||||
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::markdown;
|
||||
use iced_widget::text_editor::{self, Binding, KeyPress, Motion, Status, Style};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
EditorAction(text_editor::Action),
|
||||
TogglePreview,
|
||||
MarkdownLink(markdown::Uri),
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
ZoomReset,
|
||||
}
|
||||
|
||||
pub struct EditorState {
|
||||
pub content: text_editor::Content<iced_wgpu::Renderer>,
|
||||
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 {
|
||||
|
|
@ -22,23 +45,69 @@ impl EditorState {
|
|||
Self {
|
||||
content: text_editor::Content::new(),
|
||||
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) {
|
||||
match message {
|
||||
Message::EditorAction(action) => {
|
||||
let is_edit = action.is_edit();
|
||||
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> {
|
||||
let cursor = self.content.cursor();
|
||||
let line = cursor.position.line + 1;
|
||||
let col = cursor.position.column + 1;
|
||||
let main_content: Element<'_, Message, Theme, iced_wgpu::Renderer> = if self.preview {
|
||||
let settings = markdown::Settings::with_text_size(self.font_size, md_style());
|
||||
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)
|
||||
.font(Font::MONOSPACE)
|
||||
.size(self.font_size)
|
||||
|
|
@ -52,13 +121,23 @@ impl EditorState {
|
|||
placeholder: Color::from_rgb(0.4, 0.4, 0.4),
|
||||
value: Color::WHITE,
|
||||
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(
|
||||
iced_widget::text(format!("Ln {}, Col {}", line, col))
|
||||
iced_widget::row([
|
||||
iced_widget::text(format!("{mode_label} Ln {line}, Col {col}"))
|
||||
.font(Font::MONOSPACE)
|
||||
.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)
|
||||
.padding(Padding { top: 3.0, right: 10.0, bottom: 3.0, left: 10.0 })
|
||||
|
|
@ -70,7 +149,7 @@ impl EditorState {
|
|||
snap: false,
|
||||
});
|
||||
|
||||
iced_widget::column([editor.into(), status_bar.into()])
|
||||
iced_widget::column([main_content, status_bar.into()])
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
|
@ -84,6 +163,18 @@ fn macos_key_binding(key_press: KeyPress) -> Option<Binding<Message>> {
|
|||
}
|
||||
|
||||
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() => {
|
||||
Some(Binding::Sequence(vec![
|
||||
Binding::Select(Motion::WordLeft),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use iced_graphics::{Shell, Viewport};
|
|||
use iced_runtime::user_interface::{self, UserInterface};
|
||||
use iced_wgpu::core::renderer::Style;
|
||||
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 raw_window_handle::{
|
||||
AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||
|
|
@ -157,6 +157,19 @@ pub fn render(handle: &mut ViewportHandle) {
|
|||
let mut clipboard = MacClipboard;
|
||||
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(
|
||||
&handle.events,
|
||||
handle.cursor,
|
||||
|
|
|
|||
Loading…
Reference in New Issue