Gtk-rs application crashes at window.show_all() function - rust

My Gtk-rs application crashes whenever I try calling the show_all() function.
In a simple application window, I have added a headerbar and a label. If I compile without adding the headerbar, the window works and shows the label as intended. However if I add the headerbar the window crashes.
use gio::prelude::*;
use gtk::{
prelude::*,
HeaderBarExt,
GtkWindowExt
};
fn gen_header_bar(subtitle: Option<String>) -> gtk::HeaderBar {
let header_bar = gtk::HeaderBar::new();
header_bar.set_title(Some(crate::consts::APP_NAME));
header_bar.set_show_close_button(true);
match subtitle {
Some(subtitle) => {
header_bar.set_subtitle(Some(&subtitle));
},
_ => {
}
}
header_bar
}
pub fn build_application_window() -> Result<(), Box<dyn std::error::Error>> {
let application = gtk::Application::new(
Some(crate::consts::APP_ID),
gio::ApplicationFlags::FLAGS_NONE,
)?;
application.connect_activate(move |app| {
let window = gtk::ApplicationWindow::new(app);
window.set_title(crate::consts::APP_NAME);
window.set_default_size(32 * 10, 200); // golden ratio
window.set_position(gtk::WindowPosition::Center);
let header_bar = gen_header_bar(None);
window.set_titlebar(Some(&header_bar));
window.add(&{
let label = gtk::Label::new(Some("Welcome!"));
label
});
window.show_all(); // crashes here
});
application.run(&std::env::args().collect::<Vec<_>>());
Ok(())
}
What is causing this?

Related

How to create new window through window menu in Tauri?

I've tried to create new app window through window menu. But there i faced with issue that method
create_window
is not available
How can i do that?
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use tauri::{Menu, WindowMenuEvent, Wry, Submenu, CustomMenuItem, WindowUrl, WindowBuilder};
pub fn create() -> Menu {
let test_menu = Menu::new()
.add_item(CustomMenuItem::new("new_window", "Create new window"));
Menu::new()
.add_submenu(Submenu::new("Test", test_menu))
}
pub fn handler(event: WindowMenuEvent<Wry>) {
match event.menu_item_id() {
"new_window" => {
let mut window = event.window().clone();
}
_ => {}
}
}
fn main() {
tauri::Builder::default()
.menu(create())
.on_menu_event(handler)
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
I created menu and cloned main window(i need to create same window) in event

RUST + FLTK: accessing another widget from widget on_push() function

I used FLTK to create a window and two buttons inside, the btn_A has a callback and should change the btn_B label, but I dont see any non-monstrous approach do to this, ples halp? =''[
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application=app::App::default();
let mut win = window::Window::default().with_size(500,300);
//BTN_A
let mut btn_A:Listener<_> = button::Button::new(100,100,100,50,"btn_A").into();
//BTN_B
let mut btn_B:Listener<_> = button::Button::new(300,100,100,50,"btn_B").into();
//BTN_A_CALLBACK
btn_A.handle(|elem,evt| match evt {
enums::Event::Push => { btn_A(elem); true }
_ => { false }
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A(elem:&mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
}
In principle all that is needed is to pass a mutable reference to btn_B to your handler function:
pub fn btn_A(elem:&mut button::Button, btn_B: &mut button::Button) {
...
}
However there is one slight problem with your code: You named the function the same as the variable that holds your button.
Apart from that in the most recent version of the fltk crate (v.1.2.23, that I used because you did not specify which version you used in your question) there does not seem to be a Listener<_> type.
Here is an example based on the snippet you posted for changing the label of btn_B:
use fltk::{prelude::{WidgetExt, GroupExt, WidgetBase}, window, app, button, enums};
fn main() {
showMainWindow();
}
pub fn showMainWindow() {
//WINDOW
let application = app::App::default();
let mut win = window::Window::default().with_size(500, 300);
//BTN_A
let mut btn_A = button::Button::new(100, 100, 100, 50, "btn_A");
//BTN_B
let mut btn_B = button::Button::new(300, 100, 100, 50, "btn_B");
//BTN_A_CALLBACK
btn_A.handle(move |elem, evt| match evt {
enums::Event::Push => {
btn_A_click(elem, &mut btn_B);
true
}
_ => false,
});
win.end();
win.show();
application.run().unwrap();
}
pub fn btn_A_click(elem: &mut button::Button, btn_B: &mut button::Button) {
elem.deactivate(); //deactivate itself
//but how do I access btn_B here?
btn_B.set_label("New title.")
}
Also note, that the handle closure now takes ownership of btn_B because of the move keyword.

Proper way to update a TextView in Rust's Cursive library

I'm currently working on writing up a tui for a server application in a personal project, and I picked the cursive library to do it. I have run into a problem with a component of my application that is supposed to serve as the server's console window. In setting up the ability to output text to the window, I've run into a problem where I can't update the TextView.
According to the last comment in this issue, the way to do this is to use a TextContent and update that, which I set up below. While the initial content will show up in the UI, no updates to the content do. I have tried setting content on the TextView directly by giving it a name, but no luck. I've been able to get_content on the TextView itself and see that the content has been updated, it just isn't reflected in the UI.
Is there any way to do this, or do I need to make a custom view for the output?
Here is the code in question:
use std::ops::Add;
use std::borrow::{BorrowMut, Borrow};
use cursive::{View, Printer, Vec2, Rect, With};
use cursive::event::{Event, EventResult, AnyCb, Key};
use cursive::view::{Selector, ViewNotFound, ViewWrapper};
use cursive::direction::Direction;
use cursive::views::{EditView, LinearLayout, OnEventView, DummyView, TextView, TextContent};
use cursive::traits::{Nameable, Scrollable, Resizable};
use cursive::view::scroll::Scroller;
pub const CONSOLE_INPUT_NAME: &str = "console_input";
pub const CONSOLE_OUTPUT_LENGTH: u16 = 1000;
pub(crate) struct ConsoleView {
inner_view: LinearLayout,
console_output: TextContent,
console_output_history: LinkedList<String>
}
impl Default for ConsoleView {
fn default() -> Self {
let console_output = TextContent::new("This content should get replaced.");
let inner_view = LinearLayout::vertical()
.child(TextView::new_with_content(console_output.clone())
.full_screen()
.scrollable()
.wrap_with(OnEventView::new)
.on_pre_event_inner(Key::PageUp, |v, _| {
let scroller = v.get_scroller_mut();
if scroller.can_scroll_up() {
scroller.scroll_up(
scroller.last_outer_size().y.saturating_sub(1),
);
}
Some(EventResult::Consumed(None))
})
.on_pre_event_inner(Key::PageDown, |v, _| {
let scroller = v.get_scroller_mut();
if scroller.can_scroll_down() {
scroller.scroll_down(
scroller.last_outer_size().y.saturating_sub(1),
);
}
Some(EventResult::Consumed(None))
}))
.child(DummyView)
.child(EditView::new().with_name(CONSOLE_INPUT_NAME));
return Self {
inner_view,
console_output,
console_output_history: LinkedList::new()
}
}
}
impl ViewWrapper for ConsoleView {
type V = LinearLayout;
fn with_view<F, R>(&self, f: F) -> Option<R> where F: FnOnce(&Self::V) -> R {
return Some(f(self.inner_view.borrow()));
}
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R> where F: FnOnce(&mut Self::V) -> R {
return Some(f(self.inner_view.borrow_mut()));
}
fn into_inner(self) -> Result<Self::V, Self> where Self: Sized, Self::V: Sized {
return Ok(self.inner_view);
}
fn wrap_draw(&self, printer: &Printer) {
self.inner_view.draw(printer);
}
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 {
return req;
}
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
return self.inner_view.on_event(ch);
}
fn wrap_layout(&mut self, size: Vec2) {
self.inner_view.layout(size);
}
fn wrap_take_focus(&mut self, source: Direction) -> bool {
return self.inner_view.take_focus(source);
}
fn wrap_call_on_any<'a>(&mut self, selector: &Selector<'_>, callback: AnyCb<'a>) {
self.inner_view.call_on_any(selector, callback);
}
fn wrap_focus_view(&mut self, selector: &Selector<'_>) -> Result<(), ViewNotFound> {
return self.inner_view.focus_view(selector);
}
fn wrap_needs_relayout(&self) -> bool {
return self.inner_view.needs_relayout();
}
fn wrap_important_area(&self, size: Vec2) -> Rect {
return self.inner_view.important_area(size);
}
}
impl ConsoleView {
pub fn print(&mut self, line: &str) {
log::info!("Printing to console: {}", line);
self.console_output_history.push_back(line.to_string());
if self.console_output_history.len() > CONSOLE_OUTPUT_LENGTH as usize {
self.console_output_history.pop_front();
}
let out = self.console_output_history.iter().fold("".to_string(), |acc, s| {
return acc.add(s.as_str()).add("\n");
});
self.console_output.set_content(out);
}
}
Here is a main() to go with to demonstrate the problem. Basically, once the UI is set up and the event loop is started, I can't update the information displayed in the UI. Given that the UI doesn't display until I start the event loop, this is a problem.
let mut siv = cursive::default();
siv.add_layer(ConsoleView::default().with_name("view_name"));
siv.add_global_callback(Key::Esc, |s| s.quit());
siv.run();
siv.call_on_name("view_name", |view: &mut ConsoleView| {
view.print("I should see this text in the app.");
});
}

How can I create a MessageDialog using winrt-rs?

I am attempting to show a MessageDialog upon keypress. So far, it doesn't seem to do anything. The code does compile, but does not function. The code verifies that the keypress works, but the dialog simply wont display. I have tried taking the returned IAsyncOperation and using 'get()' but that seems to freeze the application completely. What am I missing?
//#![windows_subsystem = "windows"]
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
use winrt::*;
import!(
dependencies
os
modules
"windows.data.xml.dom"
"windows.foundation"
"windows.ui"
"windows.ui.popups"
);
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Resized (_size),
..
} => (),
Event::WindowEvent {
event: WindowEvent::KeyboardInput {input,..},
..
} if input.state == winit::event::ElementState::Pressed => {
use windows::ui::popups::MessageDialog;
let mymsg = MessageDialog::create("Test").unwrap().show_async();
println!("KeyState-{}",input.scancode);
},
_ => (),
}
});
}
This highlights one of the differences in using some WinRT APIs in a Win32 application. In a UWP application, your app has a CoreWindow associated with its main thread. Normally, dialogs query for this window and display themselves as modal to it. However, in a Win32 application the system can't make an assumption about what window you want to use. In these instances, you need to QI for the IInitializeWithWindow interface and call the initialize function with your window handle.
Since the IInitializeWithWindow interface is a pure COM interface and not a WinRT one, winrt-rs doesn't have a projection for it. Instead you'll need to define it yourself (make sure you get the GUID right!):
#[repr(C)]
pub struct abi_IInitializeWithWindow {
__base: [usize; 3],
initialize: extern "system" fn(
winrt::NonNullRawComPtr<InitializeWithWindowInterop>,
*mut c_void,
) -> winrt::ErrorCode,
}
unsafe impl winrt::ComInterface for InitializeWithWindowInterop {
type VTable = abi_IInitializeWithWindow;
fn iid() -> winrt::Guid {
winrt::Guid::from_values(1047057597, 28981, 19728, [128, 24, 159, 182, 217, 243, 63, 161])
}
}
#[repr(transparent)]
#[derive(Default, Clone)]
pub struct InitializeWithWindowInterop {
ptr: winrt::ComPtr<InitializeWithWindowInterop>,
}
impl InitializeWithWindowInterop {
pub fn initialize(
&self,
window: *mut c_void,
) -> winrt::Result<()> {
match self.ptr.abi() {
None => panic!("The `this` pointer was null when calling method"),
Some(this) => unsafe {
(this.vtable().initialize)(
this,
window,
)
.ok()?;
Ok(())
},
}
}
}
To get the window handle from your winit Window, you'll need the raw-window-handle crate. From there you can create a helper trait for anything that implements HasRawWindowHandle:
trait InitializeWithWindow {
fn initialize_with_window<O: RuntimeType + ComInterface>(&self, object: &O) -> winrt::Result<()>;
}
impl<T> InitializeWithWindow for T
where
T: HasRawWindowHandle,
{
fn initialize_with_window<O: RuntimeType + ComInterface>(
&self,
object: &O,
) -> winrt::Result<()> {
// Get the window handle
let window_handle = self.raw_window_handle();
let window_handle = match window_handle {
raw_window_handle::RawWindowHandle::Windows(window_handle) => window_handle.hwnd,
_ => panic!("Unsupported platform!"),
};
let init: InitializeWithWindowInterop = object.try_into()?;
init.initialize(window_handle)?;
Ok(())
}
}
Now in your event loop you can call it using the following:
let dialog = MessageDialog::create("Test").unwrap();
window.initialize_with_window(&dialog).unwrap();
dialog.show_async().unwrap();
println!("KeyState-{}",input.scancode);
Note that I'm not waiting on the result of the IAsyncOperation that is retunred by show_async. The reason is because only synchronous waiting it supported by the projection right now, and that would tie up your message pump and cause the window to hang. This means that the print statement will run before the dialog returns. This should improve once broader async support is up and running in the projection.
You can now use your initialize_with_window method on dialogs and pickers (e.g. FileSavePicker, GraphicsCapturePicker).

How to connect Buttons to Actions in custom SimpleActionGroups in gtk-rs?

I'd like to use a SimpleActionGroup registered to a Widget that handles SimpleActions. But anyhow every Button that targets to Actions with my custom prefix are grayed out and can't be clicked. However Buttons that are targeting the action prefixes "app" and "win" are working as expected.
Application with two Buttons
Here is the part in my code where my custom prefix is defined.
// Create action group for view
let view_action_group = gio::SimpleActionGroup::new();
view.insert_action_group("view", &view_action_group);
// SimpleAction for view
let view_action = gio::SimpleAction::new("test", None);
view_action.connect_activate(|_, _| println!("view.test"));
view_action_group.add_action(&view_action);
Here I respond to SimpleActions with prefix "app"
// SimpleAction for app
let app_action = gio::SimpleAction::new("test", None);
app_action.connect_activate(|_, _| println!("app.test"));
app.add_action(&app_action);
The SimpleAction itself should be ok, because if I start the application with GTK Inspector I can activate it.
GTK Inspector invoking actions
So what am I missing that the secound Button is not greyed out and responds to my SimpleAction?
Here is the full code:
extern crate gtk;
extern crate gio;
use std::env::args;
use gtk::{GtkApplicationExt, GtkWindowExt, ContainerExt, WidgetExt, HeaderBarExt, ActionableExt};
use gio::{SimpleActionExt, ActionMapExt, ApplicationExt, ApplicationExtManual};
struct Window {
pub window: gtk::Window,
}
impl Window {
pub fn new() -> Self {
// Create Widgets and window
let header_bar = gtk::HeaderBar::new();
let window = gtk::Window::new(gtk::WindowType::Toplevel);
let view = gtk::Box::new(gtk::Orientation::Horizontal, 5);
let app_button = gtk::Button::new_with_label("App-Action-Button");
let view_button = gtk::Button::new_with_label("View-Action-Button");
// Create Action Group for view
let view_action_group = gio::SimpleActionGroup::new();
view.insert_action_group("view", &view_action_group);
// SimpleAction for view
let view_action = gio::SimpleAction::new("test", None);
view_action.connect_activate(|_, _| println!("view.test"));
view_action_group.add_action(&view_action);
// Set Action for Buttons
app_button.set_action_name("app.test");
view_button.set_action_name("view.test");
// Configure HeaderBar and add Buttons to it
header_bar.set_show_close_button(true);
header_bar.pack_start(&app_button);
header_bar.pack_start(&view_button);
// Window Stuff
window.set_titlebar(&header_bar);
window.add(&view);
window.show_all();
Self { window }
}
}
struct App {
app: gtk::Application
}
impl App {
pub fn new () -> Self {
// Create app and window
let app = gtk::Application::new(Some("com.example.test"), gio::ApplicationFlags::FLAGS_NONE).unwrap();
let window = Window::new();
// SimpleAction for app
let app_action = gio::SimpleAction::new("test", None);
app_action.connect_activate(|_, _| println!("app.test"));
app.add_action(&app_action);
// add Signal
let cloned_window = window.window.clone();
app.connect_activate(move |app| app.add_window(&cloned_window));
Self { app }
}
pub fn run(&self, args: &[String]) {
self.app.run(args);
}
}
fn main() {
let app = App::new();
app.run(&args().collect::<Vec<_>>());
}

Resources