port to druid
This commit is contained in:
parent
1e98067548
commit
ff0597ac40
3 changed files with 114 additions and 114 deletions
|
@ -6,7 +6,5 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
gtk = "^0.14.0"
|
||||
relm = "^0.22.0"
|
||||
relm-derive = "^0.22.0"
|
||||
druid = { git = "https://github.com/linebender/druid.git" }
|
||||
uwuify = "0.2.2"
|
||||
|
|
182
src/main.rs
182
src/main.rs
|
@ -1,127 +1,87 @@
|
|||
use gtk::{
|
||||
glib::BoolError,
|
||||
prelude::{FrameExt, OrientableExt, ScrolledWindowExt, TextBufferExt, TextViewExt, WidgetExt},
|
||||
Inhibit,
|
||||
Orientation,
|
||||
PolicyType,
|
||||
WrapMode,
|
||||
use druid::{
|
||||
widget::{Flex, Label, TextBox},
|
||||
AppLauncher,
|
||||
Data,
|
||||
Lens,
|
||||
LensExt,
|
||||
PlatformError,
|
||||
Widget,
|
||||
WidgetExt,
|
||||
WindowDesc,
|
||||
};
|
||||
use relm::{connect, Relm, Widget};
|
||||
use relm_derive::{widget, Msg};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use util::{ImmutLens, Key};
|
||||
use uwuifier::{round_up16, uwuify_sse};
|
||||
|
||||
fn main() -> Result<(), BoolError> {
|
||||
Win::run(())
|
||||
mod util;
|
||||
|
||||
fn main() -> Result<(), PlatformError> {
|
||||
AppLauncher::with_window(WindowDesc::new(build_ui()).title("guwu")).launch(AppData::default())
|
||||
}
|
||||
|
||||
pub struct Model {
|
||||
relm: Relm<Win>,
|
||||
uwubuf1: Vec<u8>,
|
||||
uwubuf2: Vec<u8>,
|
||||
#[derive(Default, Data, Lens, Clone)]
|
||||
struct AppData {
|
||||
input: String,
|
||||
output: String,
|
||||
|
||||
uwubuf1: Rc<RefCell<Vec<u8>>>,
|
||||
uwubuf2: Rc<RefCell<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn prep_uwubufs(&mut self, len: usize) {
|
||||
if len > self.uwubuf1.len() {
|
||||
self.uwubuf1.resize(len, 0);
|
||||
impl AppData {
|
||||
fn alloc_uwubufs(&self, len: usize) {
|
||||
let mut uwubuf1 = self.uwubuf1.borrow_mut();
|
||||
let mut uwubuf2 = self.uwubuf2.borrow_mut();
|
||||
|
||||
if len > uwubuf1.len() {
|
||||
uwubuf1.resize(len, 0);
|
||||
}
|
||||
|
||||
if len > self.uwubuf2.len() {
|
||||
self.uwubuf2.resize(len, 0);
|
||||
}
|
||||
if len > uwubuf2.len() {
|
||||
uwubuf2.resize(len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Msg)]
|
||||
pub enum Msg {
|
||||
Quit,
|
||||
Edited,
|
||||
}
|
||||
fn uwu(&mut self) {
|
||||
self.alloc_uwubufs(round_up16(self.input.len()) * 16);
|
||||
|
||||
#[widget]
|
||||
impl Widget for Win {
|
||||
fn model(relm: &Relm<Self>, _: ()) -> Model {
|
||||
Model {
|
||||
relm: relm.clone(),
|
||||
uwubuf1: vec![],
|
||||
uwubuf2: vec![],
|
||||
let mut uwubuf1 = self.uwubuf1.borrow_mut();
|
||||
let mut uwubuf2 = self.uwubuf2.borrow_mut();
|
||||
|
||||
let uwuified = uwuify_sse(self.input.as_bytes(), &mut uwubuf1, &mut uwubuf2);
|
||||
|
||||
// SAFETY: uwuify should always return valid UTF-8 if the input string was valid
|
||||
// UTF-8, which we can be sure of, as it's a `String`.
|
||||
self.output = unsafe { String::from_utf8_unchecked(uwuified.to_vec()) };
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, event: Msg) {
|
||||
match event {
|
||||
Msg::Quit => gtk::main_quit(),
|
||||
Msg::Edited => {
|
||||
if let (Some(inbuf), Some(outbuf)) =
|
||||
(self.widgets.input.buffer(), self.widgets.output.buffer())
|
||||
{
|
||||
if let Some(text) = inbuf.text(&inbuf.start_iter(), &inbuf.end_iter(), true) {
|
||||
let len = uwuifier::round_up16(text.len()) * 16;
|
||||
self.model.prep_uwubufs(len);
|
||||
let uwu = uwuifier::uwuify_sse(
|
||||
text.as_bytes(),
|
||||
&mut self.model.uwubuf1[..len],
|
||||
&mut self.model.uwubuf2[..len],
|
||||
);
|
||||
|
||||
if let Ok(uwu) = std::str::from_utf8(uwu) {
|
||||
outbuf.set_text(uwu);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn init_view(&mut self) {
|
||||
self.widgets
|
||||
.input_scroll
|
||||
.set_policy(PolicyType::Never, PolicyType::Automatic);
|
||||
|
||||
self.widgets
|
||||
.output_scroll
|
||||
.set_policy(PolicyType::Never, PolicyType::Automatic);
|
||||
|
||||
if let Some(buf) = self.widgets.input.buffer() {
|
||||
connect!(buf, connect_changed(_), self.model.relm, Msg::Edited);
|
||||
}
|
||||
}
|
||||
|
||||
view! {
|
||||
gtk::Window {
|
||||
gtk::Box {
|
||||
orientation: Orientation::Horizontal,
|
||||
|
||||
gtk::Frame {
|
||||
label: Some("Input"),
|
||||
|
||||
#[name = "input_scroll"]
|
||||
gtk::ScrolledWindow {
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
#[name = "input"]
|
||||
gtk::TextView {
|
||||
wrap_mode: WrapMode::WordChar,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
gtk::Frame {
|
||||
label: Some("Output"),
|
||||
|
||||
#[name = "output_scroll"]
|
||||
gtk::ScrolledWindow {
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
#[name = "output"]
|
||||
gtk::TextView {
|
||||
editable: false,
|
||||
wrap_mode: WrapMode::WordChar,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
delete_event(_, _) => (Msg::Quit, Inhibit(false)),
|
||||
}
|
||||
}
|
||||
fn build_ui() -> impl Widget<AppData> {
|
||||
Flex::row()
|
||||
.with_flex_child(
|
||||
Flex::column()
|
||||
.with_child(Label::new("Input"))
|
||||
.with_flex_child(
|
||||
TextBox::multiline()
|
||||
.with_placeholder("Type here...")
|
||||
.expand()
|
||||
.lens(AppData::input)
|
||||
.controller(Key::new(|_, data: &mut AppData, _, _| data.uwu())),
|
||||
1.,
|
||||
),
|
||||
1.,
|
||||
)
|
||||
.with_spacer(4.)
|
||||
.with_flex_child(
|
||||
Flex::column()
|
||||
.with_child(Label::new("Output"))
|
||||
.with_flex_child(
|
||||
TextBox::multiline()
|
||||
.with_placeholder("UwU")
|
||||
.lens(AppData::output.then(ImmutLens))
|
||||
.expand(),
|
||||
1.,
|
||||
),
|
||||
1.,
|
||||
)
|
||||
}
|
||||
|
|
42
src/util.rs
Normal file
42
src/util.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use druid::{widget::Controller, Data, Env, Event, EventCtx, KeyEvent, Widget, Lens};
|
||||
|
||||
pub struct Key<T> {
|
||||
action: Box<dyn Fn(&mut EventCtx, &mut T, &Env, &KeyEvent)>,
|
||||
}
|
||||
|
||||
impl<T: Data> Key<T> {
|
||||
pub fn new<F: Fn(&mut EventCtx, &mut T, &Env, &KeyEvent) + 'static>(action: F) -> Self {
|
||||
Self {
|
||||
action: Box::new(action),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Data, W: Widget<T>> Controller<T, W> for Key<T> {
|
||||
fn event(
|
||||
&mut self,
|
||||
child: &mut W,
|
||||
ctx: &mut EventCtx,
|
||||
event: &druid::Event,
|
||||
data: &mut T,
|
||||
env: &Env,
|
||||
) {
|
||||
child.event(ctx, event, data, env);
|
||||
|
||||
if let Event::KeyUp(e) = event {
|
||||
(self.action)(ctx, data, env, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImmutLens;
|
||||
|
||||
impl<T: Clone> Lens<T, T> for ImmutLens {
|
||||
fn with<V, F: FnOnce(&T) -> V>(&self, data: &T, f: F) -> V {
|
||||
f(data)
|
||||
}
|
||||
|
||||
fn with_mut<V, F: FnOnce(&mut T) -> V>(&self, data: &mut T, f: F) -> V {
|
||||
f(&mut data.clone())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue