I have a GtkMenu Widget and i am adding it to screen on button click,,
but it gets added at mouse location but i want to add it to end edge of button widget like,
+-------+
|BUTTON |
+-------+
+------------+
|Menu Item 1 |
|Menu Item 2 |
+------------+
I am using following code to add popup menu
// Add popup menu.
gtk_menu_popup( GTK_MENU (widget), NULL, NULL, NULL, NULL,
bevent->button, bevent->time);
Ok added this function but popup menu gets added to end of window not at the end of button widget...
void set_position (GtkMenu *menu, gint *px, gint *py, gboolean *push_in, gpointer data)
{
gint w, h;
GtkBuilder *builder = GetBuilderPointer();
GtkWidget *button = GTK_WIDGET( gtk_builder_get_object( builder, "button_presence"));
gdk_window_get_size (button->window, &w, &h);
gdk_window_get_origin (button->window, px, py);
*py = h;
printf("\n\n w[%d] h[%d] px[%d] py[%d]\n\n", w, h, *px, *py );
*push_in = TRUE;
}
Printf gives output like follows,
w[350] h[400] px[341] py[607]
i am not able to retrieve x, y, height, width of button widget...
Note: This button is a custom widget with (GtkHBox+(GtkImage+GtkLabel)) in it.
thanks unwind for your answer.
The fourth argument to gtk_menu_popup() is a pointer to a GtkMenuPositionFunc, which is a callback that you can define. You need to add such a callback, and have it return the desired position, by reading it out of the button widget's GdkWindow.
It's been a while since I did this, but you might also have to read out the parent window's position on-screen, and add the widget's position to them to get the absolute coordinates where you want the menu to pop up.
Related
I want to implement custom widgets by subclassing DrawingArea Widget, for this I need to draw using cairo. It seems like in gtk3 there is a new signal called 'draw' introduced. How do I draw inside the widget? Should the map and realize signals also be overrided?
A simple example code would be very helpful. Thanks.
To put it simply, you'll need to override the draw signal which will supply a Cairo context:
gboolean
user_function (GtkWidget *widget,
CairoContext *cr,
gpointer user_data)
Then you can use the CairoContext crto draw the actual contents of the widget.
From the C API:
The GtkDrawingArea widget is used for creating custom user interface
elements. It’s essentially a blank widget; you can draw on it. After
creating a drawing area, the application may want to connect to:
Mouse and button press signals to respond to input from the user. (Use
gtk_widget_add_events() to enable events you wish to receive.)
The “realize” signal to take any necessary actions when the widget is instantiated on a particular display. (Create GDK resources in
response to this signal.)
The “size-allocate” signal to take any necessary actions when the widget changes size.
The “draw” signal to handle redrawing the contents of the widget.
The widget should queue some draws when the widget changes, for example, on size allocate you should use gtk_widget_queue_draw to force the widget to draw itsef again.
Example - Using a drawing area not as sub classing it but the concept remains:
(taken from Gnome C API)
gboolean
draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
{
guint width, height;
GdkRGBA color;
GtkStyleContext *context;
context = gtk_widget_get_style_context (widget);
width = gtk_widget_get_allocated_width (widget);
height = gtk_widget_get_allocated_height (widget);
gtk_render_background (context, cr, 0, 0, width, height);
cairo_arc (cr,
width / 2.0, height / 2.0,
MIN (width, height) / 2.0,
0, 2 * G_PI);
gtk_style_context_get_color (context,
gtk_style_context_get_state (context),
&color);
gdk_cairo_set_source_rgba (cr, &color);
cairo_fill (cr);
return FALSE;
}
[...]
GtkWidget *drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request (drawing_area, 100, 100);
g_signal_connect (G_OBJECT (drawing_area), "draw",
G_CALLBACK (draw_callback), NULL);
You should also read about Height-for-width Geometry Management in GtkWidget
I've used C because there was no reference to programming language on your question and at the same time it's the original API from which all other are written.
There are some examples about creating Gtk+ custom Widgets on the internet.
I have created my own custom widget and I want to support internal drag and drop for the widgets.
I have added 4 of my custom widgets in a vertical box layout. Now i want to drag and drop the custom widgets internally. To be more clear, If i drag the last widget and drop it in the first position, then the first widget has to move to the second positon and the last widget (which is dragged) has to move to first position. (same like drag and drop of the items in the List view). Can anyone suggest me a way to drag and drop of the custom widgets.
You need to reimplement mousePressEvent, mouseMoveEvent and mouseReleaseEvent methods of a widget you want to drag or install an event filter on them.
Store the cursor position in mousePressEvent and move the widget in mousePressEvent to the distance the cursor moved from the press point. Don't forget to clear the cursor position in the mouseReleaseEvent. The exact code depends of how you want the widget to look when is being dragged and how other widgets should behave when drag/drop the widget. In the simplest case it will look like this:
void mousePressEvent(QMouseEvent* event)
{
m_nMouseClick_X_Coordinate = event->globalX();
m_nMouseClick_Y_Coordinate = event->globalY();
};
void mouseMoveEvent(QMouseEvent* event)
{
if (m_nMouseClick_X_Coordinate < 0)
return;
const int distanceX = event->globalX() - m_nMouseClick_X_Coordinate;
const int distanceY = event->globalY() - m_nMouseClick_Y_Coordinate;
move(x() + distanceX, y() + distanceY());
};
void mouseReleaseEvent(QMouseEvent* event)
{
m_nMouseClick_X_Coordinate = -1;
}
My purpose is to draw in the CENTER of the composite. Actually, I have an rcp view and I'm drawing some shapes inside it. this is the code that I use :
display = parent.getDisplay();
white= display.getSystemColor(SWT.COLOR_WHITE);
parent.setLayout(new FillLayout(SWT.VERTICAL));
// Create the ScrolledComposite to scroll horizontally and vertically
final ScrolledComposite sc =new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
sc.setExpandHorizontal(true);
sc.setExpandVertical(true);
sc.setMinHeight(100);
sc.setMinWidth(100);
sc.setSize(100,100);
Composite child = new Composite(sc,SWT.NONE);
child.setLayout(new FillLayout());
child.layout(true);
parent.addListener (SWT.Resize, new Listener () {
public void handleEvent (Event e) {
x = child.getBounds().width/2;
y = child.getBounds().height/2;
child.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
dessin(gc); // to raw the circle
}
});
sc.getDisplay().update();
}
});
I defined the view with a ratio (so when the view is empty I get the wanted size)...I don't know the exact size of the view since it can be resized by the user at anymoment, or when an editor is opened... So, my problem is how to draw just in the center of the view and keep the drawings in the center even if the view is resized...
PS: Using (Point.x and point.y), I get (0,0) when the view appears first, then I get other values...
Pleaaaaaaaaaaaaaase help
You can use getOrigin() method on ScrolledComposite, which will return Point instance with the point in the content that currently appears in the top left corner of the scrolled composite. See docs getOrigin method on ScrolledComposite.
With that information and size of the component which you'll get from getBounds() method you can easily calculate the 'real' center.
I have a form interface with a large number of horizontal drop down and text boxes that are inside a GTKscrolledWindow. The form has to be aligned horizontally because there are an unlimited number of forms in the window (think tabel of forms).
Is it (possible/how do I) auto-scroll the GTKScrolledWindow to the right when the operator tabs to the next form control which is off to the right of the screen.?
Related question would be how do I detect if the focused element (button etc) is visible in it's parent scrolledwindow.
Thanks.
I hope you don't mind I am using GTK+ C API in the description, the sollution could be converted to the PyGTK easily and the principles remains the same.
Starting with the second question - if you know which widget to test, you can detect its visibility by calling gtk_widget_translate_coordinates(child, parent, 0, 0, &x, &y) to get the position of the child relative to the parent. By gtk_widget_get_allocation() you get the size of parent and child and you simply test if whole child rectangle is in the scrolled window.
gboolean is_visible_in (GtkWidget *child, GtkWidget *scrolled)
{
gint x, y;
GtkAllocation child_alloc, scroll_alloc;
gtk_widget_translate_coordinates (child, scrolled, 0, 0, &x, &y);
gtk_widget_get_allocation(child, &child_alloc);
gtk_widget_get_allocation(scrolled, &scroll_alloc);
return (x >= 0 && y >= 0)
&& x + child_alloc.width <= scroll_alloc.width
&& y + child_alloc.height <= scroll_alloc.height;
}
You can obtain the curently focused widget in window by gtk_window_get_focus () or you can detect it when focus is changed.
In the autoscroll problem you can handle "focus" signal connected to the widget which can be focussed or the "set-focus-child" event connected to the container containing the widgets. In the signal handler you should check, if the focused widget is visible. If not, determinate its position and scroll properly.
To do so you have to detect the position of the widget inside the whole scrolled area. If you are using some container which does not support scrolling (such GtkHBox) iside GtkScrolledWindow (adapted by viewport), you can get the coordinates of the focused widget relative to the container by gtk_widget_translate_coordinates() again - now using the container instead of scrolled window. The value of the adjustment, if using GtkViewport, the adjustment value correspond to the position in pixels in the scrolled area, so setting adjustment value to x relative coordinate will do scrolling. So the important part of the handler could be
GtkWidget *scrolled = /* The scrolled window */
GtkWidget *container = /* The container in the scrolled window */
GtkWidget *focused = /* The focused widget */
GtkAdjustment *hadj = gtk_scrolled_window_get_hadjustment(
GTK_SCROLLED_WINDOW(scrolled));
gint x, y;
gtk_widget_translate_coordinates (focused, container, 0, 0, &x, &y);
gtk_adjustment_set_value(hadj, min(x, maximal adjustment value allowed);
The maximal adjustment value allowed is adjustment.upper - adjustment.page_size. The focused widget is passed as signal handler argument for both signals, in the case of "set-focus-child" signal you get also the container as argument.
Here's what I did, based on Michy's answer.
To make a scrolled window auto-scroll, run in the constructor:
FocusScroll(scrolledwindow).
class FocusScroll(object):
"""
Get a gtk.ScrolledWindow which contains a gtk.Viewport.
Attach event handlers which will scroll it to show the focused widget.
"""
def __init__(self, scrolledwindow):
self.scrolledwindow = scrolledwindow
self.viewport = scrolledwindow.get_child()
assert isinstance(self.viewport, gtk.Viewport)
self.main_widget = self.viewport.get_child()
self.vadj = scrolledwindow.get_vadjustment()
self.window = self.get_window(scrolledwindow)
self.viewport.connect('set-focus-child', self.on_viewport_set_focus_child)
def get_window(self, widget):
if isinstance(widget, gtk.Window):
return widget
else:
return self.get_window(widget.get_parent())
def is_child(self, widget, container):
"""
Go recursively over all children of container, to check if widget is
a child of it.
"""
for child in container.get_children():
if child is widget:
return True
elif isinstance(child, gtk.Container):
if self.is_child(widget, child):
return True
else:
return False
def on_viewport_set_focus_child(self, _viewport, _child):
idle_add(self.scroll_slide_viewport)
def scroll_slide_viewport(self):
"""Scroll the viewport if needed to see the current focused widget"""
widget = self.window.get_focus()
if not self.is_child(widget, self.main_widget):
return
_wleft, wtop = widget.translate_coordinates(self.main_widget, 0, 0)
wbottom = wtop + widget.get_allocation().height
top = self.vadj.value
bottom = top + self.vadj.page_size
if wtop < top:
self.vadj.value = wtop
elif wbottom > bottom:
self.vadj.value = wbottom - self.vadj.page_size
First, the first.
How do you detect the focus ? You connect to GtkWidget::focus signal.
And in that callback you can scroll the window to whereever you like, how to do that, you need to get the GtkAdjustment of GtkScrolledWindow and move it accordinly to show what you want.
I have added GtkMenu using following code:
// Add popup menu.
gtk_menu_popup( GTK_MENU (widget), NULL, NULL, set_position, NULL,
bevent->button, bevent->time);
And to adjust this GtkMenu under my button i have used this function:
void set_position (GtkMenu *menu, gint *px, gint *py, gboolean *push_in, gpointer data)
{
gint w, h;
GtkBuilder *builder = GetBuilderPointer();
GtkWidget *button = GTK_WIDGET( gtk_builder_get_object( builder, "button_presence"));
gdk_window_get_size (button->window, &w, &h);
gdk_window_get_origin (button->window, px, py);
*py = h;
printf("\n\n w[%d] h[%d] px[%d] py[%d]\n\n", w, h, *px, *py );
*push_in = TRUE;
}
but popup is getting displayed at the end of the whole window not at the end of the button...
Out put of the printf:
w[350] h[400] px[341] py[607]
what is going wrong why it is not giving x, y and height, width of button correctly?
Note: The button widget used in this, is a custom composite widget with (GtkHBox+(GtkImage+GtkLabel)) in it.
I have tried same code with normal label button but still GtkMenu is getting displayed
under root window not under button.
Why this might be happening... i am relay stuck on this...
thnaks,
PP
Ok Let me answer this.... if anyone is facing the same problem he/she can refer this..
To Adjust GtkMenu under GtkButton (Custom Composite Button).. use following set position callback function...
static void
pos_func( GtkMenu *menu,
gint *x,
gint *y,
gboolean *push,
GtkWidget *widget )
{
GtkRequisition req;
gtk_widget_size_request( widget, &req );
gdk_window_get_origin( gtk_widget_get_window( widget ), x, y );
*x += widget->allocation.x;
*y += widget->allocation.y + req.height;
*push = TRUE;
}
This will set position of GtkMenu under GtkButton Widget!