219 lines
5.6 KiB
Rust
219 lines
5.6 KiB
Rust
#![warn(unused_import_braces)]
|
|
#![warn(unused_lifetimes)]
|
|
#![warn(unused_qualifications)]
|
|
#![feature(once_cell)]
|
|
|
|
// GTK 3
|
|
use gtk::prelude::*;
|
|
use gtk::{Button, Application, ApplicationWindow, Entry, EntryBuffer, Orientation};
|
|
use gtk::Box as gbox;
|
|
|
|
// cli parsing
|
|
use clap::Parser; // cli parser
|
|
mod cli; // where the arguments are defined
|
|
|
|
// Lua
|
|
use mlua::prelude::LuaResult;
|
|
use mlua::Lua;
|
|
use std::io::BufRead;
|
|
|
|
// Originally I thought I should find an alternate method ASAP,
|
|
// but then I realised that this is actually a lot easier than
|
|
// passing around values. It also makes the code easier to read IMO.
|
|
use std::cell::RefCell;
|
|
thread_local! {
|
|
pub static LUA: RefCell<Lua> = RefCell::new(Lua::new());
|
|
}
|
|
|
|
const APPLICATION_ID: &str = "ca.thetechrobo.rad";
|
|
|
|
#[derive(Debug, Clone)]
|
|
#[allow(clippy::enum_variant_names)]
|
|
enum CallbackType {
|
|
OnModify,
|
|
OnClick,
|
|
OnMouseEnter,
|
|
OnMouseLeave,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum WhatDo {
|
|
AddWidget(Widget),
|
|
RemoveWidget(usize),
|
|
Cancel,
|
|
Submit,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Callback {
|
|
raw: String,
|
|
callback_type: CallbackType,
|
|
whatdo: String
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum NewLinePositioning {
|
|
Before,
|
|
After,
|
|
Both
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Positioning {
|
|
NewLine(NewLinePositioning),
|
|
NoNewLine,
|
|
Absolute(f32, f32)
|
|
}
|
|
#[derive(Debug, Clone)]
|
|
enum InputType {
|
|
Password,
|
|
Date,
|
|
Normal,
|
|
Number,
|
|
Character
|
|
}
|
|
#[derive(Debug, Clone)]
|
|
enum Widget {
|
|
Label(String),
|
|
Button(String, Callback),
|
|
InputBox(String, Callback, InputType),
|
|
Checkbox(bool, Callback), // the bool is the default value
|
|
CancelButton,
|
|
SubmitButton,
|
|
}
|
|
|
|
// the master settings struct
|
|
#[derive(Debug)]
|
|
struct Window {
|
|
title: String,
|
|
widgets: Vec<Widget>,
|
|
height: i32,
|
|
width: i32,
|
|
app: Option<Application>,
|
|
window: Option<ApplicationWindow>
|
|
}
|
|
|
|
// I'm going to try to avoid calling GTK apart from these helper functions.
|
|
// That should make upgrading to a breaking version easier.
|
|
|
|
fn create_app(title: String, width: i32, height: i32, mut window: Window) -> Application { // maybe we can put this back in the impl with an Rc<RefCell<T>>?
|
|
let app = Application::builder()
|
|
.application_id(APPLICATION_ID)
|
|
.build();
|
|
app.connect_activate(move |app| {
|
|
// We create the main window.
|
|
let lua_script = r#"print("21")"#;
|
|
LUA.with(|st| {
|
|
let _ = st.borrow_mut().load(lua_script).exec();
|
|
});
|
|
let win = ApplicationWindow::builder()
|
|
.application(app)
|
|
.default_width(width)
|
|
.default_height(height)
|
|
.title(&title)
|
|
.build();
|
|
|
|
let bobox = gbox::new(Orientation::Horizontal, 10);
|
|
win.add(&bobox);
|
|
|
|
let button = Button::with_label("Click me!");
|
|
button.connect_clicked(|_| {
|
|
eprintln!("Clicked!");
|
|
});
|
|
bobox.add(&button);
|
|
|
|
let buffer = EntryBuffer::new(Some("hi"));
|
|
let input = Entry::with_buffer(&buffer);
|
|
input.connect_changed(|e| {
|
|
let uuid = e.widget_name();
|
|
LUA.with(|st| {
|
|
let lua = st.borrow_mut();
|
|
let script = format!(r#"
|
|
for key, func in ipairs(input_changed) do
|
|
func("{}")
|
|
end"#, uuid);
|
|
lua.load(&script).exec().expect("Failed to run Lua");
|
|
});
|
|
set_title(get_window_from_widget(e.toplevel().unwrap()).unwrap(), &uuid);
|
|
});
|
|
println!("{}", add_widget(bobox, input));
|
|
|
|
// Don't forget to make all widgets visible.
|
|
win.show_all();
|
|
});
|
|
window.app = Some(app);
|
|
let app = window.app.expect("The application disappeared. (did you forget to call .init?)");
|
|
let pseudo_args: [&str; 0] = [];
|
|
app.run_with_args(&pseudo_args); // prevents GTK from trying to parse the command-line arguments; we have clap for that
|
|
app
|
|
}
|
|
|
|
fn get_window_from_widget<T: IsA<gtk::Widget>>(widget: T) -> Option<gtk::gdk::Window> { // todo: IsA<gtk::Widget> doesn't work, but we should support it
|
|
widget.toplevel().unwrap().window()
|
|
}
|
|
|
|
fn set_title(window: gtk::gdk::Window, text: &str) {
|
|
window.set_title(text);
|
|
}
|
|
|
|
fn add_widget<T: IsA<gtk::Widget>>(cont: gtk::Box, widget: T) -> String {
|
|
let uuid: String = uuidv7::create();
|
|
widget.set_widget_name(&uuid);
|
|
cont.add(&widget);
|
|
uuid
|
|
}
|
|
|
|
impl Window {
|
|
fn init(&mut self, app: Application) {
|
|
self.app = Some(app);
|
|
}
|
|
fn remove_widget(&mut self, id: usize) -> usize {
|
|
self.widgets.remove(id);
|
|
self.widgets.len()
|
|
}
|
|
fn update_title(&mut self, title: String, window: ApplicationWindow) {
|
|
self.title = title;
|
|
window.set_title(&self.title);
|
|
}
|
|
/// Will output in a yad-compatible format.
|
|
fn submit(&mut self) -> String {
|
|
unimplemented!()
|
|
}
|
|
}
|
|
|
|
// End of GTK.
|
|
|
|
fn item() -> LuaResult<()> {
|
|
println!("Item!");
|
|
Ok(())
|
|
}
|
|
|
|
fn setup_lua() -> LuaResult<()> {
|
|
LUA.with(|st| -> LuaResult<()> {
|
|
let lua = st.borrow_mut();
|
|
let f = lua.create_function(|_, ()| -> LuaResult<()> {
|
|
item()?;
|
|
Ok(())
|
|
})?;
|
|
lua.globals().set("item", f)?;
|
|
lua.load(r#"input_changed = {item}"#).exec()?;
|
|
Ok(())
|
|
})?;
|
|
Ok(())
|
|
}
|
|
|
|
fn main() -> LuaResult<()> {
|
|
setup_lua()?;
|
|
let args = cli::Args::parse();
|
|
let window = Window {
|
|
title: args.title,
|
|
widgets: Vec::new(),
|
|
width: args.width,
|
|
height: args.height,
|
|
window: None,
|
|
app: None
|
|
};
|
|
create_app(window.title.clone(), window.width, window.height, window);
|
|
Ok(())
|
|
}
|