feat: migrate to a daemon to support windowing in future. allow to run without attached console

This commit is contained in:
Stevan Freeborn
2026-01-26 12:44:50 -06:00
parent db0cd51818
commit 3b40d30744
3 changed files with 40 additions and 12 deletions
+25 -10
View File
@@ -1,3 +1,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod components; mod components;
mod constants; mod constants;
mod file; mod file;
@@ -18,7 +20,7 @@ use crate::message::Message;
use crate::state::State; use crate::state::State;
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
iced::application(boot, update, view) iced::daemon(boot, update, view)
.subscription(subscription) .subscription(subscription)
.font(constants::CUSTOM_FONT_BYTES) .font(constants::CUSTOM_FONT_BYTES)
.theme(theme) .theme(theme)
@@ -26,15 +28,16 @@ pub fn main() -> iced::Result {
default_font: constants::CUSTOM_FONT, default_font: constants::CUSTOM_FONT,
..Default::default() ..Default::default()
}) })
.window(window::Settings {
icon: Some(icon::from_file_data(constants::ICON_BYTES, None).expect("Failed to load icon")),
..window::Settings::default()
})
.run() .run()
} }
fn boot() -> State { fn boot() -> (State, Task<Message>) {
state::State::new() let (id, open) = window::open(window::Settings {
icon: Some(icon::from_file_data(constants::ICON_BYTES, None).expect("Failed to load icon")),
..window::Settings::default()
});
(state::State::new(id), open.map(Message::WindowOpened))
} }
fn update(state: &mut State, message: Message) -> Task<Message> { fn update(state: &mut State, message: Message) -> Task<Message> {
@@ -46,10 +49,21 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
Message::ViewActionSelected(action) => handler::view_action(state, action), Message::ViewActionSelected(action) => handler::view_action(state, action),
Message::FileOpened(result) => handler::opened_file(state, result), Message::FileOpened(result) => handler::opened_file(state, result),
Message::FileSaved(result) => handler::saved_file(state, result), Message::FileSaved(result) => handler::saved_file(state, result),
Message::WindowOpened(id) => {
state.set_window_id(id);
Task::none()
}
Message::WindowClosed(id) => {
if state.window_id() == Some(id) {
iced::exit()
} else {
Task::none()
}
}
} }
} }
fn view(state: &State) -> Element<'_, Message> { fn view(state: &State, _id: iced::window::Id) -> Element<'_, Message> {
let current_file = state.active_file(); let current_file = state.active_file();
column![ column![
@@ -62,8 +76,9 @@ fn view(state: &State) -> Element<'_, Message> {
} }
fn subscription(_state: &State) -> Subscription<Message> { fn subscription(_state: &State) -> Subscription<Message> {
event::listen_with(|e, _status, _win| -> Option<Message> { event::listen_with(|e, _status, win| -> Option<Message> {
match e { match e {
iced::Event::Window(window::Event::Closed) => Some(Message::WindowClosed(win)),
iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => { iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, .. }) => {
for vb in key_bindings::ALL { for vb in key_bindings::ALL {
if vb.should_handle(&key, &modifiers) { if vb.should_handle(&key, &modifiers) {
@@ -78,7 +93,7 @@ fn subscription(_state: &State) -> Subscription<Message> {
}) })
} }
fn theme(_state: &State) -> Theme { fn theme(_state: &State, _id: iced::window::Id) -> Theme {
Theme::custom( Theme::custom(
"Win11 Dark", "Win11 Dark",
Palette { Palette {
+3 -1
View File
@@ -1,6 +1,6 @@
use std::{fmt::Display, path::PathBuf}; use std::{fmt::Display, path::PathBuf};
use iced::widget::text_editor; use iced::{widget::text_editor, window};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileAction { pub enum FileAction {
@@ -63,6 +63,8 @@ impl Display for ViewAction {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
WindowOpened(window::Id),
WindowClosed(window::Id),
Edit(text_editor::Action), Edit(text_editor::Action),
FileActionSelected(FileAction), FileActionSelected(FileAction),
ViewActionSelected(ViewAction), ViewActionSelected(ViewAction),
+12 -1
View File
@@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use iced::widget::text_editor; use iced::widget::text_editor;
use iced::window;
use crate::message::{FileAction, ViewAction}; use crate::message::{FileAction, ViewAction};
use crate::{constants, file}; use crate::{constants, file};
@@ -14,6 +15,7 @@ pub enum Mode {
#[derive(Default)] #[derive(Default)]
pub struct State { pub struct State {
window_id: Option<window::Id>,
mode: Mode, mode: Mode,
files: Vec<file::File>, files: Vec<file::File>,
current_file: usize, current_file: usize,
@@ -23,10 +25,11 @@ pub struct State {
} }
impl State { impl State {
pub fn new() -> Self { pub fn new(window_id: window::Id) -> Self {
let default_file = file::File::default(); let default_file = file::File::default();
Self { Self {
window_id: Some(window_id),
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,
@@ -133,6 +136,10 @@ impl State {
self.mode self.mode
} }
pub fn window_id(&self) -> Option<window::Id> {
self.window_id
}
pub fn font_size(&self) -> u32 { pub fn font_size(&self) -> u32 {
self.editor_font_size self.editor_font_size
} }
@@ -144,4 +151,8 @@ impl State {
pub fn selected_view_action(&self) -> Option<ViewAction> { pub fn selected_view_action(&self) -> Option<ViewAction> {
self.selected_view_action self.selected_view_action
} }
pub fn set_window_id(&mut self, id: window::Id) {
self.window_id = Some(id)
}
} }