This page is likely outdated (last edited on 10 Jul 2009). Visit the new documentation for updated content.
CreatingCustomThemedGtkSharpWidgets
This document will try to highlight was is required to implement a custom Gtk# widget that integrates properly with a Theme Engine.
Gtk# based applications can be themed based on the user preferences. The themes control how applications look on the screen. Although the core widgets in Gtk# already follow the guidelines developers that are implementing their own custom widgets need to pay special attention to make sure that their custom widgets render properly across different engines and looks.
For example, something that works like a button, should look like a button.
Writing the Widget
Here are some steps:
- Create one single instance of the widget you want to emulate
- Always load style on Expose
- Always resolve respective colors from that style
Step 1
As simple as this…
public static class Fakes
{
private static Button button = null;
/// <value>
/// Provides button template
/// </value>
public static Button Button {
get {
if (button == null)
button = new Button();
return (button);
}
}
private static Entry entry = null;
/// <value>
/// Provides entry template
/// </value>
public static Entry Entry {
get {
if (entry == null)
entry = new Entry();
return (entry);
}
}
}
With this you get two fake widgets. One for button and one for entry. Initialization should not be done in private part so you delay its initialization to as late as possible. First reason are resources used by application, but second is more important. You need to make sure to delay any widget initialization until Gtk.Application.Init() has been called. And since the only time these will be touched are during expose event of another widget, initialization already happened.
Step 2 and 3
Faking widget to look like normal Gtk+ entry widget. Using expose like that will survive any on the fly theme change and so on.
Example: Faking entry widget
protected override bool OnExposeEvent (Gdk.EventExpose evnt)
{
// Take into consideration that expose event doesn't redraw whole widget
// it just redraws region exposed to redraw
// Whole area can be resolved by using widgets Allocation property
rect = CALCULATE_MY_RECTANGLE;
// Resolve widget you want to use, it can also be null
Gtk.Widget wgd = this;
// Load style based on fake
Style myStyle = Rc.GetStyle (Fakes.Entry);
base.OnExposeEvent (evnt);
if (CONDITION_WIDGET_IS_SENSITIVE == false) {
Gtk.Style.PaintFlatBox (myStyle, aArgs.Drawable, StateType.Insensitive,
ShadowType.In, rect, wdg, "entry_bg",
rect.X, rect.Y, rect.Width, rect.Height);
Gtk.Style.PaintShadow(myStyle, aArgs.Drawable, StateType.Insensitive,
ShadowType.In, rect, wdg, "entry",
rect.X, rect.Y, rect.Width, rect.Height);
}
else if (CONDITION_WIDGET_IS_FOCUSED == true) {
Gtk.Style.PaintFlatBox (myStyle, aArgs.Drawable, State,
ShadowType.In, rect, wdg, "entry_bg",
rect.X, rect.Y, rect.Width, rect.Height);
Gtk.Style.PaintFocus (myStyle, aArgs.Drawable, State, clip, wdg, "entry",
rect.X, rect.Y, rect.Width, rect.Height);
Gtk.Style.PaintShadow (myStyle, aArgs.Drawable, State,
ShadowType.In, rect, wdg, "entry",
rect.X, rect.Y, rect.Width, rect.Height);
}
else {
Gtk.Style.PaintFlatBox (myStyle, aArgs.Drawable, State,
ShadowType.In, rect, wdg, "entry_bg",
rect.X, rect.Y, rect.Width, rect.Height);
Gtk.Style.PaintShadow(myStyle, aArgs.Drawable, State,
ShadowType.In, rect, wdg, "entry",
rect.X, rect.Y, rect.Width, rect.Height);
}
// Dispose fake style
myStyle.Dispose();
myStyle = null;
}
Warning: It is very important to always resolve colors based on the style you copied (or any other for that matter).
Resolving colors goes like this:
Gdk.Color clr = myStyle.Backgrounds[(int) StateType.Normal];