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.

Lascia un Commento

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>