I'm into a problem with a GTK+ C application. I have a container that, when starting the application, contains a button. During the running an user interation must cause this widget to contain more of them.
I need to write a function that removes all the "old" inner buttons, then adds all the ones from a list and finally refresh the view. This is what I'm writing but some parts are missing (TODOs)
void refresh_sequence_panel()
{
GSList* iterator = NULL;
GtkWidget* button;
// TODO: Here the container must be empty
// Now add all the buttons
for (iterator = steps; iterator; iterator = iterator->next) {
button = gtk_button_new_from_stock(GTK_STOCK_ADD);
gtk_widget_set_size_request(button, SEQ_BUTTON_W, SEQ_BUTTON_H);
gtk_box_pack_start(GTK_BOX(sequence_panel), button, FALSE, FALSE, 5);
handler_id = g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(seq_popup), GTK_BOX(sequence_panel));
}
// TODO: Now refresh the view, so I can see the changes...
}
Hope that someone can help, thanks!
Removing all children:
GList *children, *iter;
children = gtk_container_get_children(GTK_CONTAINER(container));
for(iter = children; iter != NULL; iter = g_list_next(iter))
gtk_widget_destroy(GTK_WIDGET(iter->data));
g_list_free(children);
Note that the above just deletes each child widget directly, rather than asking the container to remove it (with gtk_container_remove()), this is recommended by the documentation and matches what you intend, so it's fine in my opinion.
There's no point in "refreshing the view", as long as you actually add and show the newly built widgets to the container. GTK+ is event-based, and adding children to a container makes the container realize it needs to refresh its visual appearance automatically.
One-liner:
gtk_container_foreach (GTK_CONTAINER (container), (void*) gtk_widget_destroy, NULL);
Here is the method that I followed. Because I am using gtkmm on c++
Gtk::Box_Helpers::BoxList *childList = &vboxImgLst->children();
Box_Helpers::BoxList::iterator start = childList->begin();
Box_Helpers::BoxList::iterator end = childList->end();
childList->erase(start, end);
where vboxImgLst is,
VBox *vboxImgLst;
Hope this will help to someone who are using gtkmm and c++.
Thanks
This one worked for me (it's a variation of unwind's answer):
Glib::ListHandle<Widget*> childList = this->get_children();
Glib::ListHandle<Widget*>::iterator it = childList.begin();
while (it != childList.end()) {
remove(*(*it));
it++;
}
(GTKMM 2.4)
Related
This question is related to this: Keep Codename One components in invalid positions after app stop/resume
In the linked question, the solution proposed is to use a fake layout. I tried that, but it produces side effects when I try to restore the original layouts.
I tried a completely different approach, that works fine on Android. My question is why the following code works well in Android only (it doesn't work on iPhone, that seems to ignore that code) and if there are small changes that make that code working also on iPhone.
The code:
private Map<Component, Dimension> layeredPaneCmps = new HashMap<>();
public void start() {
if (current != null) {
current.show();
layeredPaneRestore(); // it works on Android, but not on iOS
return;
}
[...]
}
public void stop() {
current = getCurrentForm();
if (current instanceof Dialog) {
((Dialog) current).dispose();
current = getCurrentForm();
}
layeredPaneSave(null);
}
/**
* Save the position of all layered pane components in a recursive way: just
* invoke with null as cnt.
*
* #param cnt
*/
private void layeredPaneSave(Container cnt) {
if (cnt == null) {
layeredPaneCmps.clear();
cnt = Display.getInstance().getCurrent().getLayeredPane(this.getClass(), true);
}
for (int i = 0; i < cnt.getComponentCount(); i++) {
layeredPaneCmps.put(cnt.getComponentAt(i), new Dimension(cnt.getComponentAt(i).getX(), cnt.getComponentAt(i).getY()));
if (cnt.getComponentAt(i) instanceof Container) {
layeredPaneSave((Container) cnt.getComponentAt(i));
}
}
}
/**
* Restores all layered pane components in their position and repaints them.
*/
private void layeredPaneRestore() {
Container layeredPane = Display.getInstance().getCurrent().getLayeredPane(this.getClass(), true);
for (Component cmp : layeredPaneCmps.keySet()) {
cmp.setX(layeredPaneCmps.get(cmp).getWidth());
cmp.setY(layeredPaneCmps.get(cmp).getHeight());
cmp.repaint();
}
layeredPane.repaint();
}
Android and iOS have very different suspend/resume behaviors where iOS tries to minimize repaints and back-grounding while Android constantly suspends/resumes. I would suggest logging in the stop()/start() method to make sure they aren't invoked multiple times.
Notice that you shouldn't invoke repaint() it would be invoked for you. Since a repaint() might trigger a layout this could be a problem. Also the repaint() of the parent component loops into painting the components so layeredPane would be enough and doesn't require also cmp.repaint();.
I have a statusbar, and I want to have its text being selectable (e.g. copy and pastable), on Linux/Debian, with GTK3 (3.21.5 on Debian/Sid). The label of the statusbar is the label field of its private data, and there is no direct API to access it.
I was able to code (in C99) the following, which is working:
mom_cmdstatusbar = // some global variable
gtk_statusbar_new ();
{
GtkWidget *statmsgarea =
gtk_statusbar_get_message_area (GTK_STATUSBAR (mom_cmdstatusbar));
MOM_ASSERTPRINTF (GTK_IS_CONTAINER (statmsgarea), "bad statmsgarea#%p",
statmsgarea);
GList *lischs = gtk_container_get_children (GTK_CONTAINER (statmsgarea));
for (GList * l = lischs; l != NULL; l = l->next)
{
GtkWidget *chwidg = l->data;
MOM_ASSERTPRINTF (GTK_IS_WIDGET (chwidg), "bad chwidg#%p", chwidg);
if (GTK_IS_LABEL (chwidg))
gtk_label_set_selectable (GTK_LABEL(chwidg), true);
}
g_list_free (lischs), lischs = NULL;
}
This does work, but is there a better or simpler way to do that? Getting the list of children inside the message area of the GtkStatusbar and testing each of them smells bad. It looks like some GtkWidget* gtk_statusbar_get_label (GtkStatusbar*); function is missing in in the GTK3 API.
PS. FWIW, the code is GPLv3+, on github in file gui.c on commit 58feb1d9473c34aca.. of the expjs branch. More details & motivation about that software project on this & that questions.
If there's no direct API to access the internal child, then that is probably on purpose, so that the GTK developers can keep their options open to reorganize the internal layout of the widget in future versions.
In my application, i am generating multiple buttons at run time and add it to grid as following
for (int i = 0; i < ListOfMainCategories.Count; i++)
{
clsMainCategory tempCat = (clsMainCategory)ListOfMainCategories[i];
ButtonMainMenuCat btn = CreateMainButton(tempCat.CatTitle, i);
btn.Margin = new Thickness(0, 1, 0, 1);
btn.TabIndex = TabIndexNo;
if (i == 0)
{
buttonHomeMenu = btn;
}
btn.AddHandler(ButtonMainMenuSubSubCat.GotKeyboardFocusEvent, new RoutedEventHandler(ButtonMainMenuGotFocus), handledEventsToo: false);
// stackTableViewMainMenu.Children.Add(btn);
Grid.SetRow(btn, 1);
Grid.SetColumn(btn, i + 1);
gridHeader.Children.Add(btn);
}
After on particular event I removed all of these buttons from Grid.
gridHeader.Children.RemoveRange(0, gridHeader.Children.Count);
Here i Think i also need to Remove or dispose or unload these button from Memory. So How can i do this task ? Please suggest
Yuo can't dispose of the button in the classical sense as there is nothing to dispose. You are dealing with managed code here. The memory allocation will be cleared up by the garbage collector as long as there are no references. In the code above you are keeping a reference to the button through the routed event handler so call btn.RemoveHandler before you remove it from the Grid.
You could take a look at MSDN guidance on the WeakEvent pattern here: http://msdn.microsoft.com/en-us/library/aa970850
Hopefully this is an easy question to answer! I am trying to use GtkEntryCompletion (a la the example here) but while this code works I can't seem to get the GtkEntry to present the autocomplete results when I set the text of the field programatically. What I am trying to accomplish is a semi pre-filled text entry that is already presenting the user with some autocomplete options.
To set the text I have tried using the functions gtk_entry_set_text(...), gtk_entry_buffer_insert_text(...) and even gtk_entry_buffer_emit_inserted_text(...) but to no avail. Is there a way to do this in such a way as to act like regular user input and display the suggestions?
I think you need to call gtk_entry_completion_complete after setting the text.
EDIT
Sorry #Tylter, but wow, this is way more difficult than I imagined. The only way I can figure out how to do it is to actually send the keypress event to the window.
gtk_widget_grab_focus(entry);
GdkEvent new_event;
new_event.key.type = GDK_KEY_PRESS;
new_event.key.window = gtk_widget_get_parent_window(entry);
new_event.key.send_event = TRUE;
new_event.key.time = GDK_CURRENT_TIME;
new_event.key.keyval = 0x053; // capital S
new_event.key.state = GDK_KEY_PRESS_MASK;
new_event.key.length = 0;
new_event.key.string = 0;
new_event.key.hardware_keycode = 0;
new_event.key.group = 0;
gdk_event_put((gpointer)&new_event);
EDIT 2
Are you using a GtkDialog for your pop-up? I coded this up really quick and it seems to work. Here you would be creating the dialog in a button click event:
static void click_event( GtkWidget *widget,
gpointer data )
{
GtkWidget* window = gtk_dialog_new ();
completion = create_completion();
entry = gtk_entry_new();
gtk_entry_set_completion(GTK_ENTRY(entry), completion);
// add entry to dialog
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area),
entry, TRUE, TRUE, 0);
gtk_widget_show(entry);
gtk_widget_show(window); // you must show the window before sending the keypress event
gtk_widget_grab_focus(entry);
GdkEvent new_event;
new_event.key.type = GDK_KEY_PRESS;
new_event.key.window = gtk_widget_get_parent_window(entry);
new_event.key.send_event = TRUE;
new_event.key.time = GDK_CURRENT_TIME;
new_event.key.keyval = 0x053; // capital S
new_event.key.state = GDK_KEY_PRESS_MASK;
new_event.key.length = 0;
new_event.key.string = 0;
new_event.key.hardware_keycode = 0;
new_event.key.group = 0;
gdk_event_put((gpointer)&new_event);
}
The only gotcha I saw with this is that your must show the dialog window before sending the keypress event.
I have a silverlight 3 app with a textbox on the main window and a childwindow that has a list of all the potential textbox values. When I open that childwindow I want it to scroll to the correct one in the list.
I'm trying to do this with the code below...using the ScrollIntoView. It was not working at all until I add the UpdateLayerout(). However it does not seem to work all the time. At times it scrolls but not all the way to the item, it is a few items higher than it should be. The listbox is in an Accordion and the list items use a ItemTemplate\DataTemplate, not sure if that effects anything but thought I'd mention it.
Any ideas what I'm missing in the code below?
What I would like is to scroll the item to the top of the list ....any ideas how to that?
(Or any other suggestions on how to code this better)
Thanks!
for (int index = 0; index < myList.Items.Count; index++) {
object obj = myList.Items[index];
var listItem= obj as listItemObject;
if (listItemObj != null) {
if (string.Compare(listItemObj.id, _PastedInId, StringComparison.InvariantCultureIgnoreCase) == 0) {
selectThisIndex = index;
scrollToThisItem = obj;
}
}
}
myList.SelectedIndex = selectThisIndex;
if (scrollToThisItem != null){
myList.UpdateLayout();
myList.ScrollIntoView(scrollToThisItem);
}
Consider using the ItemsControlExtensions implementation from the Silverlight Toolkit, available at http://silverlight.codeplex.com/sourcecontrol/network/Show?projectName=Silverlight&changeSetId=47051#637494
This has a ScrollIntoView(FrameworkElement element) method that may help.