Introduction
This article contains the description of some programming techniques concerning the Roma Widget Set (Xrw) - eiter used to extend the widget set or to apply it for application programming. It has been created because the whole topic grows beyond 50 print pages, and i decided to split it into three parts with and into four parts with . With i moved the complete API description to a separate HTML documentation (that is part of the Xrw project) and restructured all four articles to make reading more entertaining and exciting:
- The origin one: Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - Introduction. It contains a short explanation of the widget set's features. (Before it was: ~ Basics. It contained general descriptions.) This atricle should always be the preferred starting point.
- The first split-off: Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - Widget set. Due to the constantly growing of 'boring' API documentation, the API documentation has been moved with to a separate HTML documentation (that is part of the Xrw project) and the article has been focused on a briefly introduction of all widgets. (Before it was: ~ Intrinsic widgets. It contained the API reference description of intrinsic widgets only.)
- This second split-off: Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - Programming techniques. Due to the constantly growing of 'boring' API documentation, the API documentation has been moved with to a separate HTML documentation (that is part of the Xrw project) and this article has been focused on programming techniques. (Before it was: ~ Simple widgets. It contained the API reference description of simple widgets only.)
- The third split-off: Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - MVVM/XAML support. Due to the constantly growing of 'boring' API documentation, the API documentation has been moved with to a separate HTML documentation (that is part of the Xrw project) and this article has been focused on introducing the MVVM/XAML support of the Xrw. (Before it was: ~ Composite widgets. It contains the API reference description of composite widgets only.)
All features, described later on, can be marked as:
- available from version 0.1,
- available from version 0.2,
- available from version 0.3,
- available from version 0.4,
- available from version 0.5,
- available from version 0.6,
- available from version 0.7,
- available from version 0.8,
- available from version 0.9 and
|
- disabled with version 0.2,
- disabled with version 0.3,
- disabled with version 0.4,
- disabled with version 0.5,
- disabled with version 0.6,
- disabled with version 0.7,
- disabled with version 0.8,
- disabled with version 0.9.
|
The framework has a generic theme support and these predefined themes:
XrwTheme.GeneralStyle.WinClassic
(in and named XrwTheme.GeneralStyle.Win95
; looks like Windows 95/98/Me/2K and a little bit like Motif - classic gray 3D),
XrwTheme.GeneralStyle.WinLuna
(loocks similar to Windows XP / MS Office 2007),
XrwTheme.GeneralStyle.WinRoyale
(loocks similar to Windows Vista/7 / MS Office 2010),
XrwTheme.GeneralStyle.WinMidori
(loocks similar to Windows 8/8.1 / MS Office 2013) and
XrwTheme.GeneralStyle.Gtk2Clearlooks
(looks like Gnome 2.30.0 Clearlooks - typical GTK 2).
The theme support covers colors, geometries and images.
To design an uncommon GUI for a specific application, almost all widget properties, that are influenced by a theme, can also be overruled by individual values. Or a new, alternatively XrwTheme.GeneralStyle
can be created and applied.
Look & feel of XrwTheme.GeneralStyle.WinClassic
demonstrated with the "File selection" dialog.
Look & feel of XrwTheme.GeneralStyle.Gtk2Clearlooks
demonstrated with the "File selection" dialog.
See HTML cocumentation of XrwTheme
class for details and X11Graphic
class for supported stock items.
Font support
The default font of most Xfree86 installation is "-misc-fixed-medium-r-semicondensed--13-*-*-*-*-*-*"
. Hence the XrwTheme
class predefines these default font specifications:
DefaultFontName = "-misc-fixed-medium-r-semicondensed--13-*-*-*-*-*-*";
DefaultItalicFontName = "-misc-fixed-medium-o-semicondensed--13-*-*-*-*-*-*";
DefaultBoldFontName = "-misc-fixed-bold-r-semicondensed--13-*-*-*-*-*-*";
These fonts are almost guaranteed to be available at every X server but they are monospace bitmap fonts and do not look very smart.
The image shows a sample -misc-fixed-
font output.
This version introduces convenience methods to change the predefined font specifications with user defined font specifications and ensures all widget's GC (graphics context) initialization with the DefaultFontName
font specification.
TrySetDefaultFont
set the DefaultFontName
to the user defined font specification and returns true on success (font is available) or leaves the DefaultFontName
unchanged and returns false otherwise.
TrySetDefaultItalicFont
set the DefaultItalicFontName
to the user defined font specification and returns true on success (font is available) or leaves the DefaultItalicFontName
unchanged and returns false otherwise.
TrySetDefaultBoldFont
set the DefaultBoldFontName
to the user defined font specification and returns true on success (font is available) or leaves the DefaultBoldFontName
unchanged and returns false otherwise.
It is recommended to set user defined font specifications after the creation of the application shell before the widget hierarchy is build up.
public static void Main ()
{
XrwTheme.Style = XrwTheme.GeneralStyle.Gtk2Clearlooks;
Point assignedPosition = new Point (0, 0);
Size assignedSize = new Size (353, 480);
X11Window appWindow = new X11Window(ref assignedPosition, ref assignedSize);
XrwTheme.TrySetDefaultBoldFont (appWindow.Display, appWindow.GC,
"-*-helvetica-bold-r-normal--12-*-*-*-*-*-*");
XrwTheme.TrySetDefaultItalicFont (appWindow.Display, appWindow.GC,
"-*-helvetica-medium-o-normal--12-*-*-*-*-*-*");
XrwTheme.TrySetDefaultFont (appWindow.Display, appWindow.GC,
"-*-helvetica-medium-r-normal--12-*-*-*-*-*-*");
appWindow.Run ();
}
This version introduces -*-helvetica-
fonts for the XrwTheme
class predefinitions, because they look smarter than the -misc-fixed-
fonts.
The image shows a sample -*-helvetica-
font output.
Support for 16 Bit color model
The general approach to support 16 bit color model is to use an individual visual and colormap for the application. In this specific case it is based on a (virtually) 24 bit color model (hardware and X11 server have 24 bit color capabilities) while the X server is running in 16 bit color mode. There is no experience with a (physically) 16 bit color model (hardware and X11 server don't have 24 bit color capabilities).
The necessary code, especially XrwCore.InitializeApplicationShellWindow()
, XrwCore.InitializeTransientShellWindow()
and XrwCore.InitializeOverrideShellWindow()
has been prepared already by
. The following changes have been implemented with to provide support for this specific 16 bit color model case finally:
- Reduction of the stock icon's color depth to 15 bit (not necessarily needed, but recommended).
- Reduction of the application's icon color depth to 15 bit (required).
- Correction of
XrwApplicationFramework.SetWmShellIcon()
(see "Fixed with " No. 3).
- Correction of
X11Graphic.
CreateIndependentGraphicPixmap()
(see "Fixed with " No. 4).
The /etc/X11/xorg.conf.d/50-screen.conf on my OPEN SUSE 11.3 Linux 32 bit EN has been modiefied like that, to test 16 bit color model support:
Section "Screen"
Identifier "Default Screen"
Device "Default Device"
## Doesn't help for radeon/radeonhd drivers; use magic in
## 50-device.conf instead
Monitor "Default Monitor"
# DefaultDepth 32 Chrashing!
# DefaultDepth 24 # Running!
DefaultDepth 16 # Running!
# DefaultDepth 15 Chrashing!
# DefaultDepth 8 # Running, but ugly!
#############################################################################
# Use one of these GRUB start options to repair a crashing X11 session:
# - vga=ask: This option allows you to select the mode for the video adaptor.
# - init=/bin/sh: Run the program /bin/sh (the shell) instead of init.
#############################################################################
EndSection
The ("X -configure" generated) /etc/X11/xorg.conf on my OPEN SUSE 12.3 Linux 64 bit DE has been modiefied like that, to test 16 bit color model support:
Section "Screen"
...
# DefaultDepth 32 Chrashing!
# DefaultDepth 24 # Running!
DefaultDepth 16 # Running!
# DefaultDepth 15 Running with Xfce only!
# DefaultDepth 8 # Running, but ugly!
EndSection
Internationalisation
Support for internationalized text output
There is a very good document about I18N. And Chapter 13.1 TWM -- usage of XFontSet instead of XFontStruct is the comprehensive guid for X11 application's internationalized text output implementation.
The first major change of the Roma Widget Set's internal architecture was to introduce the X11FontData
structure, that supports XFontSet
alternatively to XFontStruct
and to provide X11Surface.TextBoundings()
as single point for all text measurement as well as X11Surface.DrawString()
as single point for all text output.
The second major change of the internal architecture was to provide XFontSet
alternatively to XFontStruct
during font allocation. Depending on the I18N capabilities of the X server and the C runtime, the XrwApplicationShell
's UseFontSet
property is set to true
(or false
) and XFontSet
is used instead of XFontStruct
(or isn't). Font allocation is realized by XrwCore
's PrepareFont()
method.
The story of the subsequent methods is quickly recounted: After a check of the prerequisits it is tested if FontData
are to set to application's default fontset/font or current FontData
is already up-to-date. Elsewise the requested fontset (if XrwApplicationShell
's UseFontSet
property is true
) or font is loaded and assigned. To provide fast geometry calculation the fontsets/fonts maximum height, ascent and descent are provided with FontData
as well.
XrwApplicationShell
's font initialization:
public virtual bool SetFont (string fontSpecification)
{ X11.X11FontData fontData = null;
PrepareFont (fontSpecification, ref fontData);
if (fontData == null)
return false;
_fontData = fontData;
return true;
}
XrwCore
's font preparation:
protected bool PrepareFont (string fontSpecification, ref X11.X11FontData fontData)
{
if (_surface.Display == IntPtr.Zero)
{
SimpleLog.LogLine (TraceEventType.Error, CLASS_NAME +
"::PrepareFont () Can not set a fontset/font to undefined display.");
return false;
}
if (string.IsNullOrEmpty (fontSpecification))
{
SimpleLog.LogLine (TraceEventType.Error, CLASS_NAME +
"::PrepareFont () Can not set a fontset/font with empty specification.");
return false;
}
XrwApplicationShell appShell = ApplicationShell;
if (appShell == null)
{
SimpleLog.LogLine (TraceEventType.Error, CLASS_NAME +
"::PrepareFont () Can not set a fontset/font for a widget/gadget " +
"that is not associated with an application shell.");
return false;
}
if (fontData != null && fontData.FontSpecification == fontSpecification)
{
SimpleLog.LogLine (TraceEventType.Warning, CLASS_NAME +
"::PrepareFont () Skip to reset fontset/font from '" + fontData.FontSpecification +
"' to '" + fontSpecification + "'.");
return true;
}
if ((appShell.FontData != null ?
appShell.FontData.FontSpecification == fontSpecification : false) == true)
{
fontData = appShell.FontData;
return true;
}
return X11FontService.PrepareFont (fontSpecification, _surface.Display,
appShell.UseFontset, ref fontData);
}
X11FontService
's font preparation:
public static bool PrepareFont (string fontSpecification, IntPtr x11display, bool useFontset, ref X11.X11FontData fontData)
{
fontData = null;
foreach (KeyValuePair<FontDataKey, X11FontData> loadedFont in _loadedFonts)
{
if (loadedFont.Key.FontSpecification == fontSpecification &&
loadedFont.Key.X11Display == x11display && loadedFont.Key.UseFontset)
{
fontData = loadedFont.Value;
return true;
}
}
FontDataKey key = new FontDataKey (fontSpecification, x11display, useFontset)
...
Start loading the requested fontset. If requested fontSpecification
doesn't correspond to an existing fontset completely, make fontSpecification
more fuzzy step by step, until an existing fontset corresponds. This starts with stretch, continued with weight and slant. In case of no success, load the font server's fallback fontset.
...
if (useFontset)
{
IntPtr missingCharsetList;
TInt missingCharsetCount;
X11.XID fontsetResourceId = X11lib.XCreateFontSet (x11display, fontSpecification, out missingCharsetList,
out missingCharsetCount, IntPtr.Zero);
int fuzzyFactor = 0;
string fuzzyFontSpecification = (fontsetResourceId == (X11.XID)0 ? fontSpecification : null);
while (fontsetResourceId == (X11.XID)0 && fuzzyFactor < 3)
{
string lastFuzzyFontSpecification = fuzzyFontSpecification;
if (fuzzyFactor == 0)
fuzzyFontSpecification = X11FontData.ModifyFontSpecificationStretch (fuzzyFontSpecification, "*");
if (fuzzyFactor == 1)
fuzzyFontSpecification = X11FontData.ModifyFontSpecificationWieght (fuzzyFontSpecification, "*");
if (fuzzyFactor == 2)
fuzzyFontSpecification = X11FontData.ModifyFontSpecificationSlant (fuzzyFontSpecification, "*");
fuzzyFactor++;
if (lastFuzzyFontSpecification == fuzzyFontSpecification)
continue;
if (!string.IsNullOrEmpty(lastFuzzyFontSpecification) && lastFuzzyFontSpecification.Trim() != "")
{
fontsetResourceId = X11lib.XCreateFontSet (x11display, fuzzyFontSpecification,
out missingCharsetList,
out missingCharsetCount, IntPtr.Zero);
if (fontsetResourceId != (X11.XID)0)
{
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Fuzzy load fontset with specification '" +
fuzzyFontSpecification + "' " +
"instead of '" + fontSpecification + "' succeeded.");
}
}
}
string extFontSpecification = null;
if (fontsetResourceId == (X11.XID)0)
{
if (!string.IsNullOrEmpty(fontSpecification) && fontSpecification.Trim() != "" &&
!fontSpecification.Trim().EndsWith (",*"))
{
extFontSpecification = fontSpecification + ",*";
SimpleLog.LogLine (TraceEventType.Warning, CLASS_NAME +
"::PrepareFont () Can not load a fontset with specification '" +
fontSpecification + "'.");
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Retry to load a fontset with specification '" +
extFontSpecification + "'.");
fontsetResourceId = X11lib.XCreateFontSet (x11display, extFontSpecification,
out missingCharsetList,
out missingCharsetCount, IntPtr.Zero);
}
else
{
SimpleLog.LogLine (TraceEventType.Error, CLASS_NAME +
"::PrepareFont () Can not load a fontset with specification '" +
fontSpecification + "'.");
return false;
}
}
if (fontsetResourceId == (X11.XID)0)
{
SimpleLog.LogLine (TraceEventType.Error, CLASS_NAME +
"::PrepareFont () Can not load a fontset with specification '" +
extFontSpecification + "'.");
return false;
}
...
Report the finally loaded fontset and the missing character sets. Calculate some fontset attributes and create the fontData
.
...
if (!string.IsNullOrEmpty (extFontSpecification))
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Successfully loaded best matching fontset for specification '" +
fontSpecification + "' " +
"using specification '" + extFontSpecification + "'.");
else if (!string.IsNullOrEmpty (fuzzyFontSpecification))
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Successfully loaded best matching fontset for specification '" +
fontSpecification + "' " +
"using specification '" + fuzzyFontSpecification + "'.");
else
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Successfully loaded best matching fontset for specification '" +
fontSpecification + "'.");
for (int countCharSet = 0; countCharSet < (int)missingCharsetCount; countCharSet++)
{
IntPtr p = Marshal.ReadIntPtr (missingCharsetList, countCharSet * Marshal.SizeOf(typeof(IntPtr)));
string s = Marshal.PtrToStringAuto (p);
if (!string.IsNullOrEmpty (extFontSpecification))
SimpleLog.LogLine (TraceEventType.Warning, CLASS_NAME +
"::PrepareFont () Fontset for specification '" +
extFontSpecification + "' is missing font for charset '" + s + "'.");
else if (!string.IsNullOrEmpty (fuzzyFontSpecification))
SimpleLog.LogLine (TraceEventType.Warning, CLASS_NAME +
"::PrepareFont () Fontset for specification '" +
fuzzyFontSpecification + "' is missing font for charset '" + s + "'.");
else
SimpleLog.LogLine (TraceEventType.Warning, CLASS_NAME +
"::PrepareFont () Fontset for specification '" +
fontSpecification + "' is missing font for charset '" + s + "'.");
}
int ascent = 0;
int descent = 0;
X11lib.XFontSetExtents extents = X11lib.XExtentsOfFontSet (fontsetResourceId);
X11lib.XFontStruct[] fontStructArray;
string[] fontNameArray;
int maxFonts;
maxFonts = X11lib.XFontsOfFontSet (fontsetResourceId, out fontStructArray, out fontNameArray);
for (int countFonts = 0; countFonts < maxFonts; countFonts++)
{
if (ascent < (int)fontStructArray[countFonts].ascent)
ascent = (int)fontStructArray[countFonts].ascent;
if (descent < (int)fontStructArray[countFonts].descent)
descent = (int)fontStructArray[countFonts].descent;
}
string finalFontSpecification = null;
if (!string.IsNullOrEmpty (extFontSpecification))
finalFontSpecification = extFontSpecification;
else if (!string.IsNullOrEmpty (fuzzyFontSpecification))
finalFontSpecification = fuzzyFontSpecification;
else
finalFontSpecification = fontSpecification;
fontData = X11FontData.NewFontSetData (finalFontSpecification, x11display, fontsetResourceId,
(int)extents.max_logical_extent.height, ascent, descent);
IntPtr gc = X11lib.XCreateGC (x11display, X11lib.XDefaultRootWindow (x11display), 0, IntPtr.Zero);
if (gc != IntPtr.Zero)
{
fontData.SetTypicalCharWidth (AverageCharacterWidth(x11display, gc, fontData));
X11lib.XFreeGC (x11display, gc);
}
_loadedFonts.Add (key, fontData);
return true;
}
...
Start loading the requested font, if fontset isn't supported. If requested fontSpecification
doesn't correspond to an existing font completely, make fontSpecification
more fuzzy step by step, until an existing font corresponds. This starts with stretch, continued with weight and slant. In case of no success, load the font server's fallback font.
...
else {
IntPtr fontStructure = X11lib.XLoadQueryFont (x11display, fontSpecification);
int fuzzyFactor = 0;
string fuzzyFontSpecification = (fontStructure == IntPtr.Zero ? fontSpecification : null);
while (fontStructure == IntPtr.Zero && fuzzyFactor < 3)
{
string lastFuzzyFontSpecification = fuzzyFontSpecification;
if (fuzzyFactor == 0)
fuzzyFontSpecification = X11FontData.ModifyFontSpecificationStretch (fuzzyFontSpecification, "*");
if (fuzzyFactor == 1)
fuzzyFontSpecification = X11FontData.ModifyFontSpecificationWieght (fuzzyFontSpecification, "*");
if (fuzzyFactor == 2)
fuzzyFontSpecification = X11FontData.ModifyFontSpecificationSlant (fuzzyFontSpecification, "*");
fuzzyFactor++;
if (lastFuzzyFontSpecification == fuzzyFontSpecification)
continue;
if (!string.IsNullOrEmpty(lastFuzzyFontSpecification) && lastFuzzyFontSpecification.Trim() != "")
{
fontStructure = X11lib.XLoadQueryFont (x11display, lastFuzzyFontSpecification);
if (fontStructure != IntPtr.Zero)
{
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Fuzzy load font with specification '" +
fuzzyFontSpecification + "' " +
"instead of '" + fontSpecification + "' succeeded.");
}
}
}
string extFontSpecification = null;
if (fontStructure != IntPtr.Zero)
{
if (!string.IsNullOrEmpty(fontSpecification) && fontSpecification.Trim() != "" &&
!fontSpecification.Trim().EndsWith (",*"))
{
extFontSpecification = fontSpecification + ",*";
SimpleLog.LogLine (TraceEventType.Warning, CLASS_NAME +
"::PrepareFont () Can not load a fontset with specification '" +
fontSpecification + "'.");
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Retry to load a fontset with specification '" +
extFontSpecification + "'.");
fontStructure = X11lib.XLoadQueryFont (x11display, extFontSpecification);
}
else
{
SimpleLog.LogLine (TraceEventType.Error, CLASS_NAME +
"::PrepareFont () Can not load a font with specification '" +
fontSpecification + "'.");
return false;
}
}
if (fontStructure == IntPtr.Zero)
{
SimpleLog.LogLine (TraceEventType.Error, CLASS_NAME +
"::PrepareFont () Can not load a font with specification '" +
fontSpecification + "'.");
return false;
}
...
Report the finally loaded font. Create the fontData
.
...
if (!string.IsNullOrEmpty (extFontSpecification))
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Successfully loaded best matching font for specification '" +
fontSpecification + "' " +
"using specification '" + extFontSpecification + "'.");
else if (!string.IsNullOrEmpty (fuzzyFontSpecification))
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Successfully loaded best matching font for specification '" +
fontSpecification + "' " +
"using specification '" + fuzzyFontSpecification + "'.");
else
SimpleLog.LogLine (TraceEventType.Information, CLASS_NAME +
"::PrepareFont () Successfully loaded best matching font for specification '" +
fontSpecification + "'.");
X11lib.XFontStruct fs = (X11lib.XFontStruct)Marshal.PtrToStructure (fontStructure,
typeof(X11lib.XFontStruct));
string finalFontSpecification = null;
if (!string.IsNullOrEmpty (extFontSpecification))
finalFontSpecification = extFontSpecification;
else if (!string.IsNullOrEmpty (fuzzyFontSpecification))
finalFontSpecification = fuzzyFontSpecification;
else
finalFontSpecification = fontSpecification;
fontData = X11FontData.NewSingleFontData (finalFontSpecification, x11display, fs.fid,
(int)fs.ascent + (int)fs.descent,
(int)fs.ascent, (int)fs.descent);
IntPtr gc = X11lib.XCreateGC (x11display, X11lib.XDefaultRootWindow (x11display), 0, IntPtr.Zero);
if (gc != IntPtr.Zero)
{
fontData.SetTypicalCharWidth (AverageCharacterWidth(x11display, gc, fontData));
X11lib.XFreeGC (x11display, gc);
}
_loadedFonts.Add (key, fontData);
return true;
}
}
Anatomy of an application or dialog window
Application windows are based on an XrwApplicationShell
, dialog windows are based on an XrwDialogShell
. Both are derived from abstract XrwWmShell
, and XrwWmShell
is derived from XrwComposite
- the base class for containers, that manage an arbitary number of child widgets. XrwWmShell
provides interaction with the windows manager (move, resize, close, ... of a window). Since XrwComposite
has no integrated layout management, it is recommended to assign one XrwBox
, XrwGridForm
, XrwUniformGrid
or XrwDockPanel
child to each XrwApplicationShell
or XrwDialogShell
instance, that manages the layout of the shell's grandchildren.
Since XrwWmShell
derivatives set the XSetWindowAttributes
attribute bit_gravity
to NorthWestGravity
and the window's background color to XrwTheme.GeneralBackgroundColor
, the flickering effects during the redraw procedure (as concequence of ConfigureNotify
events - to observe on window resize operations) of a shell's layout manager widget/gadget are already minimized: Most of the flickering effects come from the black (undrawn) shell background and the time delay between shell background cleaning and manager widget/gadget redrawing.
There are some more reasons for flickering effects - see Chapter Specific aspects of event processing.
The sample code shows how to use a XrwBox
as manager gadget of a dialog shell.
public class XrwBitmapAndVectorFontSelectionDialog : XrwDialogShell
{
...
public XrwBitmapAndVectorFontSelectionDialog (XrwApplicationShell parent,
ref Point assignedPosition,
ref Size assignedSize, string title)
: base (parent, ref assignedPosition, ref assignedSize)
{
...
XrwBox vboxMain = XrwBox.NewVBoxGadget (this);
vboxMain.BorderWidth = XrwTheme.DlgShellPrimaryChildBorderWidth;
vboxMain.BorderColor = _backgroundColorPixel;
vboxMain.VertSpacing = XrwTheme.DlgShellPrimaryChildSpacing;
AddChild (vboxMain);
...
}
...
}
A dialog window typically contains an action area with action buttons - e.g. Cancel and OK.
The next sample code shows how to create an action area, extracted from a dialog constructor.
{
...
XrwBox hboxActionArea = XrwBox.NewHBoxGadget (vboxMain);
hboxActionArea.BorderWidth = 2;
hboxActionArea.ChildAlign = 1.0F;
hboxActionArea.HorzSpacing = XrwTheme.DlgShellPrimaryChildSpacing;
hboxActionArea.BorderColor = hboxActionArea.BackgroundColorDark;
vboxMain.AddChild (hboxActionArea);
X11Graphic cancelGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Cancel16);
XrwCommand cbCancel = XrwCommand.NewCommandWidget (hboxActionArea, "Cancel", cancelGraphic,
true, null, false);
cbCancel.ExpandToMaxSiblingWidth = true;
cbCancel.HorzTextAlign = 0.5F;
cbCancel.Clicked += HandleCancelButtonClicked;
hboxActionArea.AddChild (cbCancel);
ICommand cmd0 = new RelayCommand (ProcessCancelButtonAction);
Xrw.Utils.KeyGestureBinding kgb0 = new Xrw.Utils.KeyGestureBinding (cmd0,
X11lib.XKeySym.XK_Escap, System.Windows.Input.ModifierKeys.None);
base._inputReceiver.Add (kgb0);
X11Graphic okGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Ok16);
XrwCommand cbOk = XrwCommand.NewCommandWidget (hboxActionArea, "OK", okGraphic,
true, null, false);
cbOk.ExpandToMaxSiblingWidth = true;
cbOk.HorzTextAlign = 0.5F;
cbOk.Clicked += HandleOkButtonClicked;
hboxActionArea.AddChild (cbOk);
ICommand cmd1 = new RelayCommand (ProcessOkButtonAction);
Xrw.Utils.KeyGestureBinding kgb1 = new Xrw.Utils.KeyGestureBinding (cmd1,
X11lib.XKeySym.XK_Return, System.Windows.Input.ModifierKeys.None);
Xrw.Utils.KeyGestureBinding kgb2 = new Xrw.Utils.KeyGestureBinding (cmd1,
X11lib.XKeySym.XK_Num_Enter, System.Windows.Input.ModifierKeys.None);
base._inputReceiver.Add (kgb1);
base._inputReceiver.Add (kgb2);
...
}
This is what it looks like:
The button callbacks are connected to the Clicked
event.
void HandleOkButtonClicked (XrwRectObj source)
{
if ((source is XrwCommand) && !(source as XrwCommand).Focused)
return;
ProcessOkButtonAction (null);
}
private void ProcessOkButtonAction (object o)
{
_result = System.Windows.MessageBoxResult.OK;
this.DefaultClose ();
this.OnEnd (_result);
}
void HandleCancelButtonClicked (XrwRectObj source)
{
if ((source is XrwCommand) && !(source as XrwCommand).Focused)
return;
ProcessCancelButtonAction (null);
}
private void ProcessCancelButtonAction (object o)
{
_result = System.Windows.MessageBoxResult.Cancel;
this.DefaultClose ();
this.OnEnd (_result);
}
Since this version introduces key gesture binding, the ultimate Clicked
event processing isn't part of the event handler, but swaped out to the corresponding action method. This provides the feasibility to use the action methos a second time. Namely as keyboard input receiver using the Xrw.Utils.KeyGestureBinding
. Thereby the dialog can be closed with [Escape] key (that is equivalent to the dialog's Cancel button), [Enter] or [Return] key (that is equivalent to the dialog's OK button).
Dialogs are based on transient shells and often take over the (infinite) message loop processing from the application shell. To fully clean up such active dialogs by closing the application window, they must override the transient shell's DefaultClose()
method to stop it's (infinite) message loop processing. Here is a closer look on this: The XrwTransientShell
implements these two message handler.
OnWmClose()
is called only if shell closing is invoked via the window decoration's close button. It's only purpose is to provide event forwarding to registered delegates. Even if it is virtual, additional functionality should be implemented via delegates rather than via overwriting. This, besides, promotes code reuse.
DefaultClose()
should always be called, no matter whether closing is invoked via the window decoration's close button, a widget/gadget inside the dialog (like the [OK] or [Cancel] button) or a keyboard shortcut (like [Return] or [Escape]). Derived classes can use the override to prepare return values and terminate their (infinite) message loop processing.
#region Event handler
internal virtual void OnWmClose (XrwClientMessageEvent e)
{
WmShellCloseDelegate wmShellClose = WmShellClose;
if (wmShellClose != null)
wmShellClose (this, e);
}
public virtual bool DefaultClose ()
{
if (_disposed == true)
return true;
this.ApplicationShell.RemoveTransientShell (this);
Unrealize ();
Dispose ();
return true;
}
#endregion
A XrwTransientShell
derived class, e. g. XrwFileSelectionDialog
, should override the DefaultClose()
to prepare return values and terminate it's (infinite) message loop processing. And it should register and implement HandleDialogClose()
to provide a consistent behaviour on closing, no matter whether closing is invoked via the window decoration's close button, via a dialog's [Cancel] button or the [Escape] key shortcut.
WmShellClose += HandleDialogClose;
#region Overwritten methods (XrwTransientShell)
public override bool DefaultClose ()
{
_result = System.Windows.MessageBoxResult.Cancel;
return base.DefaultClose ();
}
#endregion Overwritten methods (XrwTransientShell)
#region Event handler
void HandleDialogClose (XrwRectObj source, XrwClientMessageEvent e)
{
this.DefaultClose ();
e.Result = 1;
this.OnEnd (_result);
}
...
Usage of popup menus
Popup menus can be created very easily. These are the steps:
- Create a popup menu shell
XrwSimpleMenuShell
.
- Add menu entries of
XrwSme
class to the menu shell and register the callbacks to the menu entries.
- Force shell's geometry management.
- Create a menu button
XrmMenuButton
and add it to the parent composite.
- Register the menu shell to the menu button.
There is no additional code to process pop up, layout, pop down or selection required.
The image shows a simple XrwMenuButton
with left and right (transparent multicolor) bitmap - including it's poped up menu, based on a XrwDialogShell
containing two XrwSme
with left and right (transparent multicolor) bitmap.
The sample code shows how to create the simple pop up menu, illustrated by the previous image.
XrwSimpleMenu _fileMenuShell = null;
...
Point origin = new Point (20, 20);
Size initSize = new Size (-1, -1);
_fileMenuShell = new XrwSimpleMenu (this, ref origin, ref initSize);
X11Graphic menuEntryGraphicA = XrwTheme.GetGraphic ( _display,_screenNumber,
X11Graphic.StockIcon.Information16);
X11Graphic menuEntryGraphicB = XrwTheme.GetGraphic ( _display,_screenNumber,
X11Graphic.StockIcon.Question16);
XrwSme menuEntry1 = XrwSme.NewSmeGadget (_fileMenuShell, "File menu entry 1",
menuEntryGraphicA, true, menuEntryGraphicB, true);
menuEntry1.ButtonRelease += HandleMenuEntry1ButtonRelease;
_fileMenuShell.AddChild (menuEntry1);
XrwSme menuEntry2 = XrwSme.NewSmeGadget (_fileMenuShell, "File menu entry 2",
menuEntryGraphicA, true, menuEntryGraphicB, true);
menuEntry2.ButtonRelease += HandleMenuEntry2ButtonRelease;
_fileMenuShell.AddChild (menuEntry2);
_fileMenuShell.CalculateChildLayout ();
_fileMenuShell.SetFixedWidth (_fileMenuShell.AssignedSize.Width);
_fileMenuShell.SetFixedHeight (_fileMenuShell.AssignedSize.Height);
X11Graphic cbw0GraphicA = XrwTheme.GetGraphic ( _display,_screenNumber,
X11Graphic.StockIcon.FileGeneric16);
X11Graphic cbw0GraphicB = XrwTheme.GetGraphic ( _display,_screenNumber,
X11Graphic.StockIcon.FolderClose16);
XrwMenuButton commandFileMenu = XrwMenuButton.NewMenuButtonWidget
(hboxFileRibbon, "File", cbw0GraphicA, true, cbw0GraphicB, true);
commandFileMenu.FrameType = XrwTheme.StaticFrameType;
commandFileMenu.FrameWidth = XrwTheme.StaticFrameWidth;
commandFileMenu.ExpandToAvailableHeight = true;
commandFileMenu.Menu = _fileMenuShell;
hboxFileRibbon.AddChild (commandFileMenu);
Closing an application Window
To provide a convenient API to the application developer, some things have to be prepared framework internally:
1. To clean up the XrwApplicationShell
, a primary shell's close delegate is registered:
this.WmShellClose += HandleApplicationShellCloseDefault;
...
void HandleApplicationShellCloseDefault (XrwRectObj source, XrwClientMessageEvent e)
{
...
Dispose ();
X11lib.XCloseDisplay (_surface.Display);
_surface.SetDisplay (IntPtr.Zero);
e.Result = 0;
}
2. Since the OnClose
delegate is implemented to invoke registered handler in reverse order, the clean up process always processes from derived classes back to the base class - and the base class handler must be the last besause it disconnects from X11 server.
public void OnClose (XrwClientMessageEvent e)
{
object[] param = new object[] {this, e};
Delegate[] delegates = WmShellClose.GetInvocationList();
for (int i=delegates.Length-1;i>=0;i--)
delegates[i].DynamicInvoke (param);
}
This enables resource deallocation without memory leaks.
Usage of standard dialogs
Curently these standard dialogs are available:
XrwMessageBox
for a notification only dialog (no input except the choice between OK and Cancel).
XrwFileSelectionDialog
for single file selection.
XrwBitmapAndVectorFontSelectionDialog
to select a font using all X11 font information.
This is an image of the XrwMessageBox
with plain text and with markup text, introduced with .
This is an image of the XrwFileSelectionDialog
.
This is an image of the XrwBitmapAndVectorFontSelectionDialog
.
(This dialog is suitable for non-internationalized text output using XLoadFont
and XDrawString
or XDrawString16
but not for I18N text output using font set.)
Now these additional standard dialogs are available:
These are images of the XrwColorSelectionDialog
, using 16 predefined colors in 8 columns and 2 rows or 2 columns and 8 rows with color names.
Now this additional standard dialog is available:
These are images of the XrwColorChooseDialog
, using one notebook page for standard colors and one notebook page for custom colors.
Now this additional standard dialog is available:
This is an image of the XrwFontSelectionDialog
.
(This dialog is suitable for internationalized text output using font set, XCreateFontSet
and X
wcDrawString
.)
The creation of XrwFileSelectionDialog
, XrwBitmapAndVectorFontSelectionDialog
, XrwColorSelectionDialog
, XrwColorChoseDialog
and XrwFontSelectionDialog
is straight forward.
The sample code shows how to use the XrwFileSelectionDialog
.
void HandleFileSelectionDialogButtonClicked (XrwRectObj source)
{
XrwFileSelectionDialog fileDialog = XrwFileSelectionDialog.
NewFileSelectionDialog (this, "Mono Develop - File selection", Environment.CurrentDirectory);
fileDialog.SetMinimumSize (fileDialog.AssignedSize);
XrwDialogShell.DialogResult result = fileDialog.Run ();
if (result == XrwDialogShell.Result.OK)
{
if (XrwApplicationSettings.VERBOSE_OUTPUT_TO_CONSOLE)
Console.WriteLine ("VERBOSE: " + CLASS_NAME +
"::HandleFileSelectionDialogButtonClicked() " +
"File dialog closed with: OK, File selected is: " + fileDialog.SelectedFile);
ApplicationFramework.WriteStatus ("File selection dialog closed with: OK");
}
else
{
if (XrwApplicationSettings.VERBOSE_OUTPUT_TO_CONSOLE)
Console.WriteLine ("VERBOSE: " + CLASS_NAME +
"::HandleFileSelectionDialogButtonClicked() " +
"File dialog closed with: Cancel");
ApplicationFramework.WriteStatus ("File selection dialog closed with: Cancel");
}
}
The next sample code shows how to use the XrwBitmapAndVectorFontSelectionDialog
.
void HandleFontDialogButtonClicked (XrwRectObj source)
{
XrwBitmapAndVectorFontSelectionDialog fontDialog = XrwBitmapAndVectorFontSelectionDialog.
NewBitmapAndVectorFontSelectionDialog (this, "Mono Develop - Font selection");
fontDialog.SetMinimumSize (fontDialog.AssignedSize);
XrwDialogShell.DialogResult result = fontDialog.Run ();
if (result == XrwDialogShell.Result.OK)
{
if (XrwApplicationSettings.VERBOSE_OUTPUT_TO_CONSOLE)
Console.WriteLine ("VERBOSE: " + CLASS_NAME + "::HandleFontDialogButtonClicked() " +
"Font dialog closed with: OK");
ApplicationFramework.WriteStatus ("Font selection dialog closed with: OK");
}
else
{
if (XrwApplicationSettings.VERBOSE_OUTPUT_TO_CONSOLE)
Console.WriteLine ("VERBOSE: " + CLASS_NAME + "::HandleFontDialogButtonClicked() " +
"Font dialog closed with: Cancel");
ApplicationFramework.WriteStatus ("Font selection dialog closed with: Cancel");
}
}
The next sample code shows how to use the XrwColorSelectionDialog
.
void HandleColorSelectionDialogButtonClicked (XrwRectObj source)
{
XrwColorSelectionDialog colorDialog = XrwColorSelectionDialog.NewColorSelectionDialog8x2 (this,
"Mono Develop - Color selection", 0x00ffffff);
colorDialog.SetMinimumSize (colorDialog.AssignedSize);
XrwDialogShell.DialogResult result = colorDialog.Run ();
if (result == XrwDialogShell.Result.OK)
{
SimpleLog.LogLine (TraceEventType.Verbose, CLASS_NAME +
"::HandleColorSelectionDialogButtonClicked () Color dialog closed with: OK, " +
"Color selected is: #{0:X000000}", colorDialog.SelectedColor);
ApplicationFramework.WriteStatus ("Color selection dialog closed with: OK");
}
else
{
SimpleLog.LogLine (TraceEventType.Verbose, CLASS_NAME +
"::HandleColorSelectionDialogButtonClicked () Color dialog closed with: Cancel");
ApplicationFramework.WriteStatus ("Color selection dialog closed with: Cancel");
}
}
The next sample code shows how to use the XrwColorChooseDialog
.
void HandleColorChooseDialogButtonRelease (XrwRectObj source, XrwButtonEvent e)
{
XrwColorChooseDialog colorDialog = XrwColorChooseDialog.NewColorCooseDialog (this,
"Mono Develop - Color choose", 0x00ffffff);
colorDialog.SetMinimumSize (colorDialog.AssignedSize);
XrwDialogShell.DialogResult result = colorDialog.Run ();
if (result == XrwDialogShell.Result.OK)
{
SimpleLog.LogLine (TraceEventType.Verbose, CLASS_NAME +
"::HandleColorChooseDialogButtonRelease () Color dialog closed with: OK, " +
"Color selected is: #{0:X000000}", colorDialog.SelectedColor);
ApplicationFramework.WriteStatus ("Color selection dialog closed with: OK");
}
else
{
SimpleLog.LogLine (TraceEventType.Verbose, CLASS_NAME +
"::HandleColorChooseDialogButtonRelease () Color dialog closed with: Cancel");
ApplicationFramework.WriteStatus ("Color selection dialog closed with: Cancel");
}
}
The next sample code shows how to use the XrwFontSelectionDialog
.
void HandleVectorFontDialogButtonRelease (XrwRectObj source, XrwButtonEvent e)
{
XrwFontSelectionDialog fontDialog =
XrwFontSelectionDialog.NewFontSelectionDialog (this, "Mono Develop - GTK/Windows " +
"compatible font selection");
fontDialog.Font = new Xrw.FontInfo ("Adobe Helvetica", 14, System.Drawing.FontStyleEx.Italic);
System.Windows.MessageBoxResult result = fontDialog.Run ();
if (result == System.Windows.MessageBoxResult.OK)
{
SimpleLog.LogLine (TraceEventType.Verbose, CLASS_NAME +
"::HandleVectorFontDialogButtonRelease () Font dialog closed with: OK");
ApplicationFramework.WriteStatus ("Font selection dialog closed with: OK");
Xrw.FontInfo font = fontDialog.Font;
if (font == null)
return;
}
else
{
SimpleLog.LogLine (TraceEventType.Verbose, CLASS_NAME +
"::HandleVectorFontDialogButtonRelease () " +
"Font dialog closed with: Cancel");
ApplicationFramework.WriteStatus ("Font selection dialog closed with: Cancel");
}
e.Result = 1;
}
After the dialog instantiation this call is recommended:
dialog.SetMinimumSize(dialog.AssignedSize)
to prevent size underflow.
All dialogs calculate their initial size in a way that it is the smallest size where all controls are displayed properly. To set the initial size as the minimum size prevents the dialog from resizing to an unsuitable display.
Previous versions of Xrw recommended to call these methods after the dialog instantiation as well:
ApplicationFramework.SetWmShellIcon(dialog, APPICON_FILEPATH)
to set the shell icon.
this.AddTransientShell(dialog)
to register the dialog to the application's transient shell list.
But these calls are not necessary any longer, because they are called framework internally now.
XrwFileSelectionDialog
, XrwBitmapAndVectorFontSelectionDialog
, XrwColorSelectionDialog
and XrwFontSelectionDialog
are implemented as application modal dialogs. Hence the call XrwDialogShell.DialogResult result = dialog.Run()
waits for the dialog end and the result
can be evaluated subsequently.
Now all standard dialogs support key gesture binding. Thereby the dialogs can be closed with [Escape] key (if dialog contains an Cancel button), [Enter] or [Return] key (that are equivalent to the dialog's OK button).
Redraw performance issues and/or flickering
During the window resize process a plenty of ConfigureNotify
events are emitted by the windows manager. The application recalculates the layout of it's child widgets for every ConfigureNotify
event. New sizes in turn lead to corresponding Expose
events. Due to the asynchronous drawing model of X11 and - especially for complex layouts due to the layout recalculation time - redraw performance issues and/or flickering are the result.
To avoid this, Athena and Motif provide event compression flags with their wigets (compress_motion, compress_exposure, compress_enterleave). But this is no satisfying solution, because the events to compress must already be queued and immediate sequential. My tests with this approach didn't show a significant improvement.
The sample code shows a snippet of the XrwApplicationShell
's DoEvent()
method, that illustrates this event compression approach.
if (xevent.type == XEventName.ConfigureNotify)
{
X11EventHelper.Matches = 0;
X11lib.XCheckIfEvent (_display, ref X11EventHelper.Event,
X11EventHelper.CountConfigureMatchesProcPtr,
xevent.ConfigureEvent.window);
if (X11EventHelper.Matches > 0)
{
Console.WriteLine ("INFORMATION: " + CLASS_NAME + "::DoEvent () // CONFIGURE " +
"found subsequent configure events for window " +
xevent.ConfigureEvent.window.ToString("x") + " and skip this event.");
return true;
}
...
}
else if (xevent.type == XEventName.Expose)
{
if (xevent.ExposeEvent.count > 0)
return true;
X11EventHelper.Matches = 0;
X11lib.XCheckIfEvent (_display, ref X11EventHelper.Event,
X11EventHelper.CountExposeMatchesProcPtr, xevent.ExposeEvent.window);
if (X11EventHelper.Matches > 0)
{
Console.WriteLine ("INFORMATION: " + CLASS_NAME + "::DoEvent () // EXPOSE " +
"found subsequent expose events for window " +
xevent.ExposeEvent.window.ToString("x") + " and skip this event.");
return true;
}
...
}
The best way i've found to avoid redraw performance issues and/or flickering is to suspend every ConfigureNotify
event for some milli-seconds and to compress all ConfigureNotify
evens emmitted during this suspension. This causes a redrawing latency equal to the suspension interval, but the entire impression during a resizing process is much smoother.
The sample code shows a snippet of the XrwApplicationShell
's DoEvent()
method, that illustrates the event suspension approach.
private ulong _compressConfigureBySuspend = 350;
private ulong _lastConfigureShell = 0;
private XConfigureEvent _lastConfigureEvent = new XConfigureEvent();
...
public bool DoEvent()
{
if (_display == IntPtr.Zero)
return false;
XEvent xevent = new XEvent ();
X11lib.XFlush (_display);
if (X11lib.XQLength (_display) == 0 && _compressConfigureBySuspend > 0)
{
DateTime dt = DateTime.Now;
ulong timeStamp = (ulong)(dt.Millisecond + dt.Second * 1000 + dt.Minute * 60000 +
dt.Hour * 3600000) + (ulong)dt.Day * (ulong)86400000;
if (timeStamp - _lastConfigureShell > _compressConfigureBySuspend &&
_lastConfigureEvent.window != IntPtr.Zero)
{
XrwConfigureEvent e = new XrwConfigureEvent (ref _lastConfigureEvent);
OnConfigure (e);
_lastConfigureEvent.window = IntPtr.Zero;
_lastConfigureShell = timeStamp;
}
return true;
}
...
}
The _compressConfigureBySuspend
interval can be adjusted to a certain use case to find the optimum between latency and flickering. A value of 0 completely suppresses event suspension.
The only drawback i've found is, that XrwPaned
doen't benefit from this approach.
Undoubtedly the best approach to avoid redraw performance issues and/or flickering is to use double buffering. This approach is saved for later use, because it breaks the Xrw zero dependency promise.
Key gesture binding
With this version key gesture binding is supported. It can be used to register global keyboard shortcuts to any XrwShell
(typically application shell or dialog shell) targeting any GUI element. This technique can e. g. be used to improve the Accessibility of an application (or dialog). Typically the preferred targets of a key gesture binding are menus and ribbons. The sample code shows how to bind key gestures to a XrwRibbon
and select a specific XrwRibbonTab
.
...
System.Windows.Input.ICommand activateRibbonTab0_Action = new RelayCommand
(ActivateRibbonTab0_Action);
Xrw.Utils.KeyGestureBinding activateRibbonTab0_Gesture = new Xrw.Utils.KeyGestureBinding
(activateRibbonTab0_Action, null, null, X11lib.XKeySym.XK_D,
System.Windows.Input.ModifierKeys.Alt);
ApplicationShell.InputReceiver.Add (activateRibbonTab0_Gesture);
System.Windows.Input.ICommand activateRibbonTab1_Action = new RelayCommand
(ActivateRibbonTab1_Action);
Xrw.Utils.KeyGestureBinding activateRibbonTab1_Gesture = new Xrw.Utils.KeyGestureBinding
(activateRibbonTab1_Action, null, null, X11lib.XKeySym.XK_R,
System.Windows.Input.ModifierKeys.Alt);
ApplicationShell.InputReceiver.Add (activateRibbonTab1_Gesture);
System.Windows.Input.ICommand activateRibbonTab2_Action = new RelayCommand
(ActivateRibbonTab2_Action);
Xrw.Utils.KeyGestureBinding activateRibbonTab2_Gesture = new Xrw.Utils.KeyGestureBinding
(activateRibbonTab2_Action, null, null, X11lib.XKeySym.XK_S,
System.Windows.Input.ModifierKeys.Alt);
ApplicationShell.InputReceiver.Add (activateRibbonTab2_Gesture);
...
public void ActivateRibbonTab0_Action (object parameter)
{
ribbon.SetSelectedTab (0);
}
public void ActivateRibbonTab1_Action (object parameter)
{
ribbon.SetSelectedTab (1);
}
public void ActivateRibbonTab2_Action (object parameter)
{
ribbon.SetSelectedTab (2);
}
...
To indicate the keyboard shortcuts for a GUI element, the markup syntax should be used.
...
XrwRibbonTab dialogtestTab = XrwRibbonTab.NewRibbonTabGadget
(ribbon, "<markup><u>D</u>ialog test</markup>");
...
XrwRibbonTab radiotoggletestTab = XrwRibbonTab.NewRibbonTabGadget
(ribbon, "<markup><u>R</u>adio & toggle test</markup>");
...
XrwRibbonTab splitTab = XrwRibbonTab.NewRibbonTabGadget
(ribbon, "<markup><u>S</u>plit test</markup>");
...
This is what the result looks like. The ribbon tabs can be secected via key combinations [Alt]+[d], [Alt]+[r] or [Alt]+[s].
Event management policy in comparison
This table takes a closer look to the pointer events and actions that are invoked. Consistent behaviour is a very important fact for user acceptance. Different behaviour is highlighted in red.
Action |
Windows 8.1 |
GTK 2 clear looks |
Xrw |
Remark on Xrw |
open a menu
drop-down |
button 1 press
on menu button |
any button press
on menu button |
any button press
on menu button |
|
close a menu
drop-down |
delayed button 1
press on appropriate
menu button |
delayed any button
release on appropriate
on menu button |
delayed any button
press on appropriate
menu button |
press is more
common |
close a menu
drop-down |
any button press
outside the menu
drop-down |
any button release
outside the menu
drop-down |
any button press
outside the menu
drop-down |
press is more
common |
select a menu item |
button 1 release
on menu item |
any button release
on menu item |
any button release
on menu item |
currently no
context help |
open a combo box |
button 1 press
on combo box |
button 1 press
on combo box |
any button press
on combo box |
|
close a combo box |
delayed button 1
press on combo
box drop-down |
delayed button 1
release on combo
box drop-down |
delayed any button
press on combo
box drop-down |
press is more
common |
close a combo box |
any button press
outside the combo
box drop-down |
any button release
outside the combo
box drop-down |
any button press
outside the combo
box drop-down |
press is more
common |
select a combo box
item |
button 1 release
on combo box item |
button 1 release
on combo box item |
button 1 release
on combo box item |
currently no
context help |
open the ribbon's
application menu |
button 1 press
on menu button |
- |
any button press
on menu button |
|
close the ribbon's
application menu |
delayed button 1
press on appropriate
menu button |
- |
delayed any button
press on appropriate
menu button |
|
close the ribbon's
application menu |
any button press
outside the menu
drop-down |
- |
any button press
outside the menu
drop-down |
|
select a ribbon's
application menu
item |
button 1 release
on menu item |
- |
any button release
on menu item |
currently no
context help |
open the ribbon's
split button menu |
button 1 press
on split button |
- |
any button press
on split button |
|
close the ribbon's
split button menu |
delayed button 1
press on appropriate
split button |
- |
delayed any button
press on appropriate
split button |
|
close the ribbon's
split button menu |
any button press
outside the split
button drop-down |
- |
any button press
outside the split
button drop-down |
|
select a ribbon's
split button menu
item |
button 1 release
on menu item |
- |
any button press |
currently no
context help |
tab selection |
button 1 press |
button 1 press |
any button press |
|
ribbon selection |
button 1 press |
- |
any button press |
|
in-place text
editor invocation |
delayed button 1
press after item
selection |
via context menu
of the selected item |
delayed any button
press after item
selection |
delayed button
press is more
common |
in-place text
editor leave |
any button press
outside the in-
place editor |
any button press
outside the in-
place editor |
any button press
outside the in-
place editor |
|
Clipboard usage
Currently the only supported clipboard data type ist STRING (atom _XA_STRING
). See XrwApplicationShell
's attribute region for more atom definitions, if other clipboard data types are to support.
To copy or paste text data (between applications) via the clipboard any widget (but not a gadget) can provide data via XrwApplicationShell.ProvideClipboardText()
or request data via XrwApplicationShell.RequestClipboardText()
. An XrwText
widget's clipboard support looks like this:
{
...
XrwApplicationShell app = this.ApplicationShell;
if (_selectionStart != _selectionEnd && app != null)
{
SetCopyBuffer ();
app.ProvideClipboardText (_surface, e.Event.time);
}
...
}
...
{
...
XrwApplicationShell app = this.ApplicationShell;
if (app != null)
{
app.RequestClipboardText (_surface, e.Event.time);
}
...
}
...
{
...
s = Marshal.PtrToStringAuto (data);
if (target as XrwText != null)
{
(target as XrwText).Paste (s);
}
...
}
Beside the 'legacy X11 compatible' methods XrwApplicationShell.ProvideClipboardText()
and XrwApplicationShell.RequestClipboardText()
, now methods of Windows compatible names but different argument lists support an alternatively 'convenient MS Windows similar' behaviour. To achieve this, the 'legacy X11 compatible' funftionality is wraped by the System.Windows.Clipboard
's methods SetText()
and GetText()
.
Attention: Even if the namespace System.Windows.Clipboard
of the 'convenient MS Windows similar' behaviour implies an application-independent clipboard, X11 doesn't provide an application-independent clipboard - it must be implemented by the windows manager to perform well, not by the application or application programming framework. Therefore a successful copy/paste operation always requires the provider widget and the receiver widget to be alife.
Finally besause of the distributed storage of clipboard data (any widget can be a clipboard data provider) and asynchronous processing of clipboard data (inter-application message processing) on X11 systems (compared to the centralized/monolithic Windows clipboard system) a delegate to inject the result into the requestor must be provided for GetText()
.
{
...
XrwApplicationShell app = this.ApplicationShell;
if (_selectionStart != _selectionEnd && app != null)
{
Clipboard.SetText (Entry.Text);
}
...
XrwApplicationShell app = this.ApplicationShell;
if (app != null)
{
Clipboard.GetText (this.ProcessClipboardPasteToEntry);
}
...
}
...
private void ProcessClipboardPasteToEntry (object result)
{
if (result != null)
{
Entry.Text = result.ToString ();
}
}
...
{
...
s = Marshal.PtrToStringAuto (data);
if (target as XrwApplicationShell != null)
{
System.Windows.ClipboardGetResultDelegate clipboardGetResult =
System.Windows.Clipboard.ClipboardGetResult;
if (clipboardGetResult != null)
clipboardGetResult (s);
}
...
}
History
This article has been split-off from the article Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - Part 1, Basics with the fourth public version of the Roma widget set, version from 13. May 2014.
The fifth public version of the Roma widget set is version from 15. August 2014.
The sixth public version of the Roma widget set is version from 05. October 2014.
The seventh public version of the Roma widget set is version from 14. December 2014.
The eighth public version of the Roma widget set is version from 8. March 2015.
The eighth public version of the Roma widget set is version from 21. October 2015.