My Gtk app currently reads data from an external database/file, then add it to a Gtk Treeview. One of the columns allows newlines, which is correctly displayed in the Treeview when single-paragraph-mode is false.
However, when I double click to edit, the cell turns into a single line entry box where a carriage return symbol (like ⤶) replaces the newlines.
This makes it impossible to edit a cell with lots of newlines because they appear as one single line. Is it possible to render the newlines as actual newlines during editing? In other words, I want single-paragraph-mode=false to apply to the cell when it is being edited
I've read the documentation for CellRendererText and CellRenderer, but it is rather lacking. Things like max-width-chars apply to the cell whether editing or not.
This code demonstrates what I mean. (It uses gtk-rs but an answer using C is fine too):
use gtk::glib;
use gtk::prelude::*;
fn main() {
let application = gtk::Application::new(
Some("com.example.xyz"),
Default::default(),
);
application.connect_activate(build_ui);
application.run();
}
fn build_ui(application: >k::Application) {
let window = gtk::ApplicationWindow::new(application);
window.set_title(Some("App"));
window.set_default_size(800, 800);
let grid = gtk::Grid::builder()
.column_spacing(5)
.row_spacing(10)
.build();
window.set_child(Some(&grid));
let (treeview, _treestore) = build_treeview();
let sw = gtk::ScrolledWindow::builder()
.child(&treeview)
.vexpand(false)
.vexpand_set(false)
.max_content_height(10)
.build();
// column, row, width, height
grid.attach(&sw, 0, 0, 1, 1);
window.show();
}
fn build_treeview() -> (gtk::TreeView, gtk::TreeStore) {
let treestore = gtk::TreeStore::new(&[glib::Type::STRING].repeat(4));
let treeview = gtk::TreeView::builder()
.hexpand(true)
.vexpand(true)
.enable_grid_lines(gtk::TreeViewGridLines::Both)
.model(&treestore)
.build();
for (i, title) in ["Title", "Description", "Path1", "Path2"]
.iter()
.enumerate()
{
let cell_renderer = gtk::CellRendererText::builder()
.editable(true)
.xalign(0.0)
.single_paragraph_mode(false)
.build();
treeview.insert_column_with_attributes(
i.try_into().unwrap(),
title,
&cell_renderer,
&[(&"text", i.try_into().unwrap())],
);
let ts = treestore.clone();
cell_renderer.connect_edited(move |_renderer, row, text| {
ts.set_value(
&ts.iter(&row).unwrap(),
i.try_into().unwrap(),
&text.to_value()
);
});
}
treestore.insert_with_values(
None,
None,
&[
(0, &"one"),
(1, &"two\ntwo\ntwo\ntwo"),
(2, &"three"),
(3, &"four"),
],
);
(treeview, treestore)
}
Related
I want contents' size in a imgui window looks always same, but on resizing OS window, imgui window size and its contents scale too. Actually contents looks 'wrong' when resized, this is scaled down figure of 'hello world' example from imgui-rs repository.
I think I should achieve it by using glViewport, but it looks like I have no access to the function or equivalent things, and ContextWrapper::resize has no effect.
Event::NewEvents(_) => {
let now = Instant::now();
imgui.io_mut().update_delta_time(now - last_frame);
last_frame = now;
}
Event::MainEventsCleared => {
let gl_window = display.gl_window();
platform
.prepare_frame(imgui.io_mut(), gl_window.window())
.expect("Failed to prepare frame");
gl_window.window().request_redraw();
}
Event::RedrawRequested(_) => {
let ui = imgui.new_frame();
let mut run = true;
run_ui(&mut run, ui);
if !run {
*control_flow = ControlFlow::Exit;
}
let gl_window = display.gl_window();
let mut target = display.draw();
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
platform.prepare_render(ui, gl_window.window());
let draw_data = imgui.render();
renderer
.render(&mut target, draw_data)
.expect("Rendering failed");
target.finish().expect("Failed to swap buffers");
}
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
display.gl_window().resize(size);
}
What should I do to keep contents' size constant and to render things correctly?
I'm using imgui with glium and winit.
I have a tui app where a user is presented with some choices through a list. Once they navigate to the choice they want and hit enter I'd like to take them to the "next" screen.
It's more complicated than just clearning existing text and printing new one because I also need to replace keybindings and basically start a new tui-rs loop. More below.
Code for Screen 1:
pub fn draw_screen() -> Result<(), Box<dyn Error>> {
// Terminal initialization
let stdout = io::stdout().into_raw_mode()?;
let stdout = MouseTerminal::from(stdout);
let stdout = AlternateScreen::from(stdout);
let backend = TermionBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let events = Events::new();
loop {
terminal.draw(|f| {
// user shown a list they can navigate through using arrow keys
});
match events.next()? {
Event::Input(input) => match input {
Key::Char('q') => {
break;
}
Key::Char('\n') => {
// this is where I need to "send" them to a new screen
}
Key::Down => {
// my_list won't exist on next screen
my_list.items.next();
}
Key::Up => {
my_list.items.previous();
}
_ => {}
},
_ => {}
}
}
Ok(())
}
As can be seen the keybindings at the bottom are specific to this screen. Eg on the next screen there's not going to be a my_list and instead there might be a my_another_list or my_box or nothing at all.
So if all I did was clear the text, I'd still be left inside the same loop with the same keybindings - doesn't work.
What's the right way to initiate a new loop with fresh keybindings?
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
I'm using Rust to write an ncurses app.
I'm trying to set the color of a subwin, however having no success. I'm not even sure the window is created in the first place, or it just doesn't want to set the color.
Here's a minimal example:
use ncurses::*;
fn main() {
setlocale(LcCategory::all, "");
initscr();
keypad(stdscr(), true);
start_color();
init_pair(1, COLOR_RED, COLOR_RED);
loop {
let user_input = get_wch();
match user_input.unwrap() {
WchResult::Char(ch) => {
match ch {
27 => break,
_ => {}
}
},
WchResult::KeyCode(code) => {
match code {
KEY_F5 => {
let ln = subwin(stdscr(), LINES(), 5, 0, 0);
wbkgd(ln, COLOR_PAIR(1));
refresh();
},
_ => {}
}
}
}
}
endwin();
}
As you can see, I initialized a color pair and invoked start_colors().
What could be the issue?
I think the problem might be that your not refreshing the sub-window. Try using wrefresh(ln) instead. Actually use both refresh and refresh(ln).
In this chunk
let ln = subwin(stdscr(), LINES(), 5, 0, 0);
wbkgd(ln, COLOR_PAIR(1));
refresh();
the refresh overwrites the result from the subwin. Also, you would get better results by ORing the COLOR_PAIR with a space (see this).
Addressing the comments:
let user_input = get_wch();
also does a refresh (overwriting the result from the subwin).
I would like to implement a fuzzy search for the GTK EntryCompletion functionality via set_match_func. There is very limited documentation.
Note that the code works with the default EntryCompletion.
The function should look something like this:
fn custom_entry_completion(store: >k::EntryCompletion, text: &str, ti: >k::TreeIter) -> bool {
println!("{} // {:?}", text, ti);
true
}
I want to embed it in something like this:
let completion_countries = gtk::EntryCompletion::new();
completion_countries.set_match_func(custom_entry_completion);
I would like a fuzzy match, but I think I will manage that part myself. Most helpful would be an example where the match is case insensitive or matching the last part of the string (or something). I am looking for a good example (preferably without unsafe) and/or good documentation.
So I need to take the value from the TreeIter and check if the match is as I want it to be. Hence, my question is how to get the item from the TreeIter with which to compare text against.
Documentation
EntryCompletionExt::get_text_column
EntryCompletionExt::get_model
TreeModelExt::get_value
glib::value
An example
fn custom_entry_completion(store: >k::EntryCompletion, text: &str, ti: >k::TreeIter) -> bool {
let tree_model = store.get_model().unwrap();
let text_column = store.get_text_column();
let ti_text_value = tree_model.get_value(ti, text_column);
if ti_text_value.is::<String>() {
let value = ti_text_value.get::<String>().unwrap();
println!("{:?} // {:?}", text, value);
} else {
println!("{:?} // not a string", text);
}
true
}
Console output when used with the entry_completion.rs example:
"adfs" // "France"
"adfs" // "Italy"
"adfs" // "Italy"
"adfs" // "Sweden"
"adfs" // "Sweden"
"adfs" // "Switzerland"
"adfs" // "Switzerland"
"adfssdf" // "France"
"adfssdf" // "Italy"
"adfssdf" // "Sweden"
"adfssdf" // "Switzerland"