tirbofish/dropbear · commit
9a860a35e652d5b955e49dd00cf44fb486d04324
FINALLY GOT TEXT TO RENDER YIPPEEEEEEE!!!! jarvis, run github actions Unverified
@@ -225,7 +225,8 @@ Hardware: width: initial_width, height: initial_height, present_mode: surface_caps.present_modes[0], - alpha_mode: surface_caps.alpha_modes[0], + // alpha_mode: surface_caps.alpha_modes[0], + alpha_mode: wgpu::CompositeAlphaMode::Auto, view_formats: vec![], desired_maximum_frame_latency: 2, }; @@ -1,5 +1,6 @@ mod button; mod utils; +mod text; use std::cell::RefCell; use std::collections::HashMap; @@ -7,12 +8,15 @@ use ::jni::JNIEnv; use ::jni::objects::JObject; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; -use yakui::{Yakui}; +use yakui::{Alignment, MainAxisSize, Yakui}; +use yakui::font::Fonts; use dropbear_engine::utils::ResourceReference; use dropbear_macro::SerializableComponent; use dropbear_traits::SerializableComponent; use crate::scripting::jni::utils::{FromJObject}; use crate::scripting::result::DropbearNativeResult; +use crate::ui::button::ButtonParser; +use crate::ui::text::TextParser; thread_local! { pub static UI_CONTEXT: RefCell<UiContext> = RefCell::new(UiContext::new()); @@ -46,16 +50,6 @@ pub struct WrapperWidget<T> { pub widget: T, } -impl NativeWidget for WrapperWidget<yakui::widgets::Button> { - fn build(self: Box<Self>, states: &mut HashMap<i64, WidgetState>) { - let res = self.widget.show(); - states.insert(self.id, WidgetState { - clicked: res.clicked, - hovering: res.hovering, - }); - } -} - pub trait WidgetParser: Send + Sync { fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<Box<dyn NativeWidget>>>; fn name(&self) -> String; @@ -67,37 +61,6 @@ pub fn register_widget_parser<P: WidgetParser + 'static>(parser: P) { }); } -struct ButtonParser; - -impl WidgetParser for ButtonParser { - fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<Box<dyn NativeWidget>>> { - let class = env.get_object_class(obj)?; - let name_str_obj = env.call_method(class, "getName", "()Ljava/lang/String;", &[])?.l()?; - let name_string: String = env.get_string(&name_str_obj.into())?.into(); - // println!("ButtonParser obj get_string result: {}", name_string); - - if name_string.contains("ButtonInstruction$Button") { - let button_obj = env.get_field(obj, "button", "Lcom/dropbear/ui/widgets/Button;")?.l()?; - let btn = yakui::widgets::Button::from_jobject(env, &button_obj)?; - - let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?; - let id = env.get_field(id_obj, "id", "J")?.j()?; - - return Ok(Some(Box::new(WrapperWidget { - id, - widget: btn, - }))); - } - - Ok(None) - } - - fn name(&self) -> String { - String::from("ButtonParser") - } -} - - pub struct UiContext { pub yakui_state: Mutex<Yakui>, pub instruction_set: Mutex<Vec<Box<dyn NativeWidget>>>, @@ -114,9 +77,15 @@ pub fn poll() { widget_states.clear(); let current_instructions = instructions.drain(..).collect::<Vec<Box<dyn NativeWidget>>>(); - for i in current_instructions { - i.build(&mut widget_states); - } + yakui::widgets::Align::new(Alignment::TOP_LEFT).show(|| { + yakui::widgets::List::column() + .main_axis_size(MainAxisSize::Min) + .show(|| { + for i in current_instructions { + i.build(&mut widget_states); + } + }); + }); }); } @@ -125,8 +94,15 @@ impl UiContext { let mut parsers: Vec<Box<dyn WidgetParser>> = Vec::new(); parsers.push(Box::new(ButtonParser)); + parsers.push(Box::new(TextParser)); let yakui = Yakui::new(); + let fonts = yakui.dom().get_global_or_init(Fonts::default); + fonts.set_sans_serif_family("Roboto"); + fonts.set_serif_family("Roboto"); + fonts.set_cursive_family("Roboto"); + fonts.set_fantasy_family("Roboto"); + fonts.set_monospace_family("Roboto"); Self { yakui_state: Mutex::new(yakui), @@ -181,7 +157,7 @@ pub mod jni { rust_instructions.push(widget); break; }, - Ok(None) => {println!("[Java_com_dropbear_ui_UINative_renderUI] Ok but None"); continue}, + Ok(None) => continue, Err(e) => { eprintln!("[Java_com_dropbear_ui_UINative_renderUI] Error converting UI instruction: {:?}", e); } @@ -199,9 +175,9 @@ pub mod jni { ui_buf_ptr: jlong, id: jlong, ) -> jboolean { - let ui = convert_ptr!(ui_buf_ptr => UiContext); - let states = ui.widget_states.lock(); - states.get(&id).map(|s| s.clicked).unwrap_or(false).into() + let ui = convert_ptr!(ui_buf_ptr => UiContext); + let states = ui.widget_states.lock(); + states.get(&id).map(|s| s.clicked).unwrap_or(false).into() } #[unsafe(no_mangle)] @@ -211,8 +187,8 @@ pub mod jni { ui_buf_ptr: jlong, id: jlong, ) -> jboolean { - let ui = convert_ptr!(ui_buf_ptr => UiContext); - let states = ui.widget_states.lock(); - states.get(&id).map(|s| s.hovering).unwrap_or(false).into() + let ui = convert_ptr!(ui_buf_ptr => UiContext); + let states = ui.widget_states.lock(); + states.get(&id).map(|s| s.hovering).unwrap_or(false).into() } } @@ -5,6 +5,48 @@ use yakui::widgets::{Button, Pad, DynamicButtonStyle}; use crate::scripting::jni::utils::FromJObject; use crate::scripting::result::DropbearNativeResult; use std::borrow::Cow; +use std::collections::HashMap; +use crate::ui::{NativeWidget, WidgetParser, WidgetState, WrapperWidget}; + +pub(crate) struct ButtonParser; + +impl WidgetParser for ButtonParser { + fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<Box<dyn NativeWidget>>> { + let class = env.get_object_class(obj)?; + let name_str_obj = env.call_method(class, "getName", "()Ljava/lang/String;", &[])?.l()?; + let name_string: String = env.get_string(&name_str_obj.into())?.into(); + // println!("ButtonParser obj get_string result: {}", name_string); + + if name_string.contains("ButtonInstruction$Button") { + let button_obj = env.get_field(obj, "button", "Lcom/dropbear/ui/widgets/Button;")?.l()?; + let btn = yakui::widgets::Button::from_jobject(env, &button_obj)?; + + let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?; + let id = env.get_field(id_obj, "id", "J")?.j()?; + + return Ok(Some(Box::new(WrapperWidget { + id, + widget: btn, + }))); + } + + Ok(None) + } + + fn name(&self) -> String { + String::from("ButtonParser") + } +} + +impl NativeWidget for WrapperWidget<yakui::widgets::Button> { + fn build(self: Box<Self>, states: &mut HashMap<i64, WidgetState>) { + let res = self.widget.show(); + states.insert(self.id, WidgetState { + clicked: res.clicked, + hovering: res.hovering, + }); + } +} impl FromJObject for Button { fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> { @@ -0,0 +1,71 @@ +use std::borrow::Cow; +use std::collections::HashMap; +use jni::JNIEnv; +use jni::objects::JObject; +use yakui::style::TextStyle; +use yakui::widgets::Pad; +use crate::scripting::jni::utils::FromJObject; +use crate::scripting::result::DropbearNativeResult; +use crate::ui::{NativeWidget, WidgetParser, WidgetState, WrapperWidget}; + +pub(crate) struct TextParser; + +impl WidgetParser for TextParser { + fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<Box<dyn NativeWidget>>> { + let class = env.get_object_class(obj)?; + let name_str_obj = env.call_method(class, "getName", "()Ljava/lang/String;", &[])?.l()?; + let name_string: String = env.get_string(&name_str_obj.into())?.into(); + // println!("TextParser obj get_string result: {}", name_string); + + if name_string.contains("TextInstruction$Text") { + let text_obj = env.get_field(obj, "text", "Lcom/dropbear/ui/widgets/Text;")?.l()?; + let text = yakui::widgets::Text::from_jobject(env, &text_obj)?; + + let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?; + let id = env.get_field(id_obj, "id", "J")?.j()?; + + return Ok(Some(Box::new(WrapperWidget { + id, + widget: text, + }))); + } + + Ok(None) + } + + fn name(&self) -> String { + String::from("TextParser") + } +} + +impl NativeWidget for WrapperWidget<yakui::widgets::Text> { + fn build(self: Box<Self>, _states: &mut HashMap<i64, WidgetState>) { + self.widget.show(); + } +} + +impl FromJObject for yakui::widgets::Text { + fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> + where + Self: Sized + { + let text_field = env.get_field(obj, "text", "Ljava/lang/String;")?; + let text_jstring = text_field.l()?.into(); + let text: String = env.get_string(&text_jstring)?.into(); + + let style_obj = env.get_field(obj, "style", "Lcom/dropbear/ui/styling/TextStyle;")?.l()?; + let style = TextStyle::from_jobject(env, &style_obj)?; + + let f = env.get_field(obj, "padding", "Lcom/dropbear/ui/styling/Padding;")?.l()?; + let padding = Pad::from_jobject( + env, + &f + )?; + + Ok(Self { + text: Cow::Owned(text), + style, + padding, + }) + } +} @@ -93,9 +93,10 @@ async fn main() -> anyhow::Result<()> { Ok(()) }) + .filter_level(LevelFilter::Warn) .filter(Some("dropbear_engine"), LevelFilter::Trace) .filter( - Some("eucalyptus-editor".replace('-', "_").as_str()), + Some("eucalyptus_editor"), LevelFilter::Debug, ) .filter(Some("eucalyptus_core"), LevelFilter::Debug) @@ -13,7 +13,6 @@ use wgpu::{Color}; use wgpu::util::DeviceExt; use winit::event_loop::ActiveEventLoop; use winit::event::WindowEvent; -use yakui::font::Fonts; use yakui_wgpu::SurfaceInfo; use dropbear_engine::camera::Camera; use dropbear_engine::buffer::ResizableBuffer; @@ -465,20 +464,26 @@ impl Scene for PlayMode { let yak = yakui_cell.borrow(); let mut yakui = yak.yakui_state.lock(); - yakui.set_surface_size(yakui::geometry::Vec2::new(display_width, display_height)); - yakui.start(); + let tex_size = graphics.viewport_texture.size; + let viewport_size = yakui::geometry::Vec2::new( + tex_size.width as f32, + tex_size.height as f32, + ); + yakui.set_surface_size(viewport_size); + yakui.set_unscaled_viewport(yakui::geometry::Rect::from_pos_size( + yakui::geometry::Vec2::ZERO, + viewport_size, + )); + yakui.set_scale_factor(graphics.window.scale_factor() as f32); - let fonts = yakui.dom().get_global_or_init(Fonts::default); - fonts.with_system(|v| { - log_once::debug_once!("font len: {}", v.db().len()); - }); + yakui.start(); eucalyptus_core::ui::poll(); - // Layer::new().show(|| { - // column(|| { - // let button_response = Button::styled("My Button") - // .padding(Pad::all(10.0)) + // yakui::widgets::Layer::new().show(|| { + // yakui::column(|| { + // let button_response = yakui::widgets::Button::styled("My Button") + // .padding(yakui::widgets::Pad::all(10.0)) // .show(); // if button_response.clicked { // println!("This is clicked!"); @@ -887,7 +892,6 @@ impl Scene for PlayMode { let mut yakui = yak.yakui_state.lock(); if let Some(yak) = &mut self.yakui_winit { yak.handle_window_event(&mut yakui, event); - } }); } Binary files /dev/null and b/resources/fonts/Roboto-Regular.ttf differ @@ -8,6 +8,9 @@ data class Padding( var top: Double, var bottom: Double, ) { + constructor(value: Double): this(value, value, value, value) + constructor(horizontal: Double, vertical: Double) : this(horizontal, horizontal, vertical, vertical) + companion object { fun zero() = Padding.all(0.0) @@ -12,6 +12,7 @@ import com.dropbear.ui.styling.Padding import com.dropbear.ui.styling.TextStyle import com.dropbear.ui.styling.fonts.TextAlignment import com.dropbear.utils.Colour +import com.dropbear.utils.ID class Button( var text: String, @@ -21,8 +22,13 @@ class Button( var style: DynamicButtonStyle, var hoverStyle: DynamicButtonStyle, var downStyle: DynamicButtonStyle, + id: WidgetId = WidgetId(text.hashCode().toLong()), ): Widget() { - override lateinit var id: WidgetId + override var id: WidgetId + + init { + this.id = id + } val clicked: Boolean get() = getClicked() @@ -0,0 +1,66 @@ +package com.dropbear.ui.widgets + +import com.dropbear.ui.UIBuilder +import com.dropbear.ui.UIInstruction +import com.dropbear.ui.Widget +import com.dropbear.ui.WidgetId +import com.dropbear.ui.styling.Padding +import com.dropbear.ui.styling.TextStyle + +class Text( + var text: String, + var style: TextStyle = TextStyle(), + var padding: Padding, + id: WidgetId = WidgetId(text.hashCode().toLong()), +) : Widget() { + override var id: WidgetId + + init { + this.id = id + } + + companion object { + fun withStyle(text: String, style: TextStyle, id: String = text): Text { + val text = Text(text, style, padding = Padding.zero()) + text.id = WidgetId(id.hashCode().toLong()) + return text + } + + fun label(text: String, id: String = text): Text { + val text = Text(text, TextStyle(), Padding.all(8.0)) + text.id = WidgetId(id.hashCode().toLong()) + return text + } + } + + sealed class TextInstruction: UIInstruction { + data class Text(val id: WidgetId, val text: com.dropbear.ui.widgets.Text) : TextInstruction() + } + + fun toInstruction(): TextInstruction.Text { + return TextInstruction.Text(this.id, this) + } +} + +fun UIBuilder.label(text: String, block: Text.() -> Unit = {}): Text { + val style = TextStyle() + val text = Text( + text = text, + style = style, + padding = Padding.zero() + ).apply(block) + instructions.add(text.toInstruction()) + return text +} + +fun UIBuilder.text(size: Double, text: String, block: Text.() -> Unit = {}): Text { + val style = TextStyle() + style.fontSize = size + val text = Text( + text = text, + style = style, + padding = Padding.zero() + ).apply(block) + instructions.add(text.toInstruction()) + return text +}