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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gtk = "^0.14.0"
|
druid = { git = "https://github.com/linebender/druid.git" }
|
||||||
relm = "^0.22.0"
|
|
||||||
relm-derive = "^0.22.0"
|
|
||||||
uwuify = "0.2.2"
|
uwuify = "0.2.2"
|
||||||
|
|
182
src/main.rs
182
src/main.rs
|
@ -1,127 +1,87 @@
|
||||||
use gtk::{
|
use druid::{
|
||||||
glib::BoolError,
|
widget::{Flex, Label, TextBox},
|
||||||
prelude::{FrameExt, OrientableExt, ScrolledWindowExt, TextBufferExt, TextViewExt, WidgetExt},
|
AppLauncher,
|
||||||
Inhibit,
|
Data,
|
||||||
Orientation,
|
Lens,
|
||||||
PolicyType,
|
LensExt,
|
||||||
WrapMode,
|
PlatformError,
|
||||||
|
Widget,
|
||||||
|
WidgetExt,
|
||||||
|
WindowDesc,
|
||||||
};
|
};
|
||||||
use relm::{connect, Relm, Widget};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
use relm_derive::{widget, Msg};
|
use util::{ImmutLens, Key};
|
||||||
|
use uwuifier::{round_up16, uwuify_sse};
|
||||||
|
|
||||||
fn main() -> Result<(), BoolError> {
|
mod util;
|
||||||
Win::run(())
|
|
||||||
|
fn main() -> Result<(), PlatformError> {
|
||||||
|
AppLauncher::with_window(WindowDesc::new(build_ui()).title("guwu")).launch(AppData::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Model {
|
#[derive(Default, Data, Lens, Clone)]
|
||||||
relm: Relm<Win>,
|
struct AppData {
|
||||||
uwubuf1: Vec<u8>,
|
input: String,
|
||||||
uwubuf2: Vec<u8>,
|
output: String,
|
||||||
|
|
||||||
|
uwubuf1: Rc<RefCell<Vec<u8>>>,
|
||||||
|
uwubuf2: Rc<RefCell<Vec<u8>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
impl AppData {
|
||||||
pub fn prep_uwubufs(&mut self, len: usize) {
|
fn alloc_uwubufs(&self, len: usize) {
|
||||||
if len > self.uwubuf1.len() {
|
let mut uwubuf1 = self.uwubuf1.borrow_mut();
|
||||||
self.uwubuf1.resize(len, 0);
|
let mut uwubuf2 = self.uwubuf2.borrow_mut();
|
||||||
|
|
||||||
|
if len > uwubuf1.len() {
|
||||||
|
uwubuf1.resize(len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if len > self.uwubuf2.len() {
|
if len > uwubuf2.len() {
|
||||||
self.uwubuf2.resize(len, 0);
|
uwubuf2.resize(len, 0);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Msg)]
|
|
||||||
pub enum Msg {
|
|
||||||
Quit,
|
|
||||||
Edited,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[widget]
|
|
||||||
impl Widget for Win {
|
|
||||||
fn model(relm: &Relm<Self>, _: ()) -> Model {
|
|
||||||
Model {
|
|
||||||
relm: relm.clone(),
|
|
||||||
uwubuf1: vec![],
|
|
||||||
uwubuf2: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, event: Msg) {
|
fn uwu(&mut self) {
|
||||||
match event {
|
self.alloc_uwubufs(round_up16(self.input.len()) * 16);
|
||||||
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) {
|
let mut uwubuf1 = self.uwubuf1.borrow_mut();
|
||||||
outbuf.set_text(uwu);
|
let mut uwubuf2 = self.uwubuf2.borrow_mut();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_view(&mut self) {
|
let uwuified = uwuify_sse(self.input.as_bytes(), &mut uwubuf1, &mut uwubuf2);
|
||||||
self.widgets
|
|
||||||
.input_scroll
|
|
||||||
.set_policy(PolicyType::Never, PolicyType::Automatic);
|
|
||||||
|
|
||||||
self.widgets
|
// SAFETY: uwuify should always return valid UTF-8 if the input string was valid
|
||||||
.output_scroll
|
// UTF-8, which we can be sure of, as it's a `String`.
|
||||||
.set_policy(PolicyType::Never, PolicyType::Automatic);
|
self.output = unsafe { String::from_utf8_unchecked(uwuified.to_vec()) };
|
||||||
|
|
||||||
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