rad/src/main.rs

219 lines
5.6 KiB
Rust
Raw Normal View History

2022-08-08 01:55:54 +00:00
#![warn(unused_import_braces)]
#![warn(unused_lifetimes)]
#![warn(unused_qualifications)]
#![feature(once_cell)]
2022-07-22 19:17:44 +00:00
// GTK 3
use gtk::prelude::*;
use gtk::{Button, Application, ApplicationWindow, Entry, EntryBuffer, Orientation};
use gtk::Box as gbox;
2022-07-22 19:17:44 +00:00
// cli parsing
use clap::Parser; // cli parser
mod cli; // where the arguments are defined
2022-08-12 14:30:04 +00:00
// Lua
use mlua::prelude::LuaResult;
use mlua::Lua;
2022-08-12 14:30:04 +00:00
use std::io::BufRead;
2022-08-08 01:55:54 +00:00
// 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.
2022-08-05 01:10:59 +00:00
use std::cell::RefCell;
thread_local! {
pub static LUA: RefCell<Lua> = RefCell::new(Lua::new());
}
const APPLICATION_ID: &str = "ca.thetechrobo.rad";
2022-07-22 19:17:44 +00:00
#[derive(Debug, Clone)]
2022-08-08 01:55:54 +00:00
#[allow(clippy::enum_variant_names)]
2022-05-18 21:52:48 +00:00
enum CallbackType {
OnModify,
OnClick,
OnMouseEnter,
OnMouseLeave,
}
2022-07-22 19:17:44 +00:00
#[derive(Debug, Clone)]
2022-05-18 21:52:48 +00:00
enum WhatDo {
AddWidget(Widget),
RemoveWidget(usize),
Cancel,
Submit,
}
2022-07-22 19:17:44 +00:00
#[derive(Debug, Clone)]
2022-05-18 21:52:48 +00:00
struct Callback {
raw: String,
callback_type: CallbackType,
whatdo: String
}
2022-07-22 19:17:44 +00:00
#[derive(Debug, Clone)]
2022-05-18 21:52:48 +00:00
enum NewLinePositioning {
Before,
After,
Both
}
2022-07-22 19:17:44 +00:00
#[derive(Debug, Clone)]
enum Positioning {
NewLine(NewLinePositioning),
2022-05-18 21:52:48 +00:00
NoNewLine,
Absolute(f32, f32)
}
2022-07-22 19:17:44 +00:00
#[derive(Debug, Clone)]
2022-05-18 21:52:48 +00:00
enum InputType {
Password,
Date,
Normal,
2022-05-18 21:52:48 +00:00
Number,
Character
}
2022-07-22 19:17:44 +00:00
#[derive(Debug, Clone)]
2022-05-18 21:52:48 +00:00
enum Widget {
Label(String),
Button(String, Callback),
InputBox(String, Callback, InputType),
Checkbox(bool, Callback), // the bool is the default value
CancelButton,
SubmitButton,
}
2022-07-22 19:17:44 +00:00
// the master settings struct
#[derive(Debug)]
2022-05-18 21:52:48 +00:00
struct Window {
title: String,
widgets: Vec<Widget>,
height: i32,
width: i32,
app: Option<Application>,
window: Option<ApplicationWindow>
2022-07-22 19:17:44 +00:00
}
// 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>>?
2022-07-22 19:17:44 +00:00
let app = Application::builder()
.application_id(APPLICATION_ID)
.build();
app.connect_activate(move |app| {
// We create the main window.
2022-08-05 01:10:59 +00:00
let lua_script = r#"print("21")"#;
LUA.with(|st| {
let _ = st.borrow_mut().load(lua_script).exec();
});
2022-07-22 19:17:44 +00:00
let win = ApplicationWindow::builder()
.application(app)
.default_width(width)
.default_height(height)
.title(&title)
2022-07-22 19:17:44 +00:00
.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);
2022-08-05 01:10:59 +00:00
let buffer = EntryBuffer::new(Some("hi"));
let input = Entry::with_buffer(&buffer);
input.connect_changed(|e| {
2022-08-12 16:32:25 +00:00
let uuid = e.widget_name();
2022-08-05 01:10:59 +00:00
LUA.with(|st| {
let lua = st.borrow_mut();
2022-08-12 16:32:25 +00:00
let script = format!(r#"
for key, func in ipairs(input_changed) do
func("{}")
end"#, uuid);
lua.load(&script).exec().expect("Failed to run Lua");
2022-08-05 01:10:59 +00:00
});
2022-08-12 16:32:25 +00:00
set_title(get_window_from_widget(e.toplevel().unwrap()).unwrap(), &uuid);
});
2022-08-08 01:55:54 +00:00
println!("{}", add_widget(bobox, input));
2022-07-22 19:17:44 +00:00
// 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
2022-07-22 19:17:44 +00:00
app
2022-05-18 21:52:48 +00:00
}
2022-08-08 01:55:54 +00:00
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);
}
2022-08-08 01:55:54 +00:00
fn add_widget<T: IsA<gtk::Widget>>(cont: gtk::Box, widget: T) -> String {
let uuid: String = uuidv7::create();
2022-08-12 16:32:25 +00:00
widget.set_widget_name(&uuid);
2022-08-08 01:55:54 +00:00
cont.add(&widget);
uuid
}
2022-05-18 21:52:48 +00:00
impl Window {
2022-07-22 19:17:44 +00:00
fn init(&mut self, app: Application) {
self.app = Some(app);
}
2022-05-18 21:52:48 +00:00
fn remove_widget(&mut self, id: usize) -> usize {
self.widgets.remove(id);
self.widgets.len()
}
fn update_title(&mut self, title: String, window: ApplicationWindow) {
2022-05-18 21:52:48 +00:00
self.title = title;
window.set_title(&self.title);
2022-05-18 21:52:48 +00:00
}
/// Will output in a yad-compatible format.
fn submit(&mut self) -> String {
unimplemented!()
}
}
2022-07-22 19:20:44 +00:00
// End of GTK.
fn item() -> LuaResult<()> {
println!("Item!");
Ok(())
}
fn setup_lua() -> LuaResult<()> {
2022-08-05 01:10:59 +00:00
LUA.with(|st| -> LuaResult<()> {
let lua = st.borrow_mut();
let f = lua.create_function(|_, ()| -> LuaResult<()> {
item()?;
Ok(())
})?;
2022-08-12 14:30:04 +00:00
lua.globals().set("item", f)?;
2022-08-12 16:32:25 +00:00
lua.load(r#"input_changed = {item}"#).exec()?;
Ok(())
2022-08-08 01:55:54 +00:00
})?;
Ok(())
}
fn main() -> LuaResult<()> {
setup_lua()?;
let args = cli::Args::parse();
2022-08-08 01:55:54 +00:00
let window = Window {
title: args.title,
widgets: Vec::new(),
width: args.width,
height: args.height,
window: None,
2022-07-22 19:17:44 +00:00
app: None
};
2022-08-05 01:10:59 +00:00
create_app(window.title.clone(), window.width, window.height, window);
Ok(())
2022-05-18 21:52:48 +00:00
}