How do I use mouse button events with gtk4-rs? - rust

I'm trying to capture up a right click event on a TreeView I have in my application using Rust and gtk4-rs. I don't see a specific EventController for mouse buttons events. I do see EventControllers for things like keyboard presses and motion events. I ended up using the EventControllerLegacy and just detecting the event type inside the closure, but I feel like I'm missing something here. Is there some other way I should be getting button press events from the mouse? Here's my code:
pub fn build_tree_view() -> TreeView {
let tree_view: TreeView = TreeView::new();
tree_view.set_headers_visible(false);
let event_controller = EventControllerLegacy::new();
event_controller.connect_event(|controller, event| {
if event.event_type() == ButtonPress {
let button_event = event.clone().downcast::<ButtonEvent>().unwrap();
if button_event.button() == GDK_BUTTON_SECONDARY as u32 {
println!("got mouse button: {}", button_event.button());
}
}
return Inhibit(false);
});
tree_view.add_controller(&event_controller);
return tree_view;
}
The reason I believe I am missing something is that the documentation for EventControllerLegacy states the following:
EventControllerLegacy is an event controller that provides raw access to the event stream. It should only be used as a last resort if none of the other event controllers or gestures do the job.
I am using Rust 1.56.0 and gtk4-rs 0.4.2
Thanks

If you look into the examples, normally in such a case gtk::GestureClick is used.
To detect right clicks, you can use it like so:
pub fn build_tree_view() -> TreeView {
let tree_view: TreeView = TreeView::new();
tree_view.set_headers_visible(false);
// Create a click gesture
let gesture = gtk::GestureClick::new();
// Set the gestures button to the right mouse button (=3)
gesture.set_button(gtk::gdk::ffi::GDK_BUTTON_SECONDARY as u32);
// Assign your handler to an event of the gesture (e.g. the `pressed` event)
gesture.connect_pressed(|gesture, _, _, _| {
gesture.set_state(gtk::EventSequenceState::Claimed);
println!("TreeView: Right mouse button pressed!");
});
// Assign the gesture to the treeview
tree_view.add_controller(&gesture);
return tree_view;
}
Note: Example is for Rust 1.58 and gtk4 0.4.4 but the differences should be minor if any.

Related

What is the gtk-rs design pattern for changing the widgets on a page?

I am using gtk-rs to build a small application and I am struggling with the concept of changing the widgets displayed on a page. So far I have been working through this guide but I have not found the information I am looking for.
An example would be you open the app and you start on the landing page, you then click a button and it takes you to another page/view which has different widgets on it.
I know how to build a page with a single set of widgets, I can update the contents of those widgets but I don't know how to either load a completely new layout or remove widgets and replace them.
Has anyone got a link to a guide or something similar detailing how to do this? If this isn't the way we're supposed to do this with gtk-rs can you point me to something detailing how we should be doing this?
I have been googling things like gtk-rs redraw window, gtk-rs change widgets, gtk-rs new view but none of these have returned an explanation of how to handle this problem.
An example from the guide is below. Assuming we pressed the button in this application I would then want to load a page with different buttons on it.
fn main() {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run();
}
fn build_ui(app: &Application) {
// Create a button
let button = Button::builder()
.label("Press me!")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
// Connect to "clicked" signal of `button`
button.connect_clicked(move |_| {
println!("The button was pressed!");
});
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&button)
.build();
// Present window
window.present();
}
I use Stack for this purpose. Here, have a look at this link. Use StackPage to add pages.
Here's an example.
use gtk::Application;
use gtk::{prelude::*, ApplicationWindow};
use gtk::StackTransitionType::SlideLeftRight;
fn main() {
let app = Application::builder()
.application_id("com.artognak.markiv")
.build();
app.connect_activate(build_ui);
app.run();
}
fn build_ui(app: &Application) {
let window = ApplicationWindow::builder()
.application(app)
.default_width(360)
.default_height(360)
.build();
let container = gtk::Box::new(gtk::Orientation::Vertical, 100);
window.set_child(Some(&container));
let stack = gtk::Stack::new();
stack.set_transition_type(SlideLeftRight);
stack.set_transition_duration(200);
let page_1_label = gtk::Label::new(Some("Page 1"));
stack.add_titled(&page_1_label, Option::<&str>::None, "Page 1");
let page_2_label = gtk::Label::new(Some("Page 2"));
stack.add_titled(&page_2_label, Option::<&str>::None, "Page 2");
let stack_switcher = gtk::StackSwitcher::new();
stack_switcher.set_stack(Some(&stack));
container.append(&stack_switcher);
container.append(&stack);
window.present();
}
You can also switch pages by setting the visible child name for the Stack. This way you won't need StackSwitcher.

Using GTK and Rust. Display image on button click but change image based on variable

I'm trying to make a Blackjack game using rust, the main logic is going fairly well so I started implementing a GUI using GTK but having some issues changing the image dynamically.
When I click hit I want to run my hit function which will pop a card out of the deck and check for bust. I then want the card to be displayed on the screen. I'm using glade to set it all up however I don't know how to change the path of the image file. I can select the path in glade but obviously I need it to update based on the card which has been drawn.
This is the code so far which I copied from a tutorial and modified slightly.
use gio::prelude::*;
use gtk::prelude::*;
use gtk::Application;
use std::env;
pub fn new() {
let app = Application::new(Some("com.game"), gio::ApplicationFlags::FLAGS_NONE)
.expect("Failed to initialize GTK");
app.connect_activate(|app| {
let glade_src = include_str!("../layout.glade");
let builder = gtk::Builder::from_string(glade_src);
let window: gtk::Window = builder.get_object("background").unwrap();
window.set_application(Some(app));
let hit_btn: gtk::Button = builder.get_object("hit_btn").unwrap();
let img_out: gtk::Image = builder.get_object("player_card_one").unwrap();
let img_out_clone = img_out.clone();
hit_btn.connect_clicked(move |_| {
img_out_clone.show();
});
window.show_all();
img_out.hide();
});
app.run(&env::args().collect::<Vec<_>>());
}

Seemingly nothing happens when attempting to add to a `gtk::ListBox` from within an event handler in GTK-RS application

I'm attempting to to add to a gtk::ListBox container from within the event handling closure of an unrelated widget. The list box in question is fetched via a gtk::Builder like so:
let notes_list: gtk::ListBox = builder.get_object(NOTES_LIST_BOX_ID).unwrap();
And the event handler where I can't seem to add to notes_list (note that I've tried without the clone! macro, with strong and weak references, wrapping in Rc pointer, etc. but nothing seems to change):
open_menu_item.connect_activate(clone!(#strong state, #strong notes_list => move |_| {
println!("Open database menu item activated");
// Seemingly can't add to notes_list from within this closure???
notes_list.add(&gtk::Label::new(Some("TEST"))); // Doesn't work???
let dialog = gtk::FileChooserDialog::with_buttons::<gtk::Window>(
Some("Open database file"),
None,
gtk::FileChooserAction::Open,
&[("_Cancel", gtk::ResponseType::Cancel),
("_Open", gtk::ResponseType::Accept)]
);
dialog.connect_response(clone!(#weak state, #weak notes_list => move |this, res| {
if res == gtk::ResponseType::Accept {
let file = this.get_file().unwrap();
let path_buf = file.get_path().unwrap();
println!("Opening database file: {}", path_buf.as_path().display());
let mut state = state.borrow_mut();
state.db = database::database_in_file(path_buf.as_path()).ok();
state.update_notes_list(&notes_list);
}
this.close();
}));
dialog.show_all();
}));
No error message is presented - the expected behaviour (i.e. the addition of a gtk::Label to the list box) does not occur.
The full code of this module (and the rest of my messy code base): https://github.com/WiredSound/nos/blob/master/src/gui.rs
If anyone could help me figure this out then I'd really appreciate it, thanks.
Widgets, in GTK3, are hidden by default. This means that calling show_all() on a container will show all its current children. If you add a new child, you're responsible for calling show() on it in order to make it visible.
In your signal handler, you're adding a gtk::Label to the list box, but you need to make it visible as well.

Phaser Game Keyboard Arrow Keys to Mobile Touch

I'm new to Phaser and am following this tutorial here: https://phaser.io/tutorials/making-your-first-phaser-3-game/part7
What do I need to change for use on a mobile cell phone browser to make the player automatically run to the right and jump when the screen is tapped? I've searched for ages but can't find the answer and am trying to learn
// Input Events
cursors = this.input.keyboard.createCursorKeys();
if (cursors.left.isDown)
{
player.setVelocityX(-160);
player.anims.play('left', true);
}
else if (cursors.right.isDown)
{
player.setVelocityX(160);
player.anims.play('right', true);
}
else
{
player.setVelocityX(0);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down)
{
player.setVelocityY(-330);
}
What I would do:
Add an input handler to your general game that calls a 'jump' function when a pointer is down.
this.input.on('pointerdown', this.jump, this);
Jump should now verify that the player can jump. Something like:
jump() {
if (this.player.body.touching.down) {
this.player.setVelocityY(-330);
}
}
To have your player automatically move at a speed, you can just set the player's body velocity when you're creating the player.
this.player.body.velocity.x = 160;
Note that if you want to keep the current ability to control the player, another option is to have actual buttons on the screen that the user can tap/click on to have the player move/act accordingly. There's an official plugin for Phaser 2 that does this, that you could look at.

How to hide keyboard when touch on another control (such as button) Swift 2.0

I've read this page: Close iOS Keyboard by touching anywhere using Swift
to find out how to hide keyboard, it's good when i touch on view, but when i touch on button it not work, does anyone know?
Paste this in your controller:
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourViewController.dissmissKeyboard))
view.addGestureRecognizer(tap)
#objc func dissmissKeyboard() {
view.endEditing(true)
}
This is rather an hack, but it works pretty well and the keyboard even hides when tapped on background. I'm not sure if it works out for your problem with the button, but have a try!
EDIT: Try to add the UITapGestureRecognizer to your button?
in viewDidLoad add code:
self.hideKeyboardWhenTappedAround()
Then add an extension to your ViewController:
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
Works really well for me.
SWIFT 4
Add this to your viewDidLoad.
let endEditingTapGesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing(_:)))
endEditingTapGesture.cancelsTouchesInView = false
view.addGestureRecognizer(endEditingTapGesture)

Resources