Some other lines of C# code

This time I was looking for a way to get the rectangle of the widget and I found an old article with this C code:

[cc lang=”C”]
static void
widget_get_rect_in_screen (GtkWidget *widget, GdkRectangle *r)
{
gint x,y,w,h;
GdkRectangle extents;
GdkWindow *window;

window = gtk_widget_get_parent_window(widget); /* getting parent window */
gdk_window_get_root_origin(window, &x,&y); /* parent’s left-top screen coordinates */
gdk_drawable_get_size(window, &w,&h); /* parent’s width and height */
gdk_window_get_frame_extents(window, &extents); /* parent’s extents (including decorations) */
r->x = x + (extents.width-w)/2 + widget->allocation.x; /* calculating x (assuming: left border size == right border size) */
r->y = y + (extents.height-h)-(extents.width-w)/2 + widget->allocation.y; /* calculating y (assuming: left border size == right border size == bottom border size) */
r->width = widget->allocation.width;
r->height = widget->allocation.height;
}
[/cc]

I translated it to C#

[cc lang=”C#”]
private Gdk.Rectangle getRectInScreen(Gtk.Widget widget)
{
int x, y, w, h;
Gdk.Rectangle extents = new Gdk.Rectangle();
Gdk.Window window;
Gdk.Rectangle r;

window = widget.ParentWindow;
window.GetRootOrigin(out x, out y);
window.GetSize(out w, out h);
extents = window.FrameExtents;
r.X = x + (extents.Width – w) / 2 + widget.Allocation.X;
r.Y = y + (extents.Height – h) – (extents.Width – w) / 2 + widget.Allocation.Y;
r.Width = widget.Allocation.Width;
r.Height = widget.Allocation.Height;

return r;
}
[/cc]

gtksharp popup menu with title

I’m studying monodevelop code more and more, and when I need to write a custom widget I get inspiration from what the good monodevelop developer worte down. This time the problem I need to solve was to have a popup menu with a title. So I ended up with the following solution.
[cc lang=”C#”]
//
// PopupMenu.cs
//
// Author:
// Stefano Canepa
//
// Copyright (c) 2011, 2011 Penta Engineering s.r.l.
//
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Penta Engineering s.r.l. nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY Penta Engineering s.r.l. “AS IS” AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL Penta Engineering s.r.l. BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using Gtk;
using Gdk;

namespace GUI.Components
{
public class PopupMenuEntry : Gtk.EventBox
{
private string menuEntryLabelString;
private Gtk.Label menuEntryLabel;

public string MenuEntryLabelString
{
get {
return this.menuEntryLabelString;
}
}

public string Markup
{
set
{
menuEntryLabel.Markup = value;
}
}

public PopupMenuEntry(string label) : base()
{
menuEntryLabel = new Label();
menuEntryLabelString = label;

BorderWidth = 2;
menuEntryLabel.SetPadding(10,1);
menuEntryLabel.UseMarkup = true;
menuEntryLabel.Markup = “” + menuEntryLabelString + ““;
menuEntryLabel.Justify = Justification.Left;

ModifyBg(Gtk.StateType.Normal, new Gdk.Color(242, 242, 242));
ModifyFg(Gtk.StateType.Normal, new Gdk.Color(0, 0, 0));
// Add(menuEntryLabel);
Gtk.HBox h = new Gtk.HBox();
h.PackStart(menuEntryLabel, false, false, 0);
Add(h);

}
}

public class PopupMenu : Gtk.Window
{
private static bool poppedUp;
private static PopupMenu prevPopup;
bool nudgeVertical = false;
bool nudgeHorizontal = false;

Gtk.VBox menuBox = new Gtk.VBox();

public void Add(PopupMenuEntry menuEntryEvBox)
{
menuBox.PackStart(menuEntryEvBox, false, false, 0);

menuEntryEvBox.ButtonPressEvent += delegate(object o, ButtonPressEventArgs args) {
this.Destroy();
};
menuEntryEvBox.EnterNotifyEvent += delegate {
menuEntryEvBox.ModifyBg(Gtk.StateType.Normal, new Gdk.Color(108, 149, 201));
menuEntryEvBox.ModifyFg(Gtk.StateType.Normal, new Gdk.Color(255, 255, 255));
menuEntryEvBox.Markup = “” + menuEntryEvBox.MenuEntryLabelString + ““;

};
menuEntryEvBox.LeaveNotifyEvent += delegate {
menuEntryEvBox.ModifyBg(Gtk.StateType.Normal, new Gdk.Color(242, 242, 242));
menuEntryEvBox.ModifyFg(Gtk.StateType.Normal, new Gdk.Color(0, 0, 0));
menuEntryEvBox.Markup = “” + menuEntryEvBox.MenuEntryLabelString + ““;

};
}

public void Popup()
{
if(poppedUp == false)
{
this.ShowAll();
prevPopup = this;
poppedUp = true;
}
else
{
prevPopup.Destroy();
poppedUp = false;
}
}

public PopupMenu(string title) : this()
{
Gtk.EventBox titleEvBox= new EventBox();
titleEvBox.ModifyBg(Gtk.StateType.Normal, new Gdk.Color(192, 192, 192));
Gtk.Label titleLabel = new Label(title);
titleLabel.SetPadding(0, 2);
titleEvBox.Add(titleLabel);

menuBox.Add(titleEvBox);
}

public PopupMenu() : base(Gtk.WindowType.Popup)
{

this.SkipPagerHint = true;
this.SkipTaskbarHint = true;
this.Decorated = false;
this.BorderWidth = 1;
this.AllowShrink = false;
this.AllowGrow = false;

//fake widget name for stupid theme engines
this.Name = “gtk-tooltip”;
this.Title = “tooltip”;

Add(menuBox);
ModifyBg(Gtk.StateType.Normal, new Gdk.Color(242, 242, 242));

int x, y;
Gdk.Screen.Default.Display.GetPointer(out x, out y);
Move(x, y);
}

public bool NudgeVertical
{
get { return nudgeVertical; }
set { nudgeVertical = value; }
}

public bool NudgeHorizontal
{
get { return nudgeHorizontal; }
set { nudgeHorizontal = value; }
}

protected override bool OnExposeEvent(Gdk.EventExpose evnt)
{
int winWidth, winHeight;
this.GetSize(out winWidth, out winHeight);
Gtk.Style.PaintFlatBox(Style, this.GdkWindow, StateType.Normal,
ShadowType.Out, evnt.Area, this,
“tooltip”, 0, 0, winWidth, winHeight);
foreach(var child in this.Children)
this.PropagateExpose(child, evnt);
return false;
}

protected override void OnSizeAllocated(Gdk.Rectangle allocation)
{
if(nudgeHorizontal || nudgeVertical)
{
int x, y;
this.GetPosition(out x, out y);
int oldY = y, oldX = x;
const int edgeGap = 2;

Gdk.Rectangle geometry = Screen.GetMonitorGeometry(Screen.GetMonitorAtPoint(x, y));
if(nudgeHorizontal)
{
if(allocation.Width < = geometry.Width && x + allocation.Width >= geometry.Width – edgeGap)
x = geometry.Left + (geometry.Width – allocation.Height – edgeGap);
if(x < = geometry.Left)
x = geometry.Left;
}

if(nudgeVertical)
{
if(allocation.Height <= geometry.Height && y + allocation.Height >= geometry.Height – edgeGap)
y = geometry.Top + (geometry.Height – allocation.Height – edgeGap);
if(y < = geometry.Top)
y = geometry.Top;
}

if(y != oldY || x != oldX)
Move(x, y);
}

base.OnSizeAllocated(allocation);
}

}
}

[/cc]

This widget can be used as a popup menu. It needs to be improved but I publish to give others a starting point. The more important thinks to improve/implement are:
1) selection highlighting needs to follow selected theme
2) title should be setted out of the constructor, too
3) separators need to be added

If you use it please provide a little commend and if you improve it please public and link you new code.

m4s0n501