Skip to content

Commit

Permalink
Hot reload hotkey
Browse files Browse the repository at this point in the history
  • Loading branch information
AurevoirXavier committed Jul 10, 2024
1 parent b336d10 commit c1c9cd7
Show file tree
Hide file tree
Showing 12 changed files with 331 additions and 191 deletions.
47 changes: 14 additions & 33 deletions src/air.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ use eframe::{egui::*, glow::Context as GlowContext, Frame, *};
use tracing_subscriber::{reload::Handle, EnvFilter, Registry};
// self
use crate::{
component::Components, os::Os, prelude::Result, service::Services, state::State, ui::Uis,
component::Components,
os::Os,
prelude::Result,
service::Services,
state::State,
ui::{self, Uis},
};

#[derive(Debug)]
Expand All @@ -18,55 +23,31 @@ struct AiR {
}
impl AiR {
fn new(log_filter_handle: Handle<EnvFilter, Registry>, ctx: &Context) -> Result<Self> {
Self::set_fonts(ctx);
ui::set_fonts(ctx);

// To enable SVG.
egui_extras::install_image_loaders(ctx);

let once = Once::new();
let components = Components::new()?;

ui::set_font_size(ctx, components.setting.general.font_size);

let state = State::new(log_filter_handle, &components.setting)?;
let services = Services::new(ctx, &components, &state)?;
let uis = Uis::new();

Ok(Self { once, components, state, services, uis })
}

fn set_fonts(ctx: &Context) {
let mut fonts = FontDefinitions::default();

// Cascadia Code.
fonts.font_data.insert(
"Cascadia Code".into(),
FontData::from_static(include_bytes!("../asset/CascadiaCode.ttf")),
);
fonts
.families
.entry(FontFamily::Proportional)
.or_default()
.insert(0, "Cascadia Code".into());
fonts.families.entry(FontFamily::Monospace).or_default().insert(0, "Cascadia Code".into());
// NotoSerifSC.
fonts.font_data.insert(
"NotoSerifSC".into(),
FontData::from_static(include_bytes!("../asset/NotoSerifSC-VariableFont_wght.ttf")),
);
fonts.families.entry(FontFamily::Proportional).or_default().insert(1, "NotoSerifSC".into());
fonts.families.entry(FontFamily::Monospace).or_default().insert(1, "NotoSerifSC".into());

ctx.set_fonts(fonts);
}
}
impl App for AiR {
fn update(&mut self, ctx: &Context, _: &mut Frame) {
let air_ctx = AiRContext {
self.uis.draw(AiRContext {
egui_ctx: ctx,
components: &mut self.components,
state: &self.state,
services: &mut self.services,
};

self.uis.draw(air_ctx);
});
}

fn save(&mut self, _: &mut dyn Storage) {
Expand Down Expand Up @@ -123,8 +104,8 @@ pub fn launch(log_filter_handle: Handle<EnvFilter, Registry>) -> Result<()> {
icon_data::from_png_bytes(include_bytes!("../asset/icon.png").as_slice())
.unwrap(),
)
.with_inner_size((720., 360.))
.with_min_inner_size((720., 360.)),
.with_inner_size((760., 360.))
.with_min_inner_size((760., 360.)),
// TODO?: transparent window.
// .with_transparent(true),
follow_system_theme: true,
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod os;
mod service;
mod state;
mod ui;
mod util;
mod widget;

mod prelude {
Expand Down
1 change: 1 addition & 0 deletions src/os/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions src/os/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

10 changes: 8 additions & 2 deletions src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,14 @@ impl Services {
let rt = Runtime::new()?;
let quoter = Quoter::new(&rt, state.chat.quote.clone());
let is_chatting = Arc::new(AtomicBool::new(false));
let chat =
Chat::new(keyboard.clone(), &rt, is_chatting.clone(), &components.setting, &state.chat);
let chat = Chat::new(
keyboard.clone(),
&rt,
is_chatting.clone(),
&components.setting.ai,
&components.setting.chat,
&state.chat,
);
let audio = Audio::new()?;
let hotkey = Hotkey::new(
ctx,
Expand Down
18 changes: 10 additions & 8 deletions src/service/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
component::{
function::Function,
openai::OpenAi,
setting::{Chat as ChatSetting, Setting},
setting::{Ai, Chat as ChatSetting},
},
state::Chat as ChatState,
};
Expand All @@ -36,12 +36,13 @@ impl Chat {
keyboard: Keyboard,
rt: &Runtime,
is_chatting: Arc<AtomicBool>,
setting: &Setting,
ai_setting: &Ai,
chat_setting: &ChatSetting,
state: &ChatState,
) -> Self {
let openai = Arc::new(Mutex::new(OpenAi::new(setting.ai.clone())));
let openai = Arc::new(Mutex::new(OpenAi::new(ai_setting.to_owned())));
let openai_ = openai.clone();
let chat_setting = Arc::new(Mutex::new(setting.chat.clone()));
let chat_setting = Arc::new(Mutex::new(chat_setting.to_owned()));
let chat_setting_ = chat_setting.clone();
let input = state.input.clone();
let output = state.output.clone();
Expand Down Expand Up @@ -100,11 +101,12 @@ impl Chat {
self.tx.send(args).expect("send must succeed");
}

pub fn renew(&mut self, setting: &Setting) {
tracing::info!("renewing chat service");
pub fn renew(&self, ai_setting: &Ai, chat_setting: &ChatSetting) {
tracing::info!("renewing openai client");

*self.openai.blocking_lock() = OpenAi::new(setting.ai.clone());
*self.chat_setting.blocking_lock() = setting.chat.clone();
*self.openai.blocking_lock() = OpenAi::new(ai_setting.to_owned());

chat_setting.clone_into(&mut self.chat_setting.blocking_lock());
}

pub fn abort(&self) {
Expand Down
69 changes: 43 additions & 26 deletions src/service/hotkey.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// std
use std::{
fmt::{Debug, Formatter, Result as FmtResult},
sync::{
atomic::{AtomicBool, Ordering},
mpsc::Sender,
Expand All @@ -11,7 +12,8 @@ use std::{
// crates.io
use arboard::Clipboard;
use eframe::egui::{Context, ViewportCommand};
use global_hotkey::{GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState};
use global_hotkey::{hotkey::HotKey, GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState};
use parking_lot::RwLock;
// self
use super::{audio::Audio, chat::ChatArgs, keyboard::Keyboard};
use crate::{
Expand All @@ -20,8 +22,12 @@ use crate::{
prelude::*,
};

#[derive(Debug)]
pub struct Hotkey(Arc<AtomicBool>);
pub struct Hotkey {
// The manager need to be kept alive during the whole program life.
_manager: GlobalHotKeyManager,
manager: Arc<RwLock<Manager>>,
abort: Arc<AtomicBool>,
}
impl Hotkey {
pub fn new(
ctx: &Context,
Expand All @@ -31,18 +37,17 @@ impl Hotkey {
audio: Audio,
tx: Sender<ChatArgs>,
) -> Result<Self> {
let _manager = GlobalHotKeyManager::new().map_err(GlobalHotKeyError::Main)?;
let ctx = ctx.to_owned();
let manager = Manager::new(hotkeys)?;
let manager = Arc::new(RwLock::new(Manager::new(&_manager, hotkeys)?));
let manager_ = manager.clone();
let abort = Arc::new(AtomicBool::new(false));
let abort_ = abort.clone();
let hk_rx = GlobalHotKeyEvent::receiver();
let mut clipboard = Clipboard::new()?;

// TODO: handle the error.
thread::spawn(move || {
// Manager must be kept alive.
let manager = manager;

while !abort_.load(Ordering::Relaxed) {
// Block the thread until a hotkey event is received.
let e = hk_rx.recv().unwrap();
Expand All @@ -51,7 +56,7 @@ impl Hotkey {
if let HotKeyState::Pressed = e.state {
audio.play_notification();

let (func, keys) = manager.match_func(e.id);
let (func, keys) = manager_.read().match_func(e.id);
let to_focus = !func.is_directly();

if to_focus && hide_on_lost_focus.load(Ordering::Relaxed) {
Expand Down Expand Up @@ -84,57 +89,69 @@ impl Hotkey {
}
});

Ok(Self(abort))
Ok(Self { _manager, manager, abort })
}

// TODO: fn renew.
pub fn renew(&self, hotkeys: &Hotkeys) {
tracing::info!("renewing hotkey manager");

let mut manager = self.manager.write();

self._manager.unregister_all(&manager.hotkeys).expect("unregister must succeed");

*manager = Manager::new(&self._manager, hotkeys).expect("renew must succeed");
}

pub fn abort(&self) {
self.0.store(true, Ordering::Release);
self.abort.store(true, Ordering::Release);
}
}
impl Debug for Hotkey {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.debug_struct("Hotkey").field("manager", &"..").field("abort", &self.abort).finish()
}
}

struct Manager {
// The manager need to be kept alive during the whole program life.
_inner: GlobalHotKeyManager,
ids: [u32; 4],
hotkeys: [HotKey; 4],
hotkeys_keys: [Keys; 4],
}
impl Manager {
fn new(hotkeys: &Hotkeys) -> Result<Self> {
let _inner = GlobalHotKeyManager::new().map_err(GlobalHotKeyError::Main)?;
fn new(_manager: &GlobalHotKeyManager, hotkeys: &Hotkeys) -> Result<Self> {
let hotkeys_raw = [
&hotkeys.rewrite,
&hotkeys.rewrite_directly,
&hotkeys.translate,
&hotkeys.translate_directly,
];
let hotkeys = hotkeys_raw
let hotkeys: [HotKey; 4] = hotkeys_raw
.iter()
.map(|h| h.parse())
.collect::<Result<Vec<_>, _>>()
.map_err(GlobalHotKeyError::Parse)?;
.map_err(GlobalHotKeyError::Parse)?
.try_into()
.expect("array must fit");

_inner.register_all(&hotkeys).map_err(GlobalHotKeyError::Main)?;
_manager.register_all(&hotkeys).map_err(GlobalHotKeyError::Main)?;

let ids =
hotkeys.iter().map(|h| h.id).collect::<Vec<_>>().try_into().expect("array must fit");
let hotkeys_keys = hotkeys_raw
.iter()
.map(|h| h.parse())
.collect::<Result<Vec<_>, _>>()?
.try_into()
.expect("array must fit");

Ok(Self { _inner, ids, hotkeys_keys })
Ok(Self { hotkeys, hotkeys_keys })
}

fn match_func(&self, id: u32) -> (Function, Keys) {
match id {
i if i == self.ids[0] => (Function::Rewrite, self.hotkeys_keys[0].clone()),
i if i == self.ids[1] => (Function::RewriteDirectly, self.hotkeys_keys[1].clone()),
i if i == self.ids[2] => (Function::Translate, self.hotkeys_keys[2].clone()),
i if i == self.ids[3] => (Function::TranslateDirectly, self.hotkeys_keys[3].clone()),
i if i == self.hotkeys[0].id => (Function::Rewrite, self.hotkeys_keys[0].clone()),
i if i == self.hotkeys[1].id =>
(Function::RewriteDirectly, self.hotkeys_keys[1].clone()),
i if i == self.hotkeys[2].id => (Function::Translate, self.hotkeys_keys[2].clone()),
i if i == self.hotkeys[3].id =>
(Function::TranslateDirectly, self.hotkeys_keys[3].clone()),
_ => unreachable!(),
}
}
Expand Down
29 changes: 27 additions & 2 deletions src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
mod panel;
use panel::{Chat, Panel, Setting};

mod util;

// crates.io
use eframe::egui::*;
// self
Expand Down Expand Up @@ -48,3 +46,30 @@ impl Uis {
});
}
}

pub fn set_fonts(ctx: &Context) {
let mut fonts = FontDefinitions::default();

// Cascadia Code.
fonts.font_data.insert(
"Cascadia Code".into(),
FontData::from_static(include_bytes!("../asset/CascadiaCode.ttf")),
);
fonts.families.entry(FontFamily::Proportional).or_default().insert(0, "Cascadia Code".into());
fonts.families.entry(FontFamily::Monospace).or_default().insert(0, "Cascadia Code".into());
// NotoSerifSC.
fonts.font_data.insert(
"NotoSerifSC".into(),
FontData::from_static(include_bytes!("../asset/NotoSerifSC-VariableFont_wght.ttf")),
);
fonts.families.entry(FontFamily::Proportional).or_default().insert(1, "NotoSerifSC".into());
fonts.families.entry(FontFamily::Monospace).or_default().insert(1, "NotoSerifSC".into());

ctx.set_fonts(fonts);
}

pub fn set_font_size(ctx: &Context, font_size: f32) {
ctx.style_mut(|s| {
s.text_styles.values_mut().for_each(|s| s.size = font_size);
});
}
Loading

0 comments on commit c1c9cd7

Please sign in to comment.