rad/src/main.rs

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(())
}