How to add a hover effect to a GtkTreeView cell - c

I have a GtkTreeView with two columns of type text (e.g. G_TYPE_STRING) and I am using GtkCellRendererText to render the column.
Is there any why that I can react when the mouse enters and leaves a certain cell and then hover or highlight the cell.
For example I would like to underline the text in the cell renderer, when the mouse enters it, in order to give a visual clue that the cell can be clicked to perform an action.

There are quite a few ways to accomplish the task. A few examples follow, separated by horizontal lines.
The simplest, and in some ways the best, option is to simply change the mouse cursor to a finger (same as the mouse cursor you see in a browser when pointing to a link) within the GtkTreeView.
Let's say GtkTreeView *view is the view we're interested in. After you have called gtk_widget_show_all(window);, you can call
GdkWindow *win = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view));
GdkDisplay *disp = gdk_window_get_display(win);
gdk_window_set_cursor(win, gdk_cursor_new_from_name(disp, "pointer"));
With this, the mouse pointer will be a finger for all rows in the view, excluding the header row.
The set of cursor names you can use (instead of "pointer" above) is listed here in the GDK3 documentation.
The downside is that if you have empty rows in the tree view, or separator rows, the mouse pointer will still look like a finger for those.
You can override the hover color by applying an application-specific CSS hover color, say
GtkTreeView:hover {
color: #ff0000;
}
with e.g.
GtkCssProvider *css_provider;
css_provider = gtk_css_provider_new();
if (gtk_css_provider_load_from_data(css_provider,
"GtkTreeView:hover {\n"
" color: #ff0000;\n"
"}\n"
"", -1, NULL))
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
GTK_STYLE_PROVIDER(css_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref(css_provider);
after creating your main window. (Of course, you can include all application-specific CSS overrides in the CSS snippet.)
However, on gtk+ 3.18.9 at least, the renderer does not seem to support text-decoration or font CSS attributes here. (The GtkCssProvider does not seem to support cursor attribute at all, in case you're wondering.)
Also, since this essentially overrides the user theme, and there are a number of possible themes a Gtk+ user might be using, changing the color this way is unlikely to look good in all themes.
If your GtkTreeView *view; consists of clickable rows, and you don't need the selection for anything (that is, you don't have a separate button or something that causes an action to be applied to all the selected rows in the GtkTreeView), then you can simply set the selection to follow the mouse cursor:
gtk_tree_view_set_hover_selection(view, TRUE);
this causes the row you hover over to be selected, and therefore also highlighted. You probably also want to limit the selection to one row at a time:
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view),
GTK_SELECTION_SINGLE);
As Herbalist already mentioned, you can create a custom GtkCellRendererText derivative, which changes the PangoUnderline underline property depending on whether the text cell being rendered is also prelit (hovered over) or not:
#include <gtk/gtk.h>
#define CUSTOM_TYPE_CELL_RENDERER_TEXT (custom_cell_renderer_text_get_type())
#define CUSTOM_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), CUSTOM_TYPE_CELL_RENDERER_TEXT, CustomCellRendererText))
#define CUSTOM_CELL_RENDERER_TEXT_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), CUSTOM_TYPE_CELL_RENDERER_TEXT, CustomCellRendererTextClass))
#define CUSTOM_IS_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), CUSTOM_TYPE_CELL_RENDERER_TEXT))
#define CUSTOM_IS_CELL_RENDERER_TEXT_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), CUSTOM_TYPE_CELL_RENDERER_TEXT))
#define CUSTOM_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), CUSTOM_TYPE_CELL_RENDERER_TEXT, CustomCellRendererTextClass))
GType custom_cell_renderer_text_get_type(void) G_GNUC_CONST;
typedef struct {
GtkCellRendererText renderer;
} CustomCellRendererText;
typedef struct {
GtkCellRendererTextClass parent_class;
} CustomCellRendererTextClass;
G_DEFINE_TYPE(CustomCellRendererText, custom_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);
void custom_cell_renderer_text_render(GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *backarea,
const GdkRectangle *cellarea,
GtkCellRendererState state)
{
if (state & GTK_CELL_RENDERER_PRELIT) {
const PangoUnderline prelit = PANGO_UNDERLINE_SINGLE;
PangoUnderline curr;
g_object_get(cell, "underline", &curr, NULL);
g_object_set(cell, "underline", prelit, NULL);
((GtkCellRendererClass *)custom_cell_renderer_text_parent_class)->render(cell, cr, widget, backarea, cellarea, state);
g_object_set(cell, "underline", curr, NULL);
} else
((GtkCellRendererClass *)custom_cell_renderer_text_parent_class)->render(cell, cr, widget, backarea, cellarea, state);
}
void custom_cell_renderer_text_class_init(CustomCellRendererTextClass *cls)
{
((GtkCellRendererClass *)cls)->render = custom_cell_renderer_text_render;
return;
}
void custom_cell_renderer_text_init(CustomCellRendererText *renderer)
{
return;
}
GtkCellRenderer *custom_cell_renderer_text_new(void)
{
return g_object_new(CUSTOM_TYPE_CELL_RENDERER_TEXT, NULL);
}
If you then replace your gtk_cell_render_text_new() with custom_cell_render_text_new() (and change GtkCellRenderText * to CustomCellRenderText *) in your GtkTreeView code, the row you hover on in the GtkTreeView will become underlined.
Note that all cells in the row you hover over will be highlighted, not just the cell/column you hover over. This is because in a GtkTreeView, the row (or index) is the unit being manipulated (rather than a specific cell on a specific column).
If you use the above renderer only for some columns in your GtkTreeView, the text in those columns will get underlined even when the mouse hovers over some other column on that row.
Also note that if you don't want all rows to indicate clickability, you can add a third column in your data model -- name "sensitive", type G_TYPE_BOOLEAN for gboolean type; value TRUE if the row is clickable, FALSE if the row is non-clickable -- and change the if clause in custom_cell_renderer_text_render() to
const GtkStateFlags flags = gtk_cell_renderer_get_state(cell, widget, state);
if ((state & GTK_CELL_RENDERER_PRELIT) &&
!(flags & GTK_STATE_FLAG_INSENSITIVE)) {
Gtk+ will render rows with TRUE normally, and rows with FALSE as separators or disabled text, because the column names in the model are automatically associated with the GtkCellRenderer properties of the same name.

You could make a custom CellRendererText and set the underline property if the PRELIT flag is present.
i only know in Python:
class My_CellRendererText( Gtk.CellRendererText ):
def __init__( self ):
super().__init__()
def do_render( self, cr, widget, background_area, cell_area, flags ):
self.props.underline = ( ( flags & Gtk.CellRendererState.PRELIT ) != 0 )
return Gtk.CellRendererText.do_render( self, cr, widget, background_area, cell_area, flags )
GObject.type_register( My_CellRendererText )

Related

Make grid of buttons fixed width & height, and wrap text

We have a GTK3 user interface (coded in C) in an embedded system that must stay fixed width/height in order to fit the screen.
Basically it's a fixed grid of buttons on a touchscreen, where the buttons can contain a text label or a graphical image - and the grid MUST stay fixed, all buttons the same width & height, and fitting the screen 100%.
However, on updating button text/labels with longer strings the button expands to fit which is not what we want at all.
I can't post all the code but I believe this is the relevant button creation code that would affect what we're trying to do;
// Number buttons 1..n
buttonId = n;
bid = buttonId - 1; // Because zero-indexing, natch
dbp = &_context->buttons[bid]; // Data about button
// Create new button, attach to global buttons
dbp->btn = gtk_button_new();
// Button name != Button CSS#ID
snprintf(value, SHORT_STR, "BTN_%02d", buttonId );
gtk_widget_set_name(dbp->btn, value);
gtk_button_set_relief(GTK_BUTTON(dbp->btn), GTK_RELIEF_NORMAL/*GTK_RELIEF_NONE*/);
// Set text label
strncpy(dbp->text, value, MAX_BUTTON_TEXT);
gtk_button_set_label (GTK_BUTTON(dbp->btn), dbp->text);
// Attempt to make labels wrap - does nothing
label = gtk_bin_get_child(GTK_BIN(dbp->btn));
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
//gtk_label_set_max_width_chars(GTK_LABEL(label), 0);
// Create new blank (&transprent) image
dbp->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, _context->innerButton.width, _conte$
gdk_pixbuf_fill(dbp->pixbuf, 0x00000000);
// Image holds pixbuf
dbp->image = (GtkImage*)gtk_image_new_from_pixbuf(dbp->pixbuf);
dbp->image_modified = 0;
// Attach image to button
gtk_button_set_image(GTK_BUTTON(dbp->btn), GTK_WIDGET(dbp->image));
// Adds an individual CSS provider per button to allow style changes
GtkStyleContext *context = gtk_widget_get_style_context(dbp->btn);
dbp->bp = gtk_css_provider_new();
// Set default colour from table of default colours
dbp->bg = get_default_colour(bid);
dbp->fg = get_label_colour(dbp->bg);
BuildButtonCSS("btnid_", buttonId, dbp->bg, dbp->fg, tstr, NULL);
// Convert CSS to provider
gtk_css_provider_load_from_data(dbp->bp, tstr, -1, NULL);
// Add provider to button
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER(dbp->bp), GTK_STYLE_PROVIDER_PRIO$
// Give the button the CSS style/class corresponding to the one we just created for it
snprintf(value, SHORT_STR, "btnid_%02d", buttonId);
gtk_style_context_add_class(context, value);
g_object_unref(dbp->bp);
// Discourage expansion (again, doesn't appear to work when label gets too long)
gtk_widget_set_hexpand(dbp->btn, FALSE);
gtk_widget_set_vexpand(dbp->btn, FALSE);
// Attach button to grid
gtk_grid_attach(GTK_GRID(_grid), dbp->btn, c, r, 1, 1);
g_signal_connect(dbp->btn, "button_press_event", G_CALLBACK(button_press_callback), GINT_TO_P$
g_signal_connect(dbp->btn, "button_release_event", G_CALLBACK(button_release_callback), GINT_$
dbp->initialised = 1;
However when we update text within a button, it renders as a single (non-wrapped) line of text and causes the button to expand and totally bu&&er up the layout.
There's two problems here;
The text should wrap, the buttons have plenty of height but the text remains a single line
The buttons should be absoltuely FIXED width & height no matter what
This feels like it should be simple to do, but I'm not very familiar with GTK and all the examples out there seem to assume that you'd want everything to flex and resize itself all the time.
Edit: Tried Alexander Dmitriev's suggestion to use ellipsize - didn't seem to work although threw no errors;
// Experiment; ellipsize
label = gtk_bin_get_child(GTK_BIN(dbp->btn));
gtk_label_set_ellipsize(GTK_LABEL(label),PANGO_ELLIPSIZE_END);
gtk_label_set_max_width_chars(GTK_LABEL(label), 14);
I also tried this exmaple by creating a GtkWidget label, applying the properties to it and then adding it to the button - but it seems gtk_button_set_label will only accept a string not a label.

How to Scroll a ScrolledWindow with arrow keys in Gtk 3.24.5?

I have a gtk entry right below the scrolled window which has the default focus , left and right keys move the cursor in the entry ,I am able to catch the key press events for up and down arrow keys but don't know how to scroll the scrolled window, referred many websites none of them were clear or explained only in parts.
Below are some of the pages I went through:
https://mail.gnome.org/archives/gtk-devel-list/2002-February/msg00104.html
https://developer.gnome.org/gtkmm-tutorial/stable/sec-keyboardevents-overview.html.en
tried using gtk_scrolled_window_set_vadjustment() couldn't get it working.
The official page says GTK_SCROLL_STEP_UP is deprecated but doesn't say what should be used instead.
Every answer would be very appreciated.Thanks
bool Method::cb_MPWindow(GtkWidget *wgt, GdkEventKey *event, MethodSelect *ms)
{
if(event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)
{
g_signal_emit_by_name(ms->ScrolledWindow, "scroll-child",(event->keyval == GDK_KEY_Up)?GTK_SCROLL_STEP_UP:GTK_SCROLL_STEP_DOWN);
//The above line works in gtk 3.14.5 but crashes the app in 3.24.5
return TRUE;
}
return FALSE;
}
In order to scroll the window with the keyboard, you need to:
Get the scrolled window's vertical or horizontal adjustment with gtk_scrolled_window_get_vadjustment() or gtk_scrolled_window_get_hadjustment().
From the adjustment object, get the following properties: value (the current scroll position), step-increment (how much to scroll by line), and page-increment (how much to scroll by page).
Then, according to the key that was pressed, you add or subtract the increment to value and then set the new value with gtk_adjustment_set_value().
The the window will scroll when change when you set the value. Typically the line increment is used when navigating with the arrow keys, while the page increment when using the Page Up/Down keys. You add them when scrolling down, and subtract while scrolling down. It is worth noting that the increments change dynamically based on the window size, so you do not need to set them manually.
Here is my code (in C). First setting up the callback:
// Create a scrolled window
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
// Get the vertical adjustment object
GtkAdjustment *page_vertical_adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolled_window));
// Connect to the key press event
g_signal_connect(
GTK_SCROLLED_WINDOW(scrolled_window),
"key-press-event",
G_CALLBACK(keyboard_scrolling),
page_vertical_adjustment
);
And then the callback function:
void keyboard_scrolling(GtkScrolledWindow *widget, GdkEventKey event, GtkAdjustment *adjustment)
{
// Get the vertical position of the page
gdouble position = gtk_adjustment_get_value(adjustment);
// Get the scrolling increments
gdouble step = gtk_adjustment_get_step_increment(adjustment); // Amount to be scrolled by the arrows (roughly a line)
gdouble page = gtk_adjustment_get_page_increment(adjustment); // Amount to be scrolled by the Page keys (roughly the visible area)
// printf("step: %f, page: %f, key: %d\n", step, page, event.keyval);
// Check which key was pressed
switch (event.keyval)
{
case GDK_KEY_Page_Down:
gtk_adjustment_set_value(adjustment, position + page);
break;
case GDK_KEY_Page_Up:
gtk_adjustment_set_value(adjustment, position - page);
break;
case GDK_KEY_Down:
gtk_adjustment_set_value(adjustment, position + step);
break;
case GDK_KEY_Up:
gtk_adjustment_set_value(adjustment, position - step);
break;
default:
break;
}
}
For convenience, here is a list of keyboard macros that GTK accepts: https://github.com/GNOME/gtk/blob/main/gdk/gdkkeysyms.h

Theme change for Button + Codenameone

is there any thing got changed for images. Circular button shows different with projections. Button use to display proper circle before. Please see the screenshots for reference. Please advise.
Button b;
Button finished = new Button("");
FontImage.setMaterialIcon(finished, FontImage.MATERIAL_CHECK, 8.0f);
Test.makeBorderRound(finished);
Style bg = finished.getDisabledStyle();
finished.setDisabledStyle(bg);
b = finished;
b.setDisabledStyle(bg);
public static void makeBorderRound(Component cmp) {
makeBorderRound(cmp.getUnselectedStyle());
makeBorderRound(cmp.getSelectedStyle());
makeBorderRound(cmp.getPressedStyle());
makeBorderRound(cmp.getDisabledStyle());
}
public static void makeBorderRound(Style stl) {
stl.setBorder(RoundBorder.create().
rectangle(true).
color(stl.getBgColor()).
opacity(stl.getBgTransparency() & 0xff));
stl.setPaddingUnit(Style.UNIT_TYPE_DIPS);
stl.setPaddingLeft(1);
stl.setPaddingRight(1);
}
That's the icon peaking out from the edges of the round border. We fixed a bug where the background wasn't painted for some cases.
A simple workaround would be:
Button finished = new Button("");
finished.getAllStyles().setBgTransparency(0);
FontImage.setMaterialIcon(finished, FontImage.MATERIAL_CHECK, 8.0f);
finished.getAllStyles().setBgTransparency(255);
Test.makeBorderRound(finished);
By setting the bg transparency to 0 the icon will have transparency, this will then get overwritten by the second line after the icons are created.

GtkCellRendererPixbuf and signals

Could anyone please give me a hint on how to attach a "double clicked" signal
to the pixbuf that is in the GtkTreeView? GtkCellRendererPixbuf
doesn't have any signals?
I managed to set the GTK_CELL_RENDERER_MODE_ACTIVATABLE switch to the
renderer, but I don't know how to work.
I checked the header file and in fact there is the "activate" method; could you please
demonstrate how to use it?
renderer = gtk_cell_renderer_pixbuf_new();
g_object_set(renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
column = gtk_tree_view_column_new_with_attributes(NULL,
renderer,
"pixbuf",
0,
NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
OK I try this:
Tree view's 'row-activated' will send the path and column as arguments
to the callback. With 'cursor-changed' just need to call
gtk_gtk_treeview_get_cursor to find out the path and column. With
gtk Widget's 'button-press-event' I get the event as an argument for
the callback and just need to call gtk_treeview_get_path_at_pos with
event x and event y to get the path and column.
A cell renderer is only supposed to draw the contents of the data model over a portion of the widget. Interaction with the user is in most cases realized using the widget itself.
In other words, simply connect to the button-press-event of the tree view and handle the case when the type is GDK_2BUTTON_PRESS. You can get the row/column under the mouse using gtk_tree_view_get_path_at_pos, as you do in your other question.
Check this:
void on_treeview_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model( treeview );
if ( gtk_tree_model_get_iter(model, &iter, path) )
{
gtk_tree_model_get(model, &iter,
ITEM, &dhd_contaItem2,
CODIGO, &dhd_G_CodProduto2 ,
DESCRICAO, &dhd_G_NomeProduto2 ,
QTD, &dhd_quantidade2,
VALOR, &dhd_valorItem2,
-1);
g_print( "Current row: %s %s %s %s %s\n", dhd_contaItem2, dhd_G_CodProduto2, dhd_G_NomeProduto2, dhd_quantidade2, dhd_valorItem2 );
}
}
I use that in one of my codes to print in terminal the selected row from a TreeView (with ListStore) when double clicked or when you press enter on it. On the gtk_tree_model_get notice that I'm using my own columns and variables, as i do in g_print. And I attach this function with row-activated signal on the TreeView. I don't know if is that what you want exactly but I hope it helps you out. Sorry for my bad english.

How to select text in GtkEntry

i have dialog window with GtkEntry. I want to select all text in entry right after dialog window becomes visible to user. I tried this, but its not working, i see no selection:
static void OnEntryShow(GtkWidget *entry, gpointer user_data)
{
gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
}
...
gtk_entry_set_text(GTK_ENTRY(myEntry), "text");
g_signal_connect(myEntry, "show", G_CALLBACK(OnEntryShow), NULL);
if (gtk_dialog_run(GTK_DIALOG(myDialog)) == GTK_RESPONSE_OK)
...
How can i select text in GtkEntry after GtkDialog becomes visible?
Perhaps you want the GtkEntry to grab focus?
Try this:
gtk_widget_grab_focus (entry);
where entry is in this case the pointer to your GtkEntry widget.
The documentation of the function can be found here.
You should use the function documented here.
text_entry.select_region(0,2) will select the first two characters, while (0, -1) will select the entire text.
Here's a solution I've used for gtkmm using the get_iter_at_offset and select_range functions.
Gtk::TextIter match_start = m_textBuffer->get_iter_at_offset(0);
Gtk::TextIter match_end = m_textBuffer->get_iter_at_offset(-1); // -1 to select all
m_textBuffer->select_range(match_start,match_end);

Resources