replace Swift editor with Iced TextEditor, delete old compositor
This commit is contained in:
parent
50aad4bf84
commit
7a2aa08d1c
|
|
@ -24,8 +24,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
_ = ConfigManager.shared
|
_ = ConfigManager.shared
|
||||||
appState = AppState()
|
appState = AppState()
|
||||||
|
|
||||||
let contentView = ContentView(state: appState)
|
let viewport = IcedViewportView(frame: NSRect(x: 0, y: 0, width: 1200, height: 800))
|
||||||
let hostingView = NSHostingView(rootView: contentView)
|
viewport.autoresizingMask = [.width, .height]
|
||||||
|
|
||||||
window = NSWindow(
|
window = NSWindow(
|
||||||
contentRect: NSRect(x: 0, y: 0, width: 1200, height: 800),
|
contentRect: NSRect(x: 0, y: 0, width: 1200, height: 800),
|
||||||
|
|
@ -37,7 +37,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
window.titleVisibility = .hidden
|
window.titleVisibility = .hidden
|
||||||
window.backgroundColor = Theme.current.base
|
window.backgroundColor = Theme.current.base
|
||||||
window.title = "Swiftly"
|
window.title = "Swiftly"
|
||||||
window.contentView = hostingView
|
window.contentView = viewport
|
||||||
window.center()
|
window.center()
|
||||||
window.setFrameAutosaveName("SwiftlyMainWindow")
|
window.setFrameAutosaveName("SwiftlyMainWindow")
|
||||||
window.makeKeyAndOrderFront(nil)
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
|
@ -235,8 +235,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
@objc private func newWindow() {
|
@objc private func newWindow() {
|
||||||
let state = AppState()
|
let state = AppState()
|
||||||
let contentView = ContentView(state: state)
|
let viewport = IcedViewportView(frame: NSRect(x: 0, y: 0, width: 1200, height: 800))
|
||||||
let hostingView = NSHostingView(rootView: contentView)
|
viewport.autoresizingMask = [.width, .height]
|
||||||
|
|
||||||
let win = NSWindow(
|
let win = NSWindow(
|
||||||
contentRect: NSRect(x: 0, y: 0, width: 1200, height: 800),
|
contentRect: NSRect(x: 0, y: 0, width: 1200, height: 800),
|
||||||
|
|
@ -248,7 +248,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
win.titleVisibility = .hidden
|
win.titleVisibility = .hidden
|
||||||
win.backgroundColor = Theme.current.base
|
win.backgroundColor = Theme.current.base
|
||||||
win.title = "Swiftly"
|
win.title = "Swiftly"
|
||||||
win.contentView = hostingView
|
win.contentView = viewport
|
||||||
win.center()
|
win.center()
|
||||||
win.makeKeyAndOrderFront(nil)
|
win.makeKeyAndOrderFront(nil)
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,12 +6,7 @@ struct ContentView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let _ = themeVersion
|
let _ = themeVersion
|
||||||
HSplitView {
|
|
||||||
EditorView(state: state)
|
|
||||||
.frame(minWidth: 400)
|
|
||||||
IcedViewportRepresentable()
|
IcedViewportRepresentable()
|
||||||
.frame(minWidth: 200)
|
|
||||||
}
|
|
||||||
.frame(minWidth: 700, minHeight: 400)
|
.frame(minWidth: 700, minHeight: 400)
|
||||||
.background(Color(ns: Theme.current.base))
|
.background(Color(ns: Theme.current.base))
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .settingsChanged)) { _ in
|
.onReceive(NotificationCenter.default.publisher(for: .settingsChanged)) { _ in
|
||||||
|
|
|
||||||
3199
src/EditorView.swift
3199
src/EditorView.swift
File diff suppressed because it is too large
Load Diff
|
|
@ -26,6 +26,7 @@ class IcedViewportView: NSView {
|
||||||
if window != nil && viewportHandle == nil {
|
if window != nil && viewportHandle == nil {
|
||||||
createViewport()
|
createViewport()
|
||||||
startDisplayLink()
|
startDisplayLink()
|
||||||
|
window?.makeFirstResponder(self)
|
||||||
} else if window == nil {
|
} else if window == nil {
|
||||||
stopDisplayLink()
|
stopDisplayLink()
|
||||||
destroyViewport()
|
destroyViewport()
|
||||||
|
|
@ -105,6 +106,7 @@ class IcedViewportView: NSView {
|
||||||
// MARK: - Mouse Events
|
// MARK: - Mouse Events
|
||||||
|
|
||||||
override func mouseDown(with event: NSEvent) {
|
override func mouseDown(with event: NSEvent) {
|
||||||
|
window?.makeFirstResponder(self)
|
||||||
guard let h = viewportHandle else { return }
|
guard let h = viewportHandle else { return }
|
||||||
let pt = convert(event.locationInWindow, from: nil)
|
let pt = convert(event.locationInWindow, from: nil)
|
||||||
viewport_mouse_event(h, Float(pt.x), Float(pt.y), 0, true)
|
viewport_mouse_event(h, Float(pt.x), Float(pt.y), 0, true)
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
import SwiftUI
|
|
||||||
|
|
@ -6,7 +6,7 @@ use smol_str::SmolStr;
|
||||||
use crate::ViewportHandle;
|
use crate::ViewportHandle;
|
||||||
|
|
||||||
pub fn push_mouse_event(handle: &mut ViewportHandle, x: f32, y: f32, button: u8, pressed: bool) {
|
pub fn push_mouse_event(handle: &mut ViewportHandle, x: f32, y: f32, button: u8, pressed: bool) {
|
||||||
let position = Point::new(x / handle.scale, y / handle.scale);
|
let position = Point::new(x, y);
|
||||||
handle.cursor = mouse::Cursor::Available(position);
|
handle.cursor = mouse::Cursor::Available(position);
|
||||||
|
|
||||||
handle.events.push(Event::Mouse(mouse::Event::CursorMoved { position }));
|
handle.events.push(Event::Mouse(mouse::Event::CursorMoved { position }));
|
||||||
|
|
@ -34,10 +34,21 @@ pub fn push_key_event(
|
||||||
) {
|
) {
|
||||||
let modifiers = decode_modifiers(modifier_flags);
|
let modifiers = decode_modifiers(modifier_flags);
|
||||||
let physical = key::Physical::Unidentified(key::NativeCode::MacOS(keycode as u16));
|
let physical = key::Physical::Unidentified(key::NativeCode::MacOS(keycode as u16));
|
||||||
let logical = text
|
|
||||||
.filter(|s| !s.is_empty())
|
let named = keycode_to_named(keycode);
|
||||||
|
let logical = if let Some(n) = named {
|
||||||
|
keyboard::Key::Named(n)
|
||||||
|
} else {
|
||||||
|
text.filter(|s| !s.is_empty())
|
||||||
.map(|s| keyboard::Key::Character(SmolStr::new(s)))
|
.map(|s| keyboard::Key::Character(SmolStr::new(s)))
|
||||||
.unwrap_or(keyboard::Key::Unidentified);
|
.unwrap_or(keyboard::Key::Unidentified)
|
||||||
|
};
|
||||||
|
|
||||||
|
let insert_text = if named.is_some() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
text.filter(|s| !s.is_empty()).map(SmolStr::new)
|
||||||
|
};
|
||||||
|
|
||||||
if pressed {
|
if pressed {
|
||||||
handle.events.push(Event::Keyboard(keyboard::Event::KeyPressed {
|
handle.events.push(Event::Keyboard(keyboard::Event::KeyPressed {
|
||||||
|
|
@ -46,7 +57,7 @@ pub fn push_key_event(
|
||||||
physical_key: physical,
|
physical_key: physical,
|
||||||
location: keyboard::Location::Standard,
|
location: keyboard::Location::Standard,
|
||||||
modifiers,
|
modifiers,
|
||||||
text: text.filter(|s| !s.is_empty()).map(SmolStr::new),
|
text: insert_text,
|
||||||
repeat: false,
|
repeat: false,
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -60,6 +71,38 @@ pub fn push_key_event(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keycode_to_named(keycode: u32) -> Option<keyboard::key::Named> {
|
||||||
|
use keyboard::key::Named;
|
||||||
|
match keycode {
|
||||||
|
36 => Some(Named::Enter),
|
||||||
|
48 => Some(Named::Tab),
|
||||||
|
51 => Some(Named::Backspace),
|
||||||
|
53 => Some(Named::Escape),
|
||||||
|
117 => Some(Named::Delete),
|
||||||
|
123 => Some(Named::ArrowLeft),
|
||||||
|
124 => Some(Named::ArrowRight),
|
||||||
|
125 => Some(Named::ArrowDown),
|
||||||
|
126 => Some(Named::ArrowUp),
|
||||||
|
115 => Some(Named::Home),
|
||||||
|
119 => Some(Named::End),
|
||||||
|
116 => Some(Named::PageUp),
|
||||||
|
121 => Some(Named::PageDown),
|
||||||
|
122 => Some(Named::F1),
|
||||||
|
120 => Some(Named::F2),
|
||||||
|
99 => Some(Named::F3),
|
||||||
|
118 => Some(Named::F4),
|
||||||
|
96 => Some(Named::F5),
|
||||||
|
97 => Some(Named::F6),
|
||||||
|
98 => Some(Named::F7),
|
||||||
|
100 => Some(Named::F8),
|
||||||
|
101 => Some(Named::F9),
|
||||||
|
109 => Some(Named::F10),
|
||||||
|
103 => Some(Named::F11),
|
||||||
|
111 => Some(Named::F12),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push_scroll_event(
|
pub fn push_scroll_event(
|
||||||
handle: &mut ViewportHandle,
|
handle: &mut ViewportHandle,
|
||||||
x: f32,
|
x: f32,
|
||||||
|
|
@ -67,7 +110,7 @@ pub fn push_scroll_event(
|
||||||
delta_x: f32,
|
delta_x: f32,
|
||||||
delta_y: f32,
|
delta_y: f32,
|
||||||
) {
|
) {
|
||||||
let position = Point::new(x / handle.scale, y / handle.scale);
|
let position = Point::new(x, y);
|
||||||
handle.cursor = mouse::Cursor::Available(position);
|
handle.cursor = mouse::Cursor::Available(position);
|
||||||
handle.events.push(Event::Mouse(mouse::Event::WheelScrolled {
|
handle.events.push(Event::Mouse(mouse::Event::WheelScrolled {
|
||||||
delta: mouse::ScrollDelta::Pixels {
|
delta: mouse::ScrollDelta::Pixels {
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,50 @@
|
||||||
use iced_wgpu::core::{Color, Element, Length, Theme};
|
use iced_wgpu::core::text::Wrapping;
|
||||||
use iced_widget::{container, Text};
|
use iced_wgpu::core::{
|
||||||
|
Background, Border, Color, Element, Font, Length, Padding, Theme,
|
||||||
|
};
|
||||||
|
use iced_widget::text_editor::{self, Style};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Message {
|
||||||
|
EditorAction(text_editor::Action),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EditorState {
|
pub struct EditorState {
|
||||||
pub text: String,
|
pub content: text_editor::Content<iced_wgpu::Renderer>,
|
||||||
|
pub font_size: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditorState {
|
impl EditorState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
text: String::from("Swiftly"),
|
content: text_editor::Content::new(),
|
||||||
|
font_size: 14.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(&self) -> Element<'_, (), Theme, iced_wgpu::Renderer> {
|
pub fn update(&mut self, message: Message) {
|
||||||
container(
|
match message {
|
||||||
Text::new(&self.text)
|
Message::EditorAction(action) => {
|
||||||
.size(32)
|
self.content.perform(action);
|
||||||
.color(Color::WHITE),
|
}
|
||||||
)
|
}
|
||||||
.width(Length::Fill)
|
}
|
||||||
|
|
||||||
|
pub fn view(&self) -> Element<'_, Message, Theme, iced_wgpu::Renderer> {
|
||||||
|
iced_widget::text_editor(&self.content)
|
||||||
|
.on_action(Message::EditorAction)
|
||||||
|
.font(Font::MONOSPACE)
|
||||||
|
.size(self.font_size)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.center_x(Length::Fill)
|
.padding(Padding { top: 38.0, right: 8.0, bottom: 8.0, left: 8.0 })
|
||||||
.center_y(Length::Fill)
|
.wrapping(Wrapping::Word)
|
||||||
|
.style(|_theme, _status| Style {
|
||||||
|
background: Background::Color(Color::from_rgb(0.08, 0.08, 0.10)),
|
||||||
|
border: Border::default(),
|
||||||
|
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()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,49 @@
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use iced_graphics::{Viewport, Shell};
|
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::{clipboard, mouse, Color, Font, Pixels, Size, Theme};
|
use iced_wgpu::core::time::Instant;
|
||||||
|
use iced_wgpu::core::{clipboard, mouse, window, Color, Event, Font, Pixels, Point, Size, Theme};
|
||||||
use iced_wgpu::Engine;
|
use iced_wgpu::Engine;
|
||||||
use raw_window_handle::{AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle};
|
use raw_window_handle::{
|
||||||
|
AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::editor::EditorState;
|
use crate::editor::{EditorState, Message};
|
||||||
use crate::ViewportHandle;
|
use crate::ViewportHandle;
|
||||||
|
|
||||||
pub fn create(nsview: *mut c_void, width: f32, height: f32, scale: f32) -> Option<ViewportHandle> {
|
struct MacClipboard;
|
||||||
|
|
||||||
|
impl clipboard::Clipboard for MacClipboard {
|
||||||
|
fn read(&self, _kind: clipboard::Kind) -> Option<String> {
|
||||||
|
std::process::Command::new("pbpaste")
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.and_then(|o| String::from_utf8(o.stdout).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _kind: clipboard::Kind, contents: String) {
|
||||||
|
use std::io::Write;
|
||||||
|
if let Ok(mut child) = std::process::Command::new("pbcopy")
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
if let Some(stdin) = child.stdin.as_mut() {
|
||||||
|
let _ = stdin.write_all(contents.as_bytes());
|
||||||
|
}
|
||||||
|
let _ = child.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
nsview: *mut c_void,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
scale: f32,
|
||||||
|
) -> Option<ViewportHandle> {
|
||||||
let ptr = NonNull::new(nsview)?;
|
let ptr = NonNull::new(nsview)?;
|
||||||
|
|
||||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||||
|
|
@ -72,16 +104,17 @@ pub fn create(nsview: *mut c_void, width: f32, height: f32, scale: f32) -> Optio
|
||||||
Shell::headless(),
|
Shell::headless(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let renderer = iced_wgpu::Renderer::new(
|
let renderer = iced_wgpu::Renderer::new(engine, Font::DEFAULT, Pixels(16.0));
|
||||||
engine,
|
|
||||||
Font::DEFAULT,
|
|
||||||
Pixels(16.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let viewport = Viewport::with_physical_size(
|
let viewport =
|
||||||
Size::new(phys_w.max(1), phys_h.max(1)),
|
Viewport::with_physical_size(Size::new(phys_w.max(1), phys_h.max(1)), scale);
|
||||||
scale,
|
|
||||||
);
|
let focus_point = Point::new(width / 2.0, height / 2.0);
|
||||||
|
let initial_events = vec![
|
||||||
|
Event::Mouse(mouse::Event::CursorMoved { position: focus_point }),
|
||||||
|
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
|
||||||
|
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)),
|
||||||
|
];
|
||||||
|
|
||||||
Some(ViewportHandle {
|
Some(ViewportHandle {
|
||||||
surface,
|
surface,
|
||||||
|
|
@ -95,8 +128,8 @@ pub fn create(nsview: *mut c_void, width: f32, height: f32, scale: f32) -> Optio
|
||||||
viewport,
|
viewport,
|
||||||
cache: user_interface::Cache::new(),
|
cache: user_interface::Cache::new(),
|
||||||
state: EditorState::new(),
|
state: EditorState::new(),
|
||||||
events: Vec::new(),
|
events: initial_events,
|
||||||
cursor: mouse::Cursor::Unavailable,
|
cursor: mouse::Cursor::Available(focus_point),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +142,10 @@ pub fn render(handle: &mut ViewportHandle) {
|
||||||
|
|
||||||
let logical_size = handle.viewport.logical_size();
|
let logical_size = handle.viewport.logical_size();
|
||||||
|
|
||||||
|
handle
|
||||||
|
.events
|
||||||
|
.push(Event::Window(window::Event::RedrawRequested(Instant::now())));
|
||||||
|
|
||||||
let cache = std::mem::take(&mut handle.cache);
|
let cache = std::mem::take(&mut handle.cache);
|
||||||
let mut ui = UserInterface::build(
|
let mut ui = UserInterface::build(
|
||||||
handle.state.view(),
|
handle.state.view(),
|
||||||
|
|
@ -117,8 +154,8 @@ pub fn render(handle: &mut ViewportHandle) {
|
||||||
&mut handle.renderer,
|
&mut handle.renderer,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut clipboard = clipboard::Null;
|
let mut clipboard = MacClipboard;
|
||||||
let mut messages: Vec<()> = Vec::new();
|
let mut messages: Vec<Message> = Vec::new();
|
||||||
|
|
||||||
let _ = ui.update(
|
let _ = ui.update(
|
||||||
&handle.events,
|
&handle.events,
|
||||||
|
|
@ -129,21 +166,31 @@ pub fn render(handle: &mut ViewportHandle) {
|
||||||
);
|
);
|
||||||
handle.events.clear();
|
handle.events.clear();
|
||||||
|
|
||||||
|
let cache = ui.into_cache();
|
||||||
|
|
||||||
|
for msg in messages.drain(..) {
|
||||||
|
handle.state.update(msg);
|
||||||
|
}
|
||||||
|
|
||||||
let theme = Theme::Dark;
|
let theme = Theme::Dark;
|
||||||
let style = Style {
|
let style = Style {
|
||||||
text_color: Color::WHITE,
|
text_color: Color::WHITE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut ui = UserInterface::build(
|
||||||
|
handle.state.view(),
|
||||||
|
Size::new(logical_size.width, logical_size.height),
|
||||||
|
cache,
|
||||||
|
&mut handle.renderer,
|
||||||
|
);
|
||||||
|
|
||||||
ui.draw(&mut handle.renderer, &theme, &style, handle.cursor);
|
ui.draw(&mut handle.renderer, &theme, &style, handle.cursor);
|
||||||
handle.cache = ui.into_cache();
|
handle.cache = ui.into_cache();
|
||||||
|
|
||||||
let bg = Color::from_rgb(0.08, 0.08, 0.10);
|
let bg = Color::from_rgb(0.08, 0.08, 0.10);
|
||||||
handle.renderer.present(
|
handle
|
||||||
Some(bg),
|
.renderer
|
||||||
handle.format,
|
.present(Some(bg), handle.format, &view, &handle.viewport);
|
||||||
&view,
|
|
||||||
&handle.viewport,
|
|
||||||
);
|
|
||||||
|
|
||||||
frame.present();
|
frame.present();
|
||||||
}
|
}
|
||||||
|
|
@ -159,10 +206,7 @@ pub fn resize(handle: &mut ViewportHandle, width: f32, height: f32, scale: f32)
|
||||||
handle.height = phys_h;
|
handle.height = phys_h;
|
||||||
handle.scale = scale;
|
handle.scale = scale;
|
||||||
|
|
||||||
handle.viewport = Viewport::with_physical_size(
|
handle.viewport = Viewport::with_physical_size(Size::new(phys_w, phys_h), scale);
|
||||||
Size::new(phys_w, phys_h),
|
|
||||||
scale,
|
|
||||||
);
|
|
||||||
|
|
||||||
handle.surface.configure(
|
handle.surface.configure(
|
||||||
&handle.device,
|
&handle.device,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue