GTK-rs getting input from GTK::Entry - rust

I have a small gtk-rs application where I am trying to display an input field and get the input from the user. At the moment I can't get the input from the input field when the user clicks the button. I know button.connect_clicked is where I need to do this but I don't know how?
My build_ui method:
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Orientation};
pub fn build_ui(app: &Application) {
let button = Button::builder()
.label("Submit")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
let input = gtk::Entry::builder()
.placeholder_text("input")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
let gtk_box = gtk::Box::builder()
.orientation(Orientation::Vertical)
.build();
gtk_box.append(&input);
gtk_box.append(&button);
button.connect_clicked(move | _button| {
println!("{}", input.display());
});
let window = ApplicationWindow::builder()
.application(app)
.title("gtk-app")
.child(&gtk_box)
.default_height(720)
.default_width(360)
.build();
window.present();
}
main.rs:
const APP_ID: &str = "org.my-gtk-app";
fn main() {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(home::build_ui);
// Run the application
app.run();
}
I have found this question which is almost exactly the same however when I try and implement the solution all of the .add methods throw an error stating:
the following trait bounds were not satisfied: `ApplicationWindow: IsA<CellArea>` which is required by `ApplicationWindow: gtk4::prelude::CellAreaExt`
EDIT:
Also Gtk::Entry does not have a .get_text() method according to the compiler. After doing a bit more googling around this it seems that GTK3 had a get_text method for Gtk::Entry however Gtk4 does not.
So does anyone know how to get the text from a Gtk::Entry object when using Gtk4?
EDIT 2:
I think I needed to use the .display() method however when I run the code now I don't get the text input from the user. input.display() just returns an empty string. I have updated the code samples to reflect these changes.

The Entry does have a text() method which gives you a GString (because Entry implements Editable), that can be converted to a rust str using as_str().
So in order to get the current text from an entry, you can do the following:
let my_input_value = my_input.text().as_str();
For completeness, here is the updated build_ui function:
pub fn build_ui(app: &Application) {
let button = Button::builder()
.label("Submit")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
let input = gtk::Entry::builder()
.placeholder_text("input")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
let gtk_box = gtk::Box::builder()
.orientation(Orientation::Vertical)
.build();
gtk_box.append(&input);
gtk_box.append(&button);
button.connect_clicked(move | _button| {
println!("{}", input.text().as_str());
});
let window = ApplicationWindow::builder()
.application(app)
.title("gtk-app")
.child(&gtk_box)
.default_height(720)
.default_width(360)
.build();
window.present();
}
Edit
Because GString implements Display the following works as well:
println!("{}", input.text());

Related

what's the easy way to make a simple window and catch key presses in Rust?

I am a blind Rust learner and i'm trying to make a simple audiogame engine for blind people but in rust.
Now I'm trying to make a simple window. A game for blind uses only audio and keyboard, so i have a question.
I'm trying to create an empty window and catch key presses to be able to make a self voiced user interface and game interaction using keyboard only, without mouce. what's the best solution for that?
I tryed to use winit and winit input helper, but maybe there is a better way to do that?
use winit::event::VirtualKeyCode;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
use winit_input_helper::WinitInputHelper;
pub fn show_window() {
let mut input = WinitInputHelper::new();
let event_loop = EventLoop::new();
let _window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| {
// Pass every event to the WindowInputHelper.
// It will return true when the last event has been processed and it is time to run your application logic.
if input.update(&event) {
// query keypresses this update
if input.key_pressed_os(VirtualKeyCode::A) {
println!("The 'A' key was pressed on the keyboard (OS repeating)");
}
if input.key_pressed(VirtualKeyCode::A) {
println!("The 'A' key was pressed on the keyboard");
}
if input.key_released(VirtualKeyCode::Q) || input.quit() {
*control_flow = ControlFlow::Exit;
return;
}
}
});
}

How do I retrieve gamepad button pressed status from navigator.getGamepads() in Rust wasm-bindgen?

web_sys::Navigator.getGamepads() returns Result<Array, JsValue> whereas I was hoping for an array of websys::Gamepad objects.
How do I parse whether a gamepad button is pressed from the wasm-bindgen::JsValue result? Is there some way to convert it to a web_sys::Gamepad object?
Here's the setup for my attempt:
let window = web_sys::window().expect("global window does not exists");
let navigator = window.navigator();
let gamepads: Result<js_sys::Array, wasm_bindgen::JsValue> = navigator.get_gamepads();
for gp in gamepads.unwrap().iter() {
// ... how to parse Gamepad.buttons from JsValue?
}
Reference: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Navigator.html#method.get_gamepads
Convert JsValue into Gamepad by using wasm-bindgen::JsCast.dyn_into()
https://docs.rs/wasm-bindgen/latest/wasm_bindgen/trait.JsCast.html#method.dyn_into
let window = web_sys::window().expect("global window does not exists");
let navigator = window.navigator();
let gamepads = navigator.get_gamepads().unwrap();
for js in gamepads.iter() {
let gp: Gamepad = js.dyn_into().unwrap(); // <= convert here
let buttons = gp.buttons();
for index in 0..buttons.length() {
let js: JsValue = index.into();
console::log_2(&js, &(buttons.get(index)));
}
}

is it possible using rust to generate jsx AST?

I'm not familiar with rust and trynna using swc to replace the babel
but I found #swc/core doesn't offer the api to generate code AST, so I want to write one using rust, is it possible to generate jsx ast?
Here is a good place to start: https://github.com/swc-project/swc/blob/main/crates/swc_ecma_parser/examples/typescript.rs
but link can get invalid so I also pasted the code:
use swc_common::{
self,
errors::{ColorConfig, Handler},
sync::Lrc,
FileName, SourceMap,
};
use swc_ecma_parser::{lexer::Lexer, Capturing, Parser, StringInput, Syntax};
fn main() {
let cm: Lrc<SourceMap> = Default::default();
let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone()));
// Real usage
// let fm = cm
// .load_file(Path::new("test.js"))
// .expect("failed to load test.js");
let fm = cm.new_source_file(
FileName::Custom("test.js".into()),
"interface Foo {}".into(),
);
let lexer = Lexer::new(
Syntax::Typescript(Default::default()),
Default::default(),
StringInput::from(&*fm),
None,
);
let capturing = Capturing::new(lexer);
let mut parser = Parser::new_from(capturing);
for e in parser.take_errors() {
e.into_diagnostic(&handler).emit();
}
let _module = parser
.parse_typescript_module()
.map_err(|e| e.into_diagnostic(&handler).emit())
.expect("Failed to parse module.");
println!("Tokens: {:?}", parser.input().take());
}
I never really worked or cared about this. All it took was 5min of browsing the repository, all the resources are there but if you are new to rust, I recommend the rust book first.

Initialize Gstreamer's PadProbeId to a default value in Rust

I am familiar with Gstreamer but new to Rust,
TLDR; I want to be able to initialize PadProbeId to a default value before using it.
The details:
I have a Bin (containing audio + video encoders and hlssink).
I have been able to add this bin to the pipeline and it works fine.
The issue I have is the audio for the stream is optional and I want to do add_probe() only when audio is available. Below is a simplified version fo what I tried to implement
let mut audio_probe_id: PadProbeId;
let mut tee_audio_pad: Pad;
if media_info.audio_available {
// get encoded audio from the tee
tee_audio_pad = audio_tee.request_pad_simple("src_%u").unwrap();
audio_probe_id = tee_audio_pad.add_probe(gst::PadProbeType::BLOCK_DOWNSTREAM, |_pad, _info| {
gst::PadProbeReturn::Ok
}).unwrap();
// link the audio_tee.src to enc_bin ghost pad
let audio_sink_pad = enc_bin.static_pad("audio").unwrap();
tee_audio_pad.link(&audio_sink_pad).unwrap();
}
enc_bin.call_async(move |bin| {
bin.sync_state_with_parent().unwrap();
if media_info.audio_available {
tee_audio_pad.remove_probe(audio_probe_id);
}
}
However because of Rust compilers restriction to using uninitialized variables, it does not let me use audio_probe_id without initializing.
I tried to initialize it like this; let mut audio_probe_id: PadProbeId = PadProbeId(NonZeroU64(u64::MAX));. However compiler complains that it is a private field.
error[E0423]: cannot initialize a tuple struct which contains private fields
Thanks a lot for your help!
The rust way to have empty variables like this is to use Option, but in your case it would simpler to have a single conditional:
if media_info.audio_available {
// get encoded audio from the tee
let tee_audio_pad = audio_tee.request_pad_simple("src_%u").unwrap();
let audio_probe_id = tee_audio_pad.add_probe(gst::PadProbeType::BLOCK_DOWNSTREAM, |_pad, _info| {
gst::PadProbeReturn::Ok
}).unwrap();
// link the audio_tee.src to enc_bin ghost pad
let audio_sink_pad = enc_bin.static_pad("audio").unwrap();
tee_audio_pad.link(&audio_sink_pad).unwrap();
enc_bin.call_async(move |bin| {
bin.sync_state_with_parent().unwrap();
tee_audio_pad.remove_probe(audio_probe_id);
}
} else {
enc_bin.call_async(move |bin| {
bin.sync_state_with_parent().unwrap();
});
}

Can't set button color

Based on this UI example I've add a custom button at the end of on_start method. But when I run the game the button body is invisible, only its text is shown ("1234567").
The code I added to the example:
// ...
use amethyst::assets::AssetStorage;
use amethyst::assets::Handle;
use amethyst::assets::Loader;
use amethyst::renderer::loaders::load_from_srgba;
use amethyst::renderer::palette::Srgba;
use amethyst::renderer::Texture;
use amethyst::renderer::types::TextureData;
use amethyst::ui::Anchor;
use amethyst::ui::UiButtonBuilder;
use amethyst::ui::UiImage;
// ...
impl SimpleState for Example {
fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) {
// ...
let texture_handle: Handle<Texture> = {
let loader = world.read_resource::<Loader>();
let texture_assets = world.read_resource::<AssetStorage<Texture>>();
let texture_builder = load_from_srgba(Srgba::new(0.8, 0.6, 0.3, 1.0));
loader.load_from_data(TextureData::from(texture_builder), (), &texture_assets)
};
let button = UiButtonBuilder::<(), u32>::new("1234567".to_string())
.with_id(666)
.with_font_size(12.0)
.with_position(64.0, -64.0)
.with_size(64.0 * 3.0, 64.0)
.with_anchor(Anchor::TopMiddle)
// HAS NO EFFECT
.with_image(texture_handle)
// HAS NO EFFECT
.with_hover_image(UiImage::SolidColor([0.1, 0.1, 0.1, 0.5]))
.with_layer(12.0)
.build_from_world(&world);
world.insert(button);
}
}
// ...
What is the proper way to create a button on the fly with custom color?
I'm using amethyst v0.15.
There is a bug with the UiButtonBuilder. I just created this issue: https://github.com/amethyst/amethyst/issues/2298

Resources