├── .cargo └── config.toml ├── .github └── workflows │ └── deploy.yaml ├── .gitignore ├── Cargo.toml ├── README.md ├── build_web.sh ├── egui.d.lua ├── examples ├── basic.rs ├── index.html └── script.lua └── src └── lib.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | 2 | # primarily for deploying example to github pages 3 | [target.wasm32-unknown-emscripten] 4 | rustflags = [ 5 | "-C", 6 | "link-arg=-s", 7 | "-C", 8 | "link-arg=USE_GLFW=3", # for glfw support. 9 | "-C", 10 | "link-arg=-s", 11 | "-C", 12 | "link-arg=FULL_ES2", # for opengl es 2 emulation 13 | "-C", 14 | "link-arg=-s", 15 | "-C", 16 | "link-arg=FULL_ES3", # for opengl es 3 emulation 17 | "-C", 18 | "link-arg=-s", 19 | "-C", 20 | "link-arg=MAX_WEBGL_VERSION=2 ", # to make sure that webgl2 is enabled. 21 | "-C", 22 | "link-arg=-s", 23 | "-C", 24 | "link-arg=MIN_WEBGL_VERSION=2", # to disable webgl1 completely, and use webgl2 exclusively. 25 | "-C", 26 | "link-arg=-s", 27 | "-C", 28 | "link-arg=ERROR_ON_UNDEFINED_SYMBOLS=0", # emscripten is very brittle sometimes with missing symbols. you can remove this flag if you don't have any problem with this. 29 | ] 30 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yaml: -------------------------------------------------------------------------------- 1 | name: Github Pages 2 | 3 | on: [push] 4 | 5 | permissions: 6 | contents: write 7 | 8 | jobs: 9 | build-github-pages: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 # repo checkout 13 | - uses: mymindstorm/setup-emsdk@v13 # setup emscripten toolchain 14 | with: 15 | version: 3.1.52 16 | - uses: actions-rs/toolchain@v1 # get rust toolchain for wasm 17 | with: 18 | toolchain: stable 19 | target: wasm32-unknown-emscripten 20 | override: true 21 | - name: Check Emscripten # just to make sure that emscripten is properly installed 22 | run: emcc -v 23 | - name: Rust Cache # cache the rust build artefacts 24 | uses: Swatinem/rust-cache@v1 25 | - name: Build # bash script to build and put all required files inside a directory called "dist" 26 | run: ./build_web.sh 27 | - name: Deploy 28 | uses: JamesIves/github-pages-deploy-action@v4 29 | with: 30 | folder: dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | /Cargo.lock 3 | /dist -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "luaegui" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | 10 | [dependencies] 11 | egui = "0.26" 12 | mlua = { version = "0.9", features = ["luau-vector4"] } 13 | 14 | [dev-dependencies] 15 | egui_commonmark = "0.13" 16 | 17 | # disable default glfw features on emscripten 18 | [target.'cfg(target_arch = "wasm32")'.dev-dependencies] 19 | egui_overlay = { version = "0.8.1", default-features = false, features = [ 20 | "egui_default", 21 | "three_d", 22 | ] } 23 | # enable default glfw feature (enabled by default) on non-wasm platforms. 24 | [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] 25 | egui_overlay = { version = "0.8.1" } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unmaintained 2 | ### This repo is abandoned and you are welcome to fork it. 3 | 4 | ## Deprecation Reason: Performance 5 | 1. Due to the immediate mode nature of `egui`, the scripts need to run *every* frame (average of 60 fps atleast). 6 | 2. Ui code creates *lots* of temporary `use once` objects, which means lots of garbage collectable objects for a reasonably complex ui script. It is also expensive to create/recreate these objects every frame. 7 | 1. Builder pattern creates a struct for every container/widget. eg: `Window::new("hello")` or `RichText::new("text").color(egui.blue)` etc.. 8 | 2. return values of widget `ui` fn like `Response` also create userdata objects. 9 | 3. each `&mut Ui` needs to create a new userdata object to bind that reference to. 10 | 4. Even value types like `Rect` will need to be userdata. 11 | 3. Due to a large amount of function calls between native host egui and the script, JIT also sucks at optimizing this. 12 | 4. All the closures also require jumping between host and guest scopes which have some "setup"/"teardown" costs 13 | 14 | This project might still work for some people, and I would encourage them to fork this repo. 15 | But I would like to experiment with retained mode toolkits now, where you only scripts on events. 16 | 17 | 18 | 19 | # luaegui 20 | egui bindings for mlua. 21 | 22 | Just look at the example for basic usage. You can play with the web version live at https://coderedart.github.io/luaegui/ 23 | 24 | There should be a window called `Script Editor` where you can edit the lua code live within egui. 25 | After editing the code, just click the `run` button on top to execute that code in the lua vm. 26 | If there was any error, it will be printed to stdout/console(on web). 27 | Below the code editor, you can see how long the `gui_run` fn takes every frame. 28 | ### gui_run 29 | Every frame, the example will try to call the `gui_run` fn (if it exists) and gives it egui context as the argument. 30 | If the fn fails for some reason, the error will be printed to stdout/console. 31 | 32 | ### egui 33 | We provide a global table called `egui` which contains most constants + types + functions to be used by lua scripts. 34 | for example, you can create a `Window` using `local window = egui.window.new("my window title");` 35 | 36 | ### Developer Experience 37 | Because we don't really have a way to properly document host api in mlua yet, we will do this manually. For now, we provide a type definition file (WIP) `egui.d.lua`. 38 | 39 | 1. Install `Luau Language Server` extension by `JohnnyMorganz` in vscode 40 | 2. copy `egui.d.lua` file from thi repo to your lua project folder 41 | 3. In the settings Ui `luau-lsp.types.definitionFiles`, add the file `egui.d.lua` 42 | 4. Now, you have autocompletion, as well as linting (to a reasonable extent) when you want to use egui. 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /build_web.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # this script is used to build and copy the files into a directory called dist. 3 | set -ex 4 | 5 | echo "building for emscripten target" 6 | cargo build --example=basic --target=wasm32-unknown-emscripten --release 7 | 8 | echo "copying files to dist directory" 9 | mkdir -p dist 10 | cp target/wasm32-unknown-emscripten/release/examples/basic.wasm dist 11 | cp target/wasm32-unknown-emscripten/release/examples/basic.js dist 12 | cp examples/index.html dist -------------------------------------------------------------------------------- /egui.d.lua: -------------------------------------------------------------------------------- 1 | declare class Context 2 | function clone(self): Context 3 | end 4 | declare class Response 5 | function clicked(self): boolean 6 | end 7 | declare class InnerResponse 8 | response: Response 9 | inner: any 10 | end 11 | 12 | declare class Ui 13 | function add_enabled_ui(self, enabled: boolean, uifn: (Ui) -> any): InnerResponse 14 | function add_space(self, space: number): () 15 | function add_visible_ui(self, visible: boolean, uifn: (Ui) -> any): InnerResponse 16 | 17 | function label(self, text: string): Response 18 | function text_edit_singleline(self, text: string): Response 19 | function button(self, text: string): Response 20 | 21 | end 22 | declare class TopBottomPanel 23 | function show(self, ctx: Context, uifn: (Ui) -> any): any 24 | end 25 | declare class Window 26 | function show(self, ctx: Context, uifn: (Ui) -> any): any 27 | end 28 | declare egui: { 29 | top_bottom_panel: { 30 | top: (string) -> TopBottomPanel, 31 | bottom: (string) -> TopBottomPanel 32 | }, 33 | window: { 34 | new: (string) -> Window 35 | } 36 | } -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | use egui_overlay::*; 2 | use mlua::Function; 3 | 4 | fn main() { 5 | fake_main(); 6 | } 7 | 8 | const LUA_CODE: &str = include_str!("script.lua"); 9 | 10 | struct AppData { 11 | pub script_time: std::time::Duration, 12 | pub lua: mlua::Lua, 13 | pub code: String, 14 | pub markdown_cache: egui_commonmark::CommonMarkCache, 15 | } 16 | 17 | impl EguiOverlay for AppData { 18 | fn gui_run( 19 | &mut self, 20 | egui_context: &egui::Context, 21 | _default_gfx_backend: &mut egui_render_three_d::ThreeDBackend, 22 | _glfw_backend: &mut egui_window_glfw_passthrough::GlfwBackend, 23 | ) { 24 | use egui::*; 25 | let ctx = egui_context.clone(); 26 | Window::new("README").show(&ctx, |ui| { 27 | egui_commonmark::CommonMarkViewer::new("readme renderer").show( 28 | ui, 29 | &mut self.markdown_cache, 30 | README, 31 | ); 32 | }); 33 | Window::new("Script Editor") 34 | .min_width(400.0) 35 | .show(&ctx, |ui| { 36 | if ui.button("run").clicked() { 37 | if let Err(e) = self.lua.load(&self.code).exec() { 38 | eprintln!("lua load error: {e:?}"); 39 | } 40 | } 41 | if !self.lua.globals().contains_key("gui_run").unwrap() { 42 | ui.colored_label(Color32::RED, "gui_run fn is not defined"); 43 | } 44 | ui.add( 45 | egui::TextEdit::multiline(&mut self.code) 46 | .code_editor() 47 | .desired_width(400.0), 48 | ); 49 | ui.horizontal(|ui| { 50 | ui.label("script execution time (micros): "); 51 | ui.label(format!("{}", self.script_time.as_micros())); 52 | }); 53 | }); 54 | let start = std::time::Instant::now(); 55 | if let Ok(f) = self.lua.globals().get::<_, Function>("gui_run") { 56 | let c = self.lua.create_any_userdata(ctx).unwrap(); 57 | let _: () = f.call(c).unwrap(); 58 | } 59 | self.script_time = start.elapsed(); 60 | } 61 | } 62 | 63 | fn fake_main() { 64 | let lua = mlua::Lua::new(); 65 | luaegui::register_egui_bindings(&lua).unwrap(); 66 | let app = AppData { 67 | lua, 68 | code: LUA_CODE.to_string(), 69 | script_time: std::time::Duration::ZERO, 70 | markdown_cache: Default::default(), 71 | }; 72 | start(app) 73 | } 74 | 75 | const README: &str = include_str!("../README.md"); 76 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/script.lua: -------------------------------------------------------------------------------- 1 | my_data = { 2 | text = "my text" 3 | } 4 | -- a function to run inside the window 5 | function window_ui(ui) 6 | ui:label(my_data.text); 7 | ui:text_edit_singleline(my_data); 8 | if ui:button("cute button"):clicked() then 9 | print("cute button pressed."); 10 | end 11 | end 12 | -- will be called every frame with egui Context as arg 13 | _G.gui_run = function (ctx) 14 | local new_window = egui.window.new("my lua window"); 15 | new_window:show(ctx, window_ui); 16 | end 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use egui::{ 2 | epaint::Shadow, 3 | load::SizedTexture, 4 | style::{Spacing, WidgetVisuals}, 5 | Align, Align2, Area, CentralPanel, Color32, Context, Direction, Frame, Id, LayerId, Layout, 6 | Margin, Order, PointerButton, Pos2, Rect, RichText, Rounding, Sense, SidePanel, Stroke, Style, 7 | TextStyle, TextureHandle, TopBottomPanel, Ui, Vec2, WidgetText, Window, 8 | }; 9 | use mlua::{ 10 | AnyUserData, Function, Lua, MultiValue, Result, Table, UserDataFields, UserDataMethods, 11 | UserDataRef, UserDataRefMut, UserDataRegistry, Value, Vector, 12 | }; 13 | 14 | trait LuaHelperTrait: Sized { 15 | fn from_lua(value: Value) -> Result; 16 | fn to_lua(value: Self, lua: &Lua) -> Result; 17 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()>; 18 | } 19 | 20 | pub fn register_egui_bindings(lua: &Lua) -> mlua::Result<()> { 21 | let et = lua.create_table()?; 22 | let egui_table = &et; 23 | Align::add_to_lua(lua, egui_table)?; 24 | Align2::add_to_lua(lua, egui_table)?; 25 | Color32::add_to_lua(lua, egui_table)?; 26 | Direction::add_to_lua(lua, egui_table)?; 27 | Id::add_to_lua(lua, egui_table)?; 28 | Margin::add_to_lua(lua, egui_table)?; 29 | PointerButton::add_to_lua(lua, egui_table)?; 30 | Pos2::add_to_lua(lua, egui_table)?; 31 | Rect::add_to_lua(lua, egui_table)?; 32 | RichText::add_to_lua(lua, egui_table)?; 33 | Rounding::add_to_lua(lua, egui_table)?; 34 | Sense::add_to_lua(lua, egui_table)?; 35 | Stroke::add_to_lua(lua, egui_table)?; 36 | TextStyle::add_to_lua(lua, egui_table)?; 37 | Vec2::add_to_lua(lua, egui_table)?; 38 | WidgetText::add_to_lua(lua, egui_table)?; 39 | 40 | add_area(lua, egui_table)?; 41 | add_context(lua, egui_table)?; 42 | add_frame(lua, egui_table)?; 43 | add_layer_id(lua, egui_table)?; 44 | add_layout(lua, egui_table)?; 45 | add_response(lua)?; 46 | add_shadow(lua, egui_table)?; 47 | add_spacing(lua, egui_table)?; 48 | add_style(lua, egui_table)?; 49 | add_ui(lua, egui_table)?; 50 | add_widget_visuals(lua, egui_table)?; 51 | add_window(lua, egui_table)?; 52 | add_central_panel(lua, egui_table)?; 53 | add_side_panel(lua, egui_table)?; 54 | add_top_bottom_panel(lua, egui_table)?; 55 | egui_table.set_readonly(true); 56 | lua.globals().set("egui", et)?; 57 | Ok(()) 58 | } 59 | 60 | fn add_context(lua: &Lua, _: &Table) -> mlua::Result<()> { 61 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 62 | reg.add_method("request_repaint", |_, this, ()| { 63 | this.request_repaint(); 64 | Ok(()) 65 | }); 66 | reg.add_method("request_repaint_after", |_, this, duration: f64| { 67 | this.request_repaint_after(std::time::Duration::from_secs_f64(duration)); 68 | Ok(()) 69 | }); 70 | })?; 71 | Ok(()) 72 | } 73 | 74 | impl LuaHelperTrait for Id { 75 | fn from_lua(value: Value) -> Result { 76 | Ok(match value { 77 | Value::Nil => Id::NULL, 78 | Value::String(s) => Id::NULL.with(s.to_str().unwrap_or_default()), 79 | Value::UserData(u) => { 80 | *u.borrow() 81 | .map_err(|_e| mlua::Error::FromLuaConversionError { 82 | from: "Value", 83 | to: "Id", 84 | message: Some( 85 | "The variant of value is not suitable for converting to Id".to_string(), 86 | ), 87 | })? 88 | } 89 | _ => { 90 | return Err(mlua::Error::FromLuaConversionError { 91 | from: "Value", 92 | to: "Id", 93 | message: Some( 94 | "The variant of value is not suitable for converting to Id".to_string(), 95 | ), 96 | }) 97 | } 98 | }) 99 | } 100 | 101 | fn to_lua(value: Self, lua: &Lua) -> Result { 102 | lua.create_any_userdata(value).map(Value::UserData) 103 | } 104 | 105 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()> { 106 | let id: Table<'_> = lua.create_table()?; 107 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 108 | reg.add_method("with", |lua, this, value: Value| { 109 | lua.create_any_userdata(match value { 110 | Value::Nil => Id::NULL, 111 | Value::Boolean(b) => this.with(b), 112 | Value::Integer(b) => this.with(b), 113 | Value::String(b) => this.with(b), 114 | _ => { 115 | return Err(mlua::Error::FromLuaConversionError { 116 | from: "value", 117 | to: "hash_for_egui_id", 118 | message: None, 119 | }) 120 | } 121 | }) 122 | }); 123 | reg.add_method("short_debug_format", |_, this, ()| { 124 | Ok(this.short_debug_format()) 125 | }); 126 | })?; 127 | id.set("null", lua.create_any_userdata(Id::NULL)?)?; 128 | egui_table.set("id", id)?; 129 | Ok(()) 130 | } 131 | } 132 | 133 | fn add_widget_visuals(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 134 | let id = lua.create_table()?; 135 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 136 | reg.add_field_method_get("bg_fill", |lua, this| Color32::to_lua(this.bg_fill, lua)); 137 | reg.add_field_method_get("weak_bg_fill", |lua, this| { 138 | Color32::to_lua(this.weak_bg_fill, lua) 139 | }); 140 | reg.add_field_method_get("bg_stroke", |lua, this| Stroke::to_lua(this.bg_stroke, lua)); 141 | reg.add_field_method_get("rounding", |lua, this| Rounding::to_lua(this.rounding, lua)); 142 | reg.add_field_method_get("fg_stroke", |lua, this| Stroke::to_lua(this.fg_stroke, lua)); 143 | reg.add_field_method_get("expansion", |_, this| Ok(this.expansion)); 144 | 145 | reg.add_field_method_set("bg_fill", |_, this, value: Value| { 146 | this.bg_fill = Color32::from_lua(value)?; 147 | Ok(()) 148 | }); 149 | reg.add_field_method_set("weak_bg_fill", |_, this, value: Value| { 150 | this.weak_bg_fill = Color32::from_lua(value)?; 151 | Ok(()) 152 | }); 153 | reg.add_field_method_set("bg_stroke", |_, this, value: Value| { 154 | this.bg_stroke = Stroke::from_lua(value)?; 155 | Ok(()) 156 | }); 157 | reg.add_field_method_set("rounding", |_, this, value: Value| { 158 | this.rounding = Rounding::from_lua(value)?; 159 | Ok(()) 160 | }); 161 | reg.add_field_method_set("fg_stroke", |_, this, value: Value| { 162 | this.fg_stroke = Stroke::from_lua(value)?; 163 | Ok(()) 164 | }); 165 | reg.add_field_method_set("expansion", |_, this, value: f32| { 166 | this.expansion = value; 167 | Ok(()) 168 | }); 169 | })?; 170 | id.set("null", lua.create_any_userdata(Id::NULL)?)?; 171 | egui_table.set("id", id)?; 172 | Ok(()) 173 | } 174 | 175 | fn add_layout(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 176 | let layout = lua.create_table()?; 177 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 178 | reg.add_field_method_get("main_dir", |lua, this| { 179 | Direction::to_lua(this.main_dir, lua) 180 | }); 181 | reg.add_field_method_get("main_wrap", |_, this| Ok(this.main_wrap)); 182 | reg.add_field_method_get("main_align", |lua, this| { 183 | Align::to_lua(this.main_align, lua) 184 | }); 185 | reg.add_field_method_get("main_justify", |_, this| Ok(this.main_justify)); 186 | reg.add_field_method_get("cross_align", |lua, this| { 187 | Align::to_lua(this.cross_align, lua) 188 | }); 189 | reg.add_field_method_get("cross_justify", |_, this| Ok(this.cross_justify)); 190 | 191 | reg.add_field_method_set("main_dir", |_, this, value: Value| { 192 | this.main_dir = Direction::from_lua(value)?; 193 | Ok(()) 194 | }); 195 | reg.add_field_method_set("main_wrap", |_, this, value: bool| { 196 | this.main_wrap = value; 197 | Ok(()) 198 | }); 199 | reg.add_field_method_set("main_align", |_, this, value: Value| { 200 | this.main_align = Align::from_lua(value)?; 201 | Ok(()) 202 | }); 203 | reg.add_field_method_set("main_justify", |_, this, value: bool| { 204 | this.main_justify = value; 205 | Ok(()) 206 | }); 207 | reg.add_field_method_set("cross_align", |_, this, value: Value| { 208 | this.cross_align = Align::from_lua(value)?; 209 | Ok(()) 210 | }); 211 | reg.add_field_method_set("cross_justify", |_, this, value: bool| { 212 | this.cross_justify = value; 213 | Ok(()) 214 | }); 215 | })?; 216 | 217 | egui_table.set("layout", layout)?; 218 | Ok(()) 219 | } 220 | fn add_spacing(lua: &Lua, _egui_table: &Table) -> mlua::Result<()> { 221 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 222 | reg.add_field_method_get("item_spacing", |lua, this| { 223 | Vec2::to_lua(this.item_spacing, lua) 224 | }); 225 | reg.add_field_method_get("window_margin", |lua, this| { 226 | Margin::to_lua(this.window_margin, lua) 227 | }); 228 | reg.add_field_method_get("button_padding", |lua, this| { 229 | Vec2::to_lua(this.button_padding, lua) 230 | }); 231 | reg.add_field_method_get("menu_margin", |lua, this| { 232 | Margin::to_lua(this.menu_margin, lua) 233 | }); 234 | reg.add_field_method_get("indent", |_, this| Ok(this.indent)); 235 | 236 | reg.add_field_method_get("interact_size", |lua, this| { 237 | Vec2::to_lua(this.interact_size, lua) 238 | }); 239 | reg.add_field_method_get("slider_width", |_, this| Ok(this.slider_width)); 240 | reg.add_field_method_get("combo_width", |_, this| Ok(this.combo_width)); 241 | reg.add_field_method_get("text_edit_width", |_, this| Ok(this.text_edit_width)); 242 | reg.add_field_method_get("icon_width", |_, this| Ok(this.icon_width)); 243 | reg.add_field_method_get("icon_width_inner", |_, this| Ok(this.icon_width_inner)); 244 | reg.add_field_method_get("icon_spacing", |_, this| Ok(this.icon_spacing)); 245 | reg.add_field_method_get("tooltip_width", |_, this| Ok(this.tooltip_width)); 246 | reg.add_field_method_get("indent_ends_with_horizontal_line", |_, this| { 247 | Ok(this.indent_ends_with_horizontal_line) 248 | }); 249 | reg.add_field_method_get("combo_height", |_, this| Ok(this.combo_height)); 250 | // reg.add_field_method_get("scroll_bar_width", |_, this| Ok(this.scroll_bar_width)); 251 | // reg.add_field_method_get("scroll_handle_min_length", |_, this| { 252 | // Ok(this.scroll_handle_min_length) 253 | // }); 254 | // reg.add_field_method_get("scroll_bar_inner_margin", |_, this| { 255 | // Ok(this.scroll_bar_inner_margin) 256 | // }); 257 | // reg.add_field_method_get("scroll_bar_outer_margin", |_, this| { 258 | // Ok(this.scroll_bar_outer_margin) 259 | // }); 260 | 261 | // reg.add_field_method_set("weak_bg_fill", |_, this, value: Value| { 262 | // this.weak_bg_fill = Color32::from_lua(value)?; 263 | // Ok(()) 264 | // }); 265 | // reg.add_field_method_set("bg_stroke", |_, this, value: Value| { 266 | // this.bg_stroke = Stroke::from_lua(value)?; 267 | // Ok(()) 268 | // }); 269 | // reg.add_field_method_set("rounding", |_, this, value: Value| { 270 | // this.rounding = Rounding::from_lua(value)?; 271 | // Ok(()) 272 | // }); 273 | // reg.add_field_method_set("fg_stroke", |_, this, value: Value| { 274 | // this.fg_stroke = Stroke::from_lua(value)?; 275 | // Ok(()) 276 | // }); 277 | // reg.add_field_method_set( 278 | // "expansion", 279 | // |_, this, value: f32| Ok(this.expansion = value), 280 | // ); 281 | })?; 282 | Ok(()) 283 | } 284 | fn add_response(lua: &Lua) -> mlua::Result<()> { 285 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 286 | reg.add_method("changed", |_, this, ()| Ok(this.changed())); 287 | reg.add_method("clicked", |_, this, ()| Ok(this.clicked())); 288 | reg.add_method("clicked_by", |_, this, value: Value| { 289 | Ok(this.clicked_by(PointerButton::from_lua(value)?)) 290 | }); 291 | reg.add_method("clicked_elsewhere", |_, this, ()| { 292 | Ok(this.clicked_elsewhere()) 293 | }); 294 | // reg.add_method("context_menu", |_, this, ()| Ok(this.changed())); 295 | reg.add_method("double_clicked", |_, this, ()| Ok(this.double_clicked())); 296 | reg.add_method("double_clicked_by", |_, this, value: Value| { 297 | Ok(this.double_clicked_by(PointerButton::from_lua(value)?)) 298 | }); 299 | reg.add_method("drag_delta", |lua, this, ()| { 300 | Ok(Vec2::to_lua(this.drag_delta(), lua)) 301 | }); 302 | reg.add_method("drag_released", |_, this, ()| Ok(this.drag_released())); 303 | reg.add_method("drag_released_by", |_, this, value: Value| { 304 | Ok(this.drag_released_by(PointerButton::from_lua(value)?)) 305 | }); 306 | reg.add_method("drag_started", |_, this, ()| Ok(this.drag_started())); 307 | reg.add_method("drag_started_by", |_, this, value: Value| { 308 | Ok(this.drag_started_by(PointerButton::from_lua(value)?)) 309 | }); 310 | reg.add_method("dragged", |_, this, ()| Ok(this.dragged())); 311 | reg.add_method("dragged_by", |_, this, value: Value| { 312 | Ok(this.dragged_by(PointerButton::from_lua(value)?)) 313 | }); 314 | reg.add_method("enabled", |_, this, ()| Ok(this.enabled())); 315 | reg.add_method("gained_focus", |_, this, ()| Ok(this.gained_focus())); 316 | reg.add_method("has_focus", |_, this, ()| Ok(this.has_focus())); 317 | reg.add_method("highlight", |lua, this, ()| { 318 | lua.create_any_userdata(this.clone().highlight()) 319 | }); 320 | reg.add_method("hover_pos", |lua, this, ()| { 321 | Ok(this.hover_pos().and_then(|p| Pos2::to_lua(p, lua).ok())) 322 | }); 323 | reg.add_method("hovered", |_, this, ()| Ok(this.hovered())); 324 | }) 325 | } 326 | 327 | fn add_ui(lua: &Lua, _egui_table: &Table) -> mlua::Result<()> { 328 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 329 | reg.add_method_mut( 330 | "add_enabled_ui", 331 | |lua, this, (enabled, add_contents): (bool, Function)| { 332 | let ir = this.add_enabled_ui(enabled, |ui| { 333 | lua.scope(|scope| { 334 | let ui = scope.create_any_userdata_ref_mut(ui)?; 335 | let result: Result = add_contents.call(ui); 336 | result 337 | }) 338 | }); 339 | let r = lua.create_any_userdata(ir.response)?; 340 | let mut i = ir.inner?; 341 | i.push_front(Value::UserData(r)); 342 | Ok(i) 343 | }, 344 | ); 345 | 346 | reg.add_method_mut("add_space", |_, this, amount: f32| { 347 | this.add_space(amount); 348 | Ok(()) 349 | }); 350 | reg.add_method_mut( 351 | "add_visible_ui", 352 | |lua, this, (visible, add_contents): (bool, Function)| { 353 | let ir = this.add_visible_ui(visible, |ui| { 354 | lua.scope(|scope| { 355 | let ui = scope.create_any_userdata_ref_mut(ui)?; 356 | let result: Result = add_contents.call(ui); 357 | result 358 | }) 359 | }); 360 | let r = lua.create_any_userdata(ir.response)?; 361 | let mut i = ir.inner?; 362 | i.push_front(Value::UserData(r)); 363 | Ok(i) 364 | }, 365 | ); 366 | reg.add_method_mut( 367 | "allocate_at_least", 368 | |lua, this, (desired_size, sense): (Value, Value)| { 369 | let (rect, resp) = this.allocate_at_least( 370 | LuaHelperTrait::from_lua(desired_size)?, 371 | LuaHelperTrait::from_lua(sense)?, 372 | ); 373 | 374 | Ok((Rect::to_lua(rect, lua)?, lua.create_any_userdata(resp)?)) 375 | }, 376 | ); 377 | reg.add_method_mut( 378 | "allocate_exact_size", 379 | |lua, this, (desired_size, sense): (Value, Value)| { 380 | let (rect, resp) = this.allocate_exact_size( 381 | LuaHelperTrait::from_lua(desired_size)?, 382 | LuaHelperTrait::from_lua(sense)?, 383 | ); 384 | 385 | Ok((Rect::to_lua(rect, lua)?, lua.create_any_userdata(resp)?)) 386 | }, 387 | ); 388 | reg.add_method_mut( 389 | "allocate_rect", 390 | |lua, this, (rect, sense): (Value, Value)| { 391 | lua.create_any_userdata(this.allocate_rect( 392 | LuaHelperTrait::from_lua(rect)?, 393 | LuaHelperTrait::from_lua(sense)?, 394 | )) 395 | }, 396 | ); 397 | reg.add_method_mut( 398 | "allocate_response", 399 | |lua, this, (desired_size, sense): (Value, Value)| { 400 | lua.create_any_userdata(this.allocate_response( 401 | LuaHelperTrait::from_lua(desired_size)?, 402 | LuaHelperTrait::from_lua(sense)?, 403 | )) 404 | }, 405 | ); 406 | 407 | reg.add_method_mut("allocate_space", |lua, this, desired_size: Value| { 408 | let (id, rect) = this.allocate_space(LuaHelperTrait::from_lua(desired_size)?); 409 | Ok((lua.create_any_userdata(id)?, Rect::to_lua(rect, lua)?)) 410 | }); 411 | reg.add_method_mut( 412 | "allocate_ui", 413 | |lua, this, (desired_size, add_contents): (Value, Function)| { 414 | let ir = this.allocate_ui(Vec2::from_lua(desired_size)?, |ui| { 415 | lua.scope(|scope| { 416 | let ui = scope.create_any_userdata_ref_mut(ui)?; 417 | let result: Result = add_contents.call(ui); 418 | result 419 | }) 420 | }); 421 | let r = lua.create_any_userdata(ir.response)?; 422 | let mut i = ir.inner?; 423 | i.push_front(Value::UserData(r)); 424 | Ok(i) 425 | }, 426 | ); 427 | reg.add_method_mut( 428 | "allocate_ui_at_rect", 429 | |lua, this, (max_rect, add_contents): (Value, Function)| { 430 | let ir = this.allocate_ui_at_rect(Rect::from_lua(max_rect)?, |ui| { 431 | lua.scope(|scope| { 432 | let ui = scope.create_any_userdata_ref_mut(ui)?; 433 | let result: Result = add_contents.call(ui); 434 | result 435 | }) 436 | }); 437 | let r = lua.create_any_userdata(ir.response)?; 438 | let mut i = ir.inner?; 439 | i.push_front(Value::UserData(r)); 440 | Ok(i) 441 | }, 442 | ); 443 | reg.add_method_mut( 444 | "allocate_ui_with_layout", 445 | |lua, this, (desired_size, layout, add_contents): (Value, UserDataRef, Function)| { 446 | let ir = this.allocate_ui_with_layout(Vec2::from_lua(desired_size)?, *layout,|ui| { 447 | lua.scope(|scope| { 448 | let ui = scope.create_any_userdata_ref_mut(ui)?; 449 | let result: Result = add_contents.call(ui); 450 | result 451 | }) 452 | }); 453 | let r = lua.create_any_userdata(ir.response)?; 454 | let mut i = ir.inner?; 455 | i.push_front(Value::UserData(r)); 456 | Ok(i) 457 | }, 458 | ); 459 | reg.add_method("auto_id_with", |lua, this, value: Value| { 460 | lua.create_any_userdata(match value { 461 | Value::Boolean(b) => { 462 | this.auto_id_with(b) 463 | }, 464 | Value::Integer(b) => this.auto_id_with(b), 465 | Value::String(b) => this.auto_id_with(b), 466 | _ => return Err(mlua::Error::external("value type cannot be hashed to get new id")) 467 | }) 468 | }); 469 | reg.add_method("available_height", |_, this, ()|Ok(this.available_height())); 470 | reg.add_method("available_rect_before_wrap", |lua, this, ()| Rect::to_lua(this.available_rect_before_wrap(), lua)); 471 | reg.add_method("avaialble_size", |lua, this, ()| Vec2::to_lua(this.available_size(), lua)); 472 | reg.add_method("avaialble_size_before_wrap", |lua, this, ()| Vec2::to_lua(this.available_size_before_wrap(), lua)); 473 | reg.add_method("available_width", |_, this, ()|Ok(this.available_width())); 474 | reg.add_method_mut("button", |lua, this, value: Value| { 475 | lua.create_any_userdata(this.button(WidgetText::from_lua(value)?)) 476 | }); 477 | reg.add_method_mut( 478 | "centered_and_justified", 479 | |lua, this, add_contents: Function| { 480 | let ir = this.centered_and_justified(|ui| { 481 | lua.scope(|scope| { 482 | let ui = scope.create_any_userdata_ref_mut(ui)?; 483 | let result: Result = add_contents.call(ui); 484 | result 485 | }) 486 | }); 487 | let r = lua.create_any_userdata(ir.response)?; 488 | let mut i = ir.inner?; 489 | i.push_front(Value::UserData(r)); 490 | Ok(i) 491 | }, 492 | ); 493 | reg.add_method_mut("checkbox", |lua, this, value: Table| { 494 | let mut b: bool = value.get("checked")?; 495 | let result = lua.create_any_userdata(this.checkbox(&mut b, WidgetText::from_lua(value.get("text")?)?)); 496 | value.set("checked", b)?; 497 | result 498 | }); 499 | reg.add_method_mut("child_ui", |lua, this, (max_rect, layout): (Value, UserDataRef)| { 500 | let ui = this.child_ui(LuaHelperTrait::from_lua(max_rect)?, *layout); 501 | lua.create_any_userdata(ui) 502 | }); 503 | // requires impl Hash for Value smh 504 | // reg.add_method_mut("child_ui_with_id_source", |lua, this, (max_rect, layout): (Value, Value)| { 505 | // let ui = this.child_ui(LuaHelperTrait::from_lua(max_rect)?, LuaHelperTrait::from_lua(layout)?); 506 | // lua.create_any_userdata(ui) 507 | // }); 508 | reg.add_method("clip_rect", |lua, this, ()| { 509 | Rect::to_lua(this.clip_rect(), lua) 510 | }); 511 | reg.add_method_mut("close_menu", |_, this, ()| { 512 | this.close_menu(); 513 | Ok(()) 514 | }); 515 | reg.add_method_mut("code", |lua, this, value: Value| { 516 | lua.create_any_userdata(this.code(RichText::from_lua(value)?)) 517 | }); 518 | reg.add_method_mut("code_editor", |lua, this, value: Table| { 519 | let mut b: String = value.get("text")?; 520 | let result = lua.create_any_userdata(this.code_editor(&mut b)); 521 | value.set("text", b)?; 522 | result 523 | }); 524 | // reg.add_method_mut( 525 | // "collapsing", 526 | // |lua, this, (heading, add_contents): (Value, Function)| { 527 | // let result = lua.create_table()?; 528 | // let ir = this.collapsing( WidgetText::from_lua(heading)?,|ui| { 529 | // lua.scope(|scope| { 530 | // let ui = scope.create_any_userdata_ref_mut(ui)?; 531 | // let _result: Result = add_contents.call(ui); 532 | // // some lifetime error... 533 | // Ok(()) 534 | // }) 535 | // }); 536 | // result.set("header_response", lua.create_any_userdata(ir.header_response)?)?; 537 | // result.set("body_response", lua.create_any_userdata(ir.body_response)?)?; 538 | // result.set("body_returned", lua.create_any_userdata(ir.body_returned)?)?; 539 | // result.set("openness", lua.create_any_userdata(ir.openness)?)?; 540 | // Ok(Value::Table(result)) 541 | // }, 542 | // ); 543 | reg.add_method_mut( 544 | "columns", 545 | |lua, this, (num, add_contents): (usize, Function)| { 546 | let ir = this.columns( num, |cols| { 547 | lua.scope(|scope| { 548 | let cols: Vec = cols.into_iter().map(|ui| scope.create_any_userdata_ref_mut(ui)).collect::>>()?; 549 | let result: Result = add_contents.call(cols); 550 | result 551 | }) 552 | }); 553 | ir 554 | }, 555 | ); 556 | reg.add_method( 557 | "ctx", 558 | |lua, this, ()| { 559 | lua.create_any_userdata(this.ctx().clone()) 560 | }, 561 | ); 562 | reg.add_method( 563 | "cursor", 564 | |lua, this, ()| { 565 | Rect::to_lua(this.cursor(), lua) 566 | }, 567 | ); 568 | reg.add_method_mut( 569 | "data", 570 | |lua, this, add_contents: Function| { 571 | let ir = this.data( |reader| { 572 | lua.scope(|scope| { 573 | let reader = scope.create_any_userdata_ref(reader)?; 574 | let result: Result = add_contents.call(reader); 575 | result 576 | }) 577 | }); 578 | ir 579 | }, 580 | ); 581 | reg.add_method_mut( 582 | "data_mut", 583 | |lua, this, add_contents: Function| { 584 | let ir = this.data_mut( |reader| { 585 | lua.scope(|scope| { 586 | let reader = scope.create_any_userdata_ref_mut(reader)?; 587 | let result: Result = add_contents.call(reader); 588 | result 589 | }) 590 | }); 591 | ir 592 | }, 593 | ); 594 | 595 | // reg.add_method( 596 | // "debug_paint_cursor", 597 | // |_, this, ()| { 598 | // Ok(this.debug_paint_cursor()) 599 | // }, 600 | // ); 601 | reg.add_method_mut("drag_angle", |lua, this, value: Table| { 602 | let mut b: f32 = value.get("value")?; 603 | let result = lua.create_any_userdata(this.drag_angle(&mut b)); 604 | value.set("value", b)?; 605 | result 606 | }); 607 | 608 | reg.add_method_mut("drag_angle_tau", |lua, this, value: Table| { 609 | let mut b: f32 = value.get("value")?; 610 | let result = lua.create_any_userdata(this.drag_angle_tau(&mut b)); 611 | value.set("value", b)?; 612 | result 613 | }); 614 | reg.add_method_mut( 615 | "end_row", 616 | |_, this, ()| { 617 | Ok(this.end_row()) 618 | }, 619 | ); 620 | 621 | reg.add_method_mut( 622 | "expand_to_include_rect", 623 | |_, this, rect: Value| { 624 | Ok(this.expand_to_include_rect(Rect::from_lua(rect)?)) 625 | }, 626 | ); 627 | 628 | reg.add_method_mut( 629 | "expand_to_include_x", 630 | |_, this, x: f32| { 631 | Ok(this.expand_to_include_x(x)) 632 | }, 633 | ); 634 | reg.add_method_mut( 635 | "expand_to_include_y", 636 | |_, this, x: f32| { 637 | Ok(this.expand_to_include_y(x)) 638 | }, 639 | ); 640 | 641 | reg.add_method_mut( 642 | "fonts", 643 | |lua, this, add_contents: Function| { 644 | let ir = this.fonts( |reader| { 645 | lua.scope(|scope| { 646 | let reader = scope.create_any_userdata_ref(reader)?; 647 | let result: Result = add_contents.call(reader); 648 | result 649 | }) 650 | }); 651 | ir 652 | }, 653 | ); 654 | 655 | reg.add_method_mut( 656 | "group", 657 | |lua, this, add_contents: Function| { 658 | let ir = this.group(|ui| { 659 | lua.scope(|scope| { 660 | let ui = scope.create_any_userdata_ref_mut(ui)?; 661 | let result: Result = add_contents.call(ui); 662 | result 663 | }) 664 | }); 665 | let r = lua.create_any_userdata(ir.response)?; 666 | let mut i = ir.inner?; 667 | i.push_front(Value::UserData(r)); 668 | Ok(i) 669 | }, 670 | ); 671 | reg.add_method_mut("heading", |lua, this, value: Value| { 672 | lua.create_any_userdata(this.heading(RichText::from_lua(value)?)) 673 | }); 674 | reg.add_method_mut( 675 | "horizontal", 676 | |lua, this, add_contents: Function| { 677 | let ir = this.horizontal(|ui| { 678 | lua.scope(|scope| { 679 | let ui = scope.create_any_userdata_ref_mut(ui)?; 680 | let result: Result = add_contents.call(ui); 681 | result 682 | }) 683 | }); 684 | let r = lua.create_any_userdata(ir.response)?; 685 | let mut i = ir.inner?; 686 | i.push_front(Value::UserData(r)); 687 | Ok(i) 688 | }, 689 | ); 690 | 691 | reg.add_method_mut( 692 | "horizontal_centered", 693 | |lua, this, add_contents: Function| { 694 | let ir = this.horizontal_centered(|ui| { 695 | lua.scope(|scope| { 696 | let ui = scope.create_any_userdata_ref_mut(ui)?; 697 | let result: Result = add_contents.call(ui); 698 | result 699 | }) 700 | }); 701 | let r = lua.create_any_userdata(ir.response)?; 702 | let mut i = ir.inner?; 703 | i.push_front(Value::UserData(r)); 704 | Ok(i) 705 | }, 706 | ); 707 | 708 | reg.add_method_mut( 709 | "horizontal_top", 710 | |lua, this, add_contents: Function| { 711 | let ir = this.horizontal_top(|ui| { 712 | lua.scope(|scope| { 713 | let ui = scope.create_any_userdata_ref_mut(ui)?; 714 | let result: Result = add_contents.call(ui); 715 | result 716 | }) 717 | }); 718 | let r = lua.create_any_userdata(ir.response)?; 719 | let mut i = ir.inner?; 720 | i.push_front(Value::UserData(r)); 721 | Ok(i) 722 | }, 723 | ); 724 | 725 | reg.add_method_mut( 726 | "horizontal_wrapped", 727 | |lua, this, add_contents: Function| { 728 | let ir = this.horizontal_wrapped(|ui| { 729 | lua.scope(|scope| { 730 | let ui = scope.create_any_userdata_ref_mut(ui)?; 731 | let result: Result = add_contents.call(ui); 732 | result 733 | }) 734 | }); 735 | let r = lua.create_any_userdata(ir.response)?; 736 | let mut i = ir.inner?; 737 | i.push_front(Value::UserData(r)); 738 | Ok(i) 739 | }, 740 | ); 741 | 742 | reg.add_method_mut("hyperlink", |lua, this, value: String| { 743 | lua.create_any_userdata(this.hyperlink(value)) 744 | }); 745 | 746 | reg.add_method_mut("hyperlink_to", |lua, this, (label, url): (Value, String)| { 747 | lua.create_any_userdata(this.hyperlink_to(WidgetText::from_lua(label)?, url )) 748 | }); 749 | 750 | reg.add_method("id", |lua, this, ()| lua.create_any_userdata(this.id())); 751 | 752 | reg.add_method_mut("image", |lua, this, (texture, size): (UserDataRef, Value)| { 753 | lua.create_any_userdata(this.image(SizedTexture::new(texture.id(), Vec2::from_lua(size)?) )) 754 | }); 755 | reg.add_method_mut( 756 | "indent", 757 | |lua, this, (hashable, add_contents): (Value, Function)| { 758 | let ir = this.indent( LuaHashable::from_lua(hashable)?, |ui| { 759 | lua.scope(|scope| { 760 | let ui = scope.create_any_userdata_ref_mut(ui)?; 761 | let result: Result = add_contents.call(ui); 762 | result 763 | }) 764 | }); 765 | let r = lua.create_any_userdata(ir.response)?; 766 | let mut i = ir.inner?; 767 | i.push_front(Value::UserData(r)); 768 | Ok(i) 769 | }, 770 | ); 771 | 772 | reg.add_method_mut( 773 | "input", 774 | |lua, this, add_contents: Function| { 775 | let ir = this.input( |reader| { 776 | lua.scope(|scope| { 777 | let reader = scope.create_any_userdata_ref(reader)?; 778 | let result: Result = add_contents.call(reader); 779 | result 780 | }) 781 | }); 782 | ir 783 | }, 784 | ); 785 | reg.add_method_mut( 786 | "input_mut", 787 | |lua, this, add_contents: Function| { 788 | let ir = this.input_mut( |reader| { 789 | lua.scope(|scope| { 790 | let reader = scope.create_any_userdata_ref_mut(reader)?; 791 | let result: Result = add_contents.call(reader); 792 | result 793 | }) 794 | }); 795 | ir 796 | }, 797 | ); 798 | 799 | reg.add_method("interact", |lua, this, (rect, id, sense): (Value, UserDataRef, Value)| { 800 | lua.create_any_userdata( this.interact(Rect::from_lua(rect)?, *id, Sense::from_lua(sense)?)) 801 | }); 802 | 803 | reg.add_method("interact_with_hovered", |lua, this, (rect, hovered, id, sense): (Value, bool, UserDataRef, Value)| { 804 | lua.create_any_userdata( this.interact_with_hovered(Rect::from_lua(rect)?, hovered, *id, Sense::from_lua(sense)?)) 805 | }); 806 | reg.add_method("is_enabled", |_, this, ()| Ok(this.is_enabled())); 807 | reg.add_method_mut("is_rect_visible", |_, this, clip_rect: Value| { 808 | Ok(this.is_rect_visible(Rect::from_lua(clip_rect)?)) 809 | }); 810 | reg.add_method("is_visible", |_, this, ()| Ok(this.is_visible())); 811 | reg.add_method_mut("label", |lua, this, value: Value| { 812 | lua.create_any_userdata(this.label(WidgetText::from_lua(value)?)) 813 | }); 814 | reg.add_method("layer_id", |lua, this, () | { 815 | lua.create_any_userdata(this.layer_id()) 816 | }); 817 | 818 | reg.add_method("layout", |lua, this, () | { 819 | lua.create_any_userdata(this.layout().clone()) 820 | }); 821 | 822 | reg.add_method_mut("link", |lua, this, value: Value| { 823 | lua.create_any_userdata(this.link(WidgetText::from_lua(value)?)) 824 | }); 825 | 826 | reg.add_method_mut("make_persistent_id", |lua, this, value: Value| { 827 | lua.create_any_userdata(this.make_persistent_id(LuaHashable::from_lua(value)?)) 828 | }); 829 | 830 | reg.add_method("max_rect", |lua, this, ()| { 831 | Rect::to_lua(this.max_rect(), lua) 832 | }); 833 | 834 | reg.add_method( 835 | "memory", 836 | |lua, this, add_contents: Function| { 837 | let ir = this.memory( |reader| { 838 | lua.scope(|scope| { 839 | let reader = scope.create_any_userdata_ref(reader)?; 840 | let result: Result = add_contents.call(reader); 841 | result 842 | }) 843 | }); 844 | ir 845 | }, 846 | ); 847 | reg.add_method_mut( 848 | "memory_mut", 849 | |lua, this, add_contents: Function| { 850 | let ir = this.memory_mut( |reader| { 851 | lua.scope(|scope| { 852 | let reader = scope.create_any_userdata_ref_mut(reader)?; 853 | let result: Result = add_contents.call(reader); 854 | result 855 | }) 856 | }); 857 | ir 858 | }, 859 | ); 860 | reg.add_method_mut( 861 | "menu_button", 862 | |lua, this, (title, add_contents): (Value, Function)| { 863 | let ir = this.menu_button(WidgetText::from_lua(title)?, |ui| { 864 | lua.scope(|scope| { 865 | let ui = scope.create_any_userdata_ref_mut(ui)?; 866 | let result: Result = add_contents.call(ui); 867 | result 868 | }) 869 | }); 870 | 871 | let mut result = MultiValue::new(); 872 | let response = lua.create_any_userdata(ir.response)?; 873 | result.push_front(Value::UserData(response)); 874 | if let Some(inner) = ir.inner { 875 | let inner = inner?; 876 | for v in inner { 877 | result.push_front(v); 878 | } 879 | } 880 | Ok(result) 881 | }, 882 | ); 883 | reg.add_method_mut("selectable_label", |lua, ui, (selected, text): (bool, Value)| { 884 | lua.create_any_userdata(ui.selectable_label(selected, WidgetText::from_lua(text)?)) 885 | }); 886 | reg.add_method_mut( 887 | "set_row_height", 888 | |_, this, height: f32| { 889 | Ok(this.set_row_height(height)) 890 | }, 891 | ); 892 | reg.add_method_mut( 893 | "output", 894 | |lua, this, add_contents: Function| { 895 | let ir = this.output( |reader| { 896 | lua.scope(|scope| { 897 | let reader = scope.create_any_userdata_ref(reader)?; 898 | let result: Result = add_contents.call(reader); 899 | result 900 | }) 901 | }); 902 | ir 903 | }, 904 | ); 905 | reg.add_method_mut( 906 | "output_mut", 907 | |lua, this, add_contents: Function| { 908 | let ir = this.output_mut( |reader| { 909 | lua.scope(|scope| { 910 | let reader = scope.create_any_userdata_ref_mut(reader)?; 911 | let result: Result = add_contents.call(reader); 912 | result 913 | }) 914 | }); 915 | ir 916 | }, 917 | ); 918 | reg.add_method( 919 | "next_widget_position", 920 | |lua, this, ()| { 921 | Pos2::to_lua(this.next_widget_position(), lua) 922 | }, 923 | ); 924 | reg.add_method( 925 | "painter", 926 | |lua, this, ()| { 927 | lua.create_any_userdata(this.painter().clone()) 928 | }, 929 | ); 930 | reg.add_method_mut("text_edit_multiline", |lua, this, value: Table| { 931 | let mut b: String = value.get("text")?; 932 | let result = lua.create_any_userdata(this.text_edit_multiline(&mut b)); 933 | value.set("text", b)?; 934 | result 935 | }); 936 | reg.add_method_mut("text_edit_singleline", |lua, this, value: Table| { 937 | let mut b: String = value.get("text")?; 938 | let result = lua.create_any_userdata(this.text_edit_singleline(&mut b)); 939 | value.set("text", b)?; 940 | result 941 | }); 942 | reg.add_method_mut("set_enabled", |_, this, enabled: bool| { 943 | this.set_enabled(enabled); 944 | Ok(()) 945 | }); 946 | 947 | reg.add_method("wrap_text", |_, this, ()| Ok(this.wrap_text())); 948 | 949 | reg.add_method_mut("set_clip_rect", |_, this, clip_rect: Value| { 950 | this.set_clip_rect(Rect::from_lua(clip_rect)?); 951 | Ok(()) 952 | }); 953 | })?; 954 | 955 | Ok(()) 956 | } 957 | impl LuaHelperTrait for Sense { 958 | fn from_lua(value: Value) -> Result { 959 | match value { 960 | Value::Integer(i) => { 961 | let i = i as u8; 962 | let click = 0 != (i & 1); 963 | let drag = 0 != (i & (1 << 1)); 964 | let focusable = 0 != (i & (1 << 2)); 965 | Ok(Self { 966 | click, 967 | drag, 968 | focusable, 969 | }) 970 | } 971 | _ => Err(mlua::Error::FromLuaConversionError { 972 | from: "luavalue", 973 | to: "pointerbutton", 974 | message: None, 975 | }), 976 | } 977 | } 978 | 979 | fn to_lua(value: Self, _lua: &Lua) -> Result { 980 | let mut u = 0u8; 981 | if value.click { 982 | u &= 1; 983 | } 984 | if value.drag { 985 | u &= 1 << 1; 986 | } 987 | if value.focusable { 988 | u &= 1 << 2; 989 | } 990 | Ok(Value::Integer(u as _)) 991 | } 992 | 993 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()> { 994 | let sense = lua.create_table()?; 995 | sense.set("hover", Sense::to_lua(Sense::hover(), lua)?)?; 996 | sense.set( 997 | "focusable_noninteractive", 998 | Sense::to_lua(Sense::focusable_noninteractive(), lua)?, 999 | )?; 1000 | sense.set("click", Sense::to_lua(Sense::click(), lua)?)?; 1001 | sense.set("drag", Sense::to_lua(Sense::drag(), lua)?)?; 1002 | sense.set( 1003 | "union", 1004 | lua.create_function(|lua, (first, second): (Value, Value)| { 1005 | let first = Sense::from_lua(first)?; 1006 | let second = Sense::from_lua(second)?; 1007 | Sense::to_lua(first.union(second), lua) 1008 | })?, 1009 | )?; 1010 | sense.set( 1011 | "union", 1012 | lua.create_function(|_, value: Value| Ok(Sense::from_lua(value)?.interactive()))?, 1013 | )?; 1014 | 1015 | egui_table.set("sense", sense)?; 1016 | Ok(()) 1017 | } 1018 | } 1019 | impl LuaHelperTrait for Margin { 1020 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 1021 | let margin = lua.create_table()?; 1022 | margin.set( 1023 | "same", 1024 | lua.create_function(|lua, margin: f32| Margin::to_lua(Margin::same(margin), lua))?, 1025 | )?; 1026 | margin.set( 1027 | "symmetric", 1028 | lua.create_function(|lua, (x, y): (f32, f32)| { 1029 | Margin::to_lua(Margin::symmetric(x, y), lua) 1030 | })?, 1031 | )?; 1032 | margin.set( 1033 | "sum", 1034 | lua.create_function(|lua, value: Value| { 1035 | Vec2::to_lua(Margin::from_lua(value)?.sum(), lua) 1036 | })?, 1037 | )?; 1038 | margin.set( 1039 | "left_top", 1040 | lua.create_function(|lua, value: Value| { 1041 | Vec2::to_lua(Margin::from_lua(value)?.left_top(), lua) 1042 | })?, 1043 | )?; 1044 | margin.set( 1045 | "right_bottom", 1046 | lua.create_function(|lua, value: Value| { 1047 | Vec2::to_lua(Margin::from_lua(value)?.right_bottom(), lua) 1048 | })?, 1049 | )?; 1050 | margin.set( 1051 | "is_same", 1052 | lua.create_function(|_, value: Value| Ok(Margin::from_lua(value)?.is_same()))?, 1053 | )?; 1054 | 1055 | egui_table.set("rounding", margin)?; 1056 | Ok(()) 1057 | } 1058 | fn from_lua(value: Value) -> Result { 1059 | Ok(match value { 1060 | Value::Table(t) => { 1061 | let left: f32 = t.get("left")?; 1062 | let right: f32 = t.get("right")?; 1063 | let top: f32 = t.get("top")?; 1064 | let bottom: f32 = t.get("bottom")?; 1065 | Self { 1066 | left, 1067 | right, 1068 | top, 1069 | bottom, 1070 | } 1071 | } 1072 | _ => { 1073 | return Err(mlua::Error::FromLuaConversionError { 1074 | from: "luavalue", 1075 | to: "pointerbutton", 1076 | message: None, 1077 | }) 1078 | } 1079 | }) 1080 | } 1081 | 1082 | fn to_lua(value: Self, lua: &Lua) -> Result { 1083 | let margin = lua.create_table()?; 1084 | margin.set("left", value.left)?; 1085 | margin.set("right", value.right)?; 1086 | margin.set("top", value.top)?; 1087 | margin.set("bottom", value.bottom)?; 1088 | Ok(Value::Table(margin)) 1089 | } 1090 | } 1091 | impl LuaHelperTrait for TextStyle { 1092 | fn from_lua(value: Value) -> Result { 1093 | match value { 1094 | Value::Integer(i) => Ok(match i { 1095 | 0 => Self::Small, 1096 | 1 => Self::Body, 1097 | 2 => Self::Monospace, 1098 | 3 => Self::Button, 1099 | 4 => Self::Heading, 1100 | _ => { 1101 | return Err(mlua::Error::RuntimeError(format!( 1102 | "the value {i} doesn't match any TextStyle enum variants" 1103 | ))) 1104 | } 1105 | }), 1106 | Value::String(s) => Ok(Self::Name(s.to_str().unwrap_or_default().into())), 1107 | _ => { 1108 | return Err(mlua::Error::RuntimeError(format!( 1109 | "invalid type to convert to TextStyle enum variants" 1110 | ))) 1111 | } 1112 | } 1113 | } 1114 | 1115 | fn to_lua(value: Self, lua: &Lua) -> Result { 1116 | Ok(match value { 1117 | TextStyle::Small => Value::Integer(0), 1118 | TextStyle::Body => Value::Integer(1), 1119 | TextStyle::Monospace => Value::Integer(2), 1120 | TextStyle::Button => Value::Integer(3), 1121 | TextStyle::Heading => Value::Integer(4), 1122 | TextStyle::Name(n) => Value::String(lua.create_string(n.as_bytes())?), 1123 | }) 1124 | } 1125 | 1126 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()> { 1127 | let text_style = lua.create_table()?; 1128 | text_style.set("small", Value::Integer(0))?; 1129 | text_style.set("body", Value::Integer(1))?; 1130 | text_style.set("monospace", Value::Integer(2))?; 1131 | text_style.set("button", Value::Integer(3))?; 1132 | text_style.set("heading", Value::Integer(4))?; 1133 | text_style.set_readonly(true); 1134 | egui_table.set("text_style", text_style)?; 1135 | Ok(()) 1136 | } 1137 | } 1138 | impl LuaHelperTrait for Rounding { 1139 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 1140 | let rounding = lua.create_table()?; 1141 | rounding.set( 1142 | "same", 1143 | lua.create_function(|lua, radius: f32| Rounding::to_lua(Rounding::same(radius), lua))?, 1144 | )?; 1145 | rounding.set( 1146 | "none", 1147 | lua.create_function(|lua, ()| Rounding::to_lua(Rounding::ZERO, lua))?, 1148 | )?; 1149 | rounding.set( 1150 | "is_same", 1151 | lua.create_function(|_, rounding: Value| -> mlua::Result { 1152 | Ok(Rounding::from_lua(rounding)?.is_same()) 1153 | })?, 1154 | )?; 1155 | rounding.set( 1156 | "atleast", 1157 | lua.create_function(|lua, (value, min): (Value, f32)| { 1158 | Rounding::to_lua(Rounding::from_lua(value)?.at_least(min), lua) 1159 | })?, 1160 | )?; 1161 | rounding.set( 1162 | "atmost", 1163 | lua.create_function(|lua, (value, max): (Value, f32)| { 1164 | Rounding::to_lua(Rounding::from_lua(value)?.at_most(max), lua) 1165 | })?, 1166 | )?; 1167 | egui_table.set("rounding", rounding)?; 1168 | Ok(()) 1169 | } 1170 | fn from_lua(value: Value) -> Result { 1171 | Ok(match value { 1172 | Value::Table(t) => { 1173 | let nw: f32 = t.get("nw")?; 1174 | let ne: f32 = t.get("ne")?; 1175 | let sw: f32 = t.get("sw")?; 1176 | let se: f32 = t.get("se")?; 1177 | Self { nw, ne, sw, se } 1178 | } 1179 | _ => { 1180 | return Err(mlua::Error::FromLuaConversionError { 1181 | from: "luavalue", 1182 | to: "pointerbutton", 1183 | message: None, 1184 | }) 1185 | } 1186 | }) 1187 | } 1188 | 1189 | fn to_lua(value: Self, lua: &Lua) -> Result { 1190 | let rounding = lua.create_table()?; 1191 | rounding.set("nw", value.nw)?; 1192 | rounding.set("ne", value.ne)?; 1193 | rounding.set("sw", value.sw)?; 1194 | rounding.set("se", value.se)?; 1195 | Ok(Value::Table(rounding)) 1196 | } 1197 | } 1198 | impl LuaHelperTrait for Rect { 1199 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 1200 | let rect = lua.create_table()?; 1201 | rect.set("everything", Rect::to_lua(Rect::EVERYTHING, lua)?)?; 1202 | rect.set("nothing", Rect::to_lua(Rect::NOTHING, lua)?)?; 1203 | rect.set("nan", Rect::to_lua(Rect::NAN, lua)?)?; 1204 | rect.set( 1205 | "from_min_max", 1206 | lua.create_function(|lua, (min, max): (Value, Value)| { 1207 | let min = Pos2::from_lua(min)?; 1208 | let max = Pos2::from_lua(max)?; 1209 | Rect::to_lua(Rect { min, max }, lua) 1210 | })?, 1211 | )?; 1212 | egui_table.set("stroke", rect)?; 1213 | Ok(()) 1214 | } 1215 | fn from_lua(value: Value) -> Result { 1216 | Ok(match value { 1217 | Value::Table(t) => { 1218 | let min = Pos2::from_lua(t.get("min")?)?; 1219 | let max = Pos2::from_lua(t.get("max")?)?; 1220 | Rect { min, max } 1221 | } 1222 | _ => { 1223 | return Err(mlua::Error::FromLuaConversionError { 1224 | from: "luavalue", 1225 | to: "pointerbutton", 1226 | message: None, 1227 | }) 1228 | } 1229 | }) 1230 | } 1231 | 1232 | fn to_lua(value: Self, lua: &Lua) -> Result { 1233 | let rect = lua.create_table()?; 1234 | rect.set("min", Pos2::to_lua(value.min, lua)?)?; 1235 | rect.set("max", Pos2::to_lua(value.max, lua)?)?; 1236 | Ok(Value::Table(rect)) 1237 | } 1238 | } 1239 | impl LuaHelperTrait for Color32 { 1240 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 1241 | let color32 = lua.create_table()?; 1242 | // multiply first align by 4 to push its bits to left. 1243 | // second align will fit in the 2 bits 1244 | color32.set("transparent", Color32::to_lua(Color32::TRANSPARENT, lua)?)?; 1245 | color32.set("black", Color32::to_lua(Color32::BLACK, lua)?)?; 1246 | color32.set("dark_gray", Color32::to_lua(Color32::DARK_GRAY, lua)?)?; 1247 | color32.set("gray", Color32::to_lua(Color32::GRAY, lua)?)?; 1248 | color32.set("light_gray", Color32::to_lua(Color32::LIGHT_GRAY, lua)?)?; 1249 | color32.set("white", Color32::to_lua(Color32::WHITE, lua)?)?; 1250 | color32.set("brown", Color32::to_lua(Color32::BROWN, lua)?)?; 1251 | color32.set("dark_red", Color32::to_lua(Color32::DARK_RED, lua)?)?; 1252 | color32.set("red", Color32::to_lua(Color32::RED, lua)?)?; 1253 | color32.set("light_red", Color32::to_lua(Color32::LIGHT_RED, lua)?)?; 1254 | color32.set("yellow", Color32::to_lua(Color32::YELLOW, lua)?)?; 1255 | color32.set("light_yellow", Color32::to_lua(Color32::LIGHT_YELLOW, lua)?)?; 1256 | color32.set("khaki", Color32::to_lua(Color32::KHAKI, lua)?)?; 1257 | color32.set("dark_green", Color32::to_lua(Color32::DARK_GREEN, lua)?)?; 1258 | color32.set("green", Color32::to_lua(Color32::GREEN, lua)?)?; 1259 | color32.set("light_green", Color32::to_lua(Color32::LIGHT_GREEN, lua)?)?; 1260 | color32.set("dark_blue", Color32::to_lua(Color32::DARK_BLUE, lua)?)?; 1261 | color32.set("blue", Color32::to_lua(Color32::BLUE, lua)?)?; 1262 | color32.set("light_blue", Color32::to_lua(Color32::LIGHT_BLUE, lua)?)?; 1263 | color32.set("gold", Color32::to_lua(Color32::GOLD, lua)?)?; 1264 | color32.set("debug_color", Color32::to_lua(Color32::DEBUG_COLOR, lua)?)?; 1265 | color32.set( 1266 | "placeholder_color", 1267 | Color32::to_lua(Color32::PLACEHOLDER, lua)?, 1268 | )?; 1269 | 1270 | color32.set( 1271 | "from_rgba_premultiplied", 1272 | lua.create_function(|lua, (r, g, b, a): (u8, u8, u8, u8)| { 1273 | Color32::to_lua(Color32::from_rgba_premultiplied(r, g, b, a), lua) 1274 | })?, 1275 | )?; 1276 | egui_table.set("color32", color32)?; 1277 | Ok(()) 1278 | } 1279 | 1280 | fn from_lua(value: Value) -> Result { 1281 | Ok(match value { 1282 | Value::Integer(i) => { 1283 | let c = i.to_le_bytes(); 1284 | 1285 | Color32::from_rgba_premultiplied(c[0], c[1], c[2], c[3]) 1286 | } 1287 | _ => { 1288 | return Err(mlua::Error::FromLuaConversionError { 1289 | from: "luavalue", 1290 | to: "pointerbutton", 1291 | message: None, 1292 | }) 1293 | } 1294 | }) 1295 | } 1296 | 1297 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1298 | Ok(Value::Integer({ 1299 | let a = value.to_array(); 1300 | i32::from_le_bytes(a) 1301 | })) 1302 | } 1303 | } 1304 | impl LuaHelperTrait for Pos2 { 1305 | fn from_lua(value: Value) -> Result { 1306 | match value { 1307 | Value::Vector(v) => Ok(Self { x: v.x(), y: v.y() }), 1308 | _ => Err(mlua::Error::FromLuaConversionError { 1309 | from: "luavalue", 1310 | to: "pos2", 1311 | message: None, 1312 | }), 1313 | } 1314 | } 1315 | 1316 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1317 | Ok(Value::Vector(Vector::new(value.x, value.y, 0.0, 0.0))) 1318 | } 1319 | 1320 | fn add_to_lua(_lua: &Lua, _egui_table: &Table) -> Result<()> { 1321 | Ok(()) 1322 | } 1323 | } 1324 | impl LuaHelperTrait for Stroke { 1325 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 1326 | let stroke = lua.create_table()?; 1327 | stroke.set("none", Stroke::to_lua(Stroke::NONE, lua)?)?; 1328 | stroke.set( 1329 | "new", 1330 | lua.create_function(|lua, (width, color): (f32, Value)| { 1331 | let color = Color32::from_lua(color)?; 1332 | Stroke::to_lua(Stroke { width, color }, lua) 1333 | })?, 1334 | )?; 1335 | egui_table.set("stroke", stroke)?; 1336 | Ok(()) 1337 | } 1338 | fn from_lua(value: Value) -> Result { 1339 | match value { 1340 | Value::Vector(v) => { 1341 | let color = v.y().to_le_bytes(); 1342 | Ok(Self { 1343 | width: v.x(), 1344 | color: Color32::from_rgba_premultiplied(color[0], color[1], color[2], color[3]), 1345 | }) 1346 | } 1347 | _ => Err(mlua::Error::FromLuaConversionError { 1348 | from: "luavalue", 1349 | to: "pos2", 1350 | message: None, 1351 | }), 1352 | } 1353 | } 1354 | 1355 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1356 | let width = value.width; 1357 | let color = value.color.to_array(); 1358 | let color = f32::from_le_bytes(color); 1359 | Ok(Value::Vector(Vector::new(width, color, 0.0, 0.0))) 1360 | } 1361 | } 1362 | impl LuaHelperTrait for Vec2 { 1363 | fn from_lua(value: Value) -> Result { 1364 | match value { 1365 | Value::Vector(v) => Ok(Self { x: v.x(), y: v.y() }), 1366 | _ => Err(mlua::Error::FromLuaConversionError { 1367 | from: "luavalue", 1368 | to: "pos2", 1369 | message: None, 1370 | }), 1371 | } 1372 | } 1373 | 1374 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1375 | Ok(Value::Vector(Vector::new(value.x, value.y, 0.0, 0.0))) 1376 | } 1377 | 1378 | fn add_to_lua(_lua: &Lua, _egui_table: &Table) -> Result<()> { 1379 | Ok(()) 1380 | } 1381 | } 1382 | impl LuaHelperTrait for Align2 { 1383 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> mlua::Result<()> { 1384 | let align = lua.create_table()?; 1385 | // multiply first align by 4 to push its bits to left. 1386 | // second align will fit in the 2 bits 1387 | align.set("left_bottom", Value::Integer(2))?; 1388 | align.set("left_center", Value::Integer(1))?; 1389 | align.set("left_top", Value::Integer(0))?; 1390 | align.set("center_bottom", Value::Integer(6))?; 1391 | align.set("center_center", Value::Integer(5))?; 1392 | align.set("center_top", Value::Integer(4))?; 1393 | align.set("right_bottom", Value::Integer(10))?; 1394 | align.set("right_center", Value::Integer(9))?; 1395 | align.set("right_top", Value::Integer(8))?; 1396 | // align.set("center", Value::Integer(1))?; 1397 | // align.set("max", Value::Integer(2))?; 1398 | egui_table.set("align2", align)?; 1399 | Ok(()) 1400 | } 1401 | fn from_lua(value: Value) -> Result { 1402 | Ok(match value { 1403 | Value::Integer(i) => match i { 1404 | 0 => Align2::LEFT_TOP, 1405 | 1 => Align2::LEFT_CENTER, 1406 | 2 => Align2::LEFT_BOTTOM, 1407 | 4 => Align2::CENTER_TOP, 1408 | 5 => Align2::CENTER_CENTER, 1409 | 6 => Align2::CENTER_BOTTOM, 1410 | 8 => Align2::RIGHT_TOP, 1411 | 9 => Align2::RIGHT_CENTER, 1412 | 10 => Align2::RIGHT_BOTTOM, 1413 | _ => { 1414 | return Err(mlua::Error::FromLuaConversionError { 1415 | from: "luavalue", 1416 | to: "pointerbutton", 1417 | message: Some("integer value out of range".to_string()), 1418 | }) 1419 | } 1420 | }, 1421 | _ => { 1422 | return Err(mlua::Error::FromLuaConversionError { 1423 | from: "luavalue", 1424 | to: "pointerbutton", 1425 | message: None, 1426 | }) 1427 | } 1428 | }) 1429 | } 1430 | 1431 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1432 | Ok(Value::Integer(match value { 1433 | Align2::LEFT_TOP => 0, 1434 | Align2::LEFT_CENTER => 1, 1435 | Align2::LEFT_BOTTOM => 2, 1436 | Align2::CENTER_TOP => 4, 1437 | Align2::CENTER_CENTER => 5, 1438 | Align2::CENTER_BOTTOM => 6, 1439 | Align2::RIGHT_TOP => 8, 1440 | Align2::RIGHT_CENTER => 9, 1441 | Align2::RIGHT_BOTTOM => 10, 1442 | })) 1443 | } 1444 | } 1445 | 1446 | impl LuaHelperTrait for Align { 1447 | fn from_lua(value: Value) -> Result { 1448 | Ok(match value { 1449 | Value::Integer(i) => match i { 1450 | 0 => Align::Min, 1451 | 1 => Align::Center, 1452 | 2 => Align::Max, 1453 | _ => { 1454 | return Err(mlua::Error::FromLuaConversionError { 1455 | from: "luavalue", 1456 | to: "pointerbutton", 1457 | message: Some("integer value out of range".to_string()), 1458 | }) 1459 | } 1460 | }, 1461 | _ => { 1462 | return Err(mlua::Error::FromLuaConversionError { 1463 | from: "luavalue", 1464 | to: "pointerbutton", 1465 | message: None, 1466 | }) 1467 | } 1468 | }) 1469 | } 1470 | 1471 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1472 | Ok(Value::Integer(match value { 1473 | Align::Min => 0, 1474 | Align::Center => 1, 1475 | Align::Max => 2, 1476 | })) 1477 | } 1478 | 1479 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()> { 1480 | let align = lua.create_table()?; 1481 | align.set("min", Value::Integer(0))?; 1482 | align.set("center", Value::Integer(1))?; 1483 | align.set("max", Value::Integer(2))?; 1484 | egui_table.set("align", align)?; 1485 | Ok(()) 1486 | } 1487 | } 1488 | impl LuaHelperTrait for PointerButton { 1489 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()> { 1490 | let pointer_button = lua.create_table()?; 1491 | pointer_button.set("primary", Value::Integer(0))?; 1492 | pointer_button.set("secondary", Value::Integer(1))?; 1493 | pointer_button.set("middle", Value::Integer(2))?; 1494 | pointer_button.set("extra1", Value::Integer(3))?; 1495 | pointer_button.set("extra2", Value::Integer(4))?; 1496 | egui_table.set("pointer_button", pointer_button)?; 1497 | Ok(()) 1498 | } 1499 | fn from_lua(value: Value) -> Result { 1500 | Ok(match value { 1501 | Value::Integer(i) => match i { 1502 | 0 => PointerButton::Primary, 1503 | 1 => PointerButton::Secondary, 1504 | 2 => PointerButton::Middle, 1505 | 3 => PointerButton::Extra1, 1506 | 4 => PointerButton::Extra2, 1507 | _ => { 1508 | return Err(mlua::Error::FromLuaConversionError { 1509 | from: "luavalue", 1510 | to: "pointerbutton", 1511 | message: Some("integer value out of range".to_string()), 1512 | }) 1513 | } 1514 | }, 1515 | _ => { 1516 | return Err(mlua::Error::FromLuaConversionError { 1517 | from: "luavalue", 1518 | to: "pointerbutton", 1519 | message: None, 1520 | }) 1521 | } 1522 | }) 1523 | } 1524 | 1525 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1526 | Ok(Value::Integer(match value { 1527 | PointerButton::Primary => 0, 1528 | PointerButton::Secondary => 1, 1529 | PointerButton::Middle => 2, 1530 | PointerButton::Extra1 => 3, 1531 | PointerButton::Extra2 => 4, 1532 | })) 1533 | } 1534 | } 1535 | 1536 | impl LuaHelperTrait for Direction { 1537 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()> { 1538 | let direction = lua.create_table()?; 1539 | direction.set("left_to_right", Value::Integer(0))?; 1540 | direction.set("right_to_left", Value::Integer(1))?; 1541 | direction.set("top_down", Value::Integer(2))?; 1542 | direction.set("bottom_up", Value::Integer(3))?; 1543 | egui_table.set("pointer_button", direction)?; 1544 | Ok(()) 1545 | } 1546 | fn from_lua(value: Value) -> Result { 1547 | Ok(match value { 1548 | Value::Integer(i) => match i { 1549 | 0 => Direction::LeftToRight, 1550 | 1 => Direction::RightToLeft, 1551 | 2 => Direction::TopDown, 1552 | 3 => Direction::BottomUp, 1553 | _ => { 1554 | return Err(mlua::Error::FromLuaConversionError { 1555 | from: "luavalue", 1556 | to: "pointerbutton", 1557 | message: Some("integer value out of range".to_string()), 1558 | }) 1559 | } 1560 | }, 1561 | _ => { 1562 | return Err(mlua::Error::FromLuaConversionError { 1563 | from: "luavalue", 1564 | to: "pointerbutton", 1565 | message: None, 1566 | }) 1567 | } 1568 | }) 1569 | } 1570 | 1571 | fn to_lua(value: Self, _lua: &Lua) -> Result { 1572 | Ok(Value::Integer(match value { 1573 | Self::LeftToRight => 0, 1574 | Self::RightToLeft => 1, 1575 | Self::TopDown => 2, 1576 | Self::BottomUp => 3, 1577 | })) 1578 | } 1579 | } 1580 | impl LuaHelperTrait for WidgetText { 1581 | fn from_lua(value: Value) -> Result { 1582 | match value { 1583 | Value::String(s) => Ok(s.to_str().unwrap_or_default().into()), 1584 | Value::UserData(u) => { 1585 | if let Ok(u) = u.borrow::() { 1586 | Ok(u.clone()) 1587 | } else if let Ok(u) = u.borrow::() { 1588 | Ok(u.clone().into()) 1589 | } else { 1590 | return Err(mlua::Error::FromLuaConversionError { 1591 | from: "userdata", 1592 | to: "widgettext", 1593 | message: None, 1594 | }); 1595 | } 1596 | } 1597 | _ => Err(mlua::Error::FromLuaConversionError { 1598 | from: "luavalue", 1599 | to: "widgettext", 1600 | message: None, 1601 | }), 1602 | } 1603 | } 1604 | 1605 | fn to_lua(value: Self, lua: &Lua) -> Result { 1606 | Ok(mlua::Value::UserData(lua.create_any_userdata(value)?)) 1607 | } 1608 | 1609 | fn add_to_lua(lua: &Lua, _egui_table: &Table) -> Result<()> { 1610 | lua.register_userdata_type(|_reg: &mut UserDataRegistry| {})?; 1611 | Ok(()) 1612 | } 1613 | } 1614 | 1615 | impl LuaHelperTrait for RichText { 1616 | fn from_lua(value: Value) -> Result { 1617 | match value { 1618 | Value::String(s) => Ok(s.to_str().unwrap_or_default().into()), 1619 | Value::UserData(u) => { 1620 | if let Ok(u) = u.borrow::() { 1621 | Ok(u.clone()) 1622 | } else { 1623 | Err(mlua::Error::FromLuaConversionError { 1624 | from: "userdata", 1625 | to: "widgettext", 1626 | message: None, 1627 | }) 1628 | } 1629 | } 1630 | Value::Table(_t) => { 1631 | Err(mlua::Error::FromLuaConversionError { 1632 | from: "table", 1633 | to: "widgettext", 1634 | message: None, 1635 | }) 1636 | // if let Ok(text) = t.get::<_, String>("text") { 1637 | // todo!() 1638 | // } else { 1639 | // return e; 1640 | // } 1641 | } 1642 | _ => Err(mlua::Error::FromLuaConversionError { 1643 | from: "luavalue", 1644 | to: "widgettext", 1645 | message: None, 1646 | }), 1647 | } 1648 | } 1649 | 1650 | fn to_lua(value: Self, lua: &Lua) -> Result { 1651 | Ok(Value::UserData(lua.create_any_userdata(value)?)) 1652 | } 1653 | 1654 | fn add_to_lua(lua: &Lua, egui_table: &Table) -> Result<()> { 1655 | lua.register_userdata_type(|reg: &mut UserDataRegistry| { 1656 | reg.add_method("is_empty", |_, this, ()| Ok(this.is_empty())); 1657 | reg.add_method("text", |_, this, ()| Ok(this.text().to_string())); 1658 | reg.add_method("size", |lua, this, size: f32| { 1659 | lua.create_any_userdata(this.clone().size(size)) 1660 | }); 1661 | })?; 1662 | let rich_text = lua.create_table()?; 1663 | rich_text.set( 1664 | "new", 1665 | lua.create_function(|lua, text: String| lua.create_any_userdata(RichText::new(text)))?, 1666 | )?; 1667 | 1668 | egui_table.set("rich_text", rich_text) 1669 | } 1670 | } 1671 | 1672 | #[derive(Hash, Debug)] 1673 | enum LuaHashable<'lua> { 1674 | LuaString(mlua::String<'lua>), 1675 | Integer(i32), 1676 | } 1677 | impl<'lua> LuaHashable<'lua> { 1678 | fn from_lua(value: Value<'lua>) -> Result { 1679 | match value { 1680 | Value::Integer(i) => Ok(Self::Integer(i)), 1681 | Value::String(i) => Ok(Self::LuaString(i)), 1682 | _ => Err(mlua::Error::FromLuaConversionError { 1683 | from: "value", 1684 | to: "LuaHashable", 1685 | message: None, 1686 | }), 1687 | } 1688 | } 1689 | } 1690 | fn add_style(lua: &Lua, egui_table: &Table) -> Result<()> { 1691 | lua.register_userdata_type(|style: &mut UserDataRegistry