1/* square.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net> 2 * 3 * Minimal sample ttk widget. 4 * 5 * $Id$ 6 */ 7 8#include <tk.h> 9#include "ttkTheme.h" 10#include "ttkWidget.h" 11 12#if defined(TTK_SQUARE_WIDGET) || 1 13 14#ifndef DEFAULT_BORDERWIDTH 15#define DEFAULT_BORDERWIDTH "2" 16#endif 17 18/* 19 * First, we setup the widget record. The Ttk package provides a structure 20 * that contains standard widget data so it is only necessary to define 21 * a structure that holds the data required for our widget. We do this by 22 * defining a widget part and then specifying the widget record as the 23 * concatenation of the two structures. 24 */ 25 26typedef struct 27{ 28 Tcl_Obj *widthObj; 29 Tcl_Obj *heightObj; 30 Tcl_Obj *reliefObj; 31 Tcl_Obj *borderWidthObj; 32 Tcl_Obj *foregroundObj; 33 Tcl_Obj *paddingObj; 34 Tcl_Obj *anchorObj; 35} SquarePart; 36 37typedef struct 38{ 39 WidgetCore core; 40 SquarePart square; 41} Square; 42 43/* 44 * Widget options. 45 * 46 * This structure is the same as the option specification structure used 47 * for Tk widgets. For each option we provide the type, name and options 48 * database name and class name and the position in the structure and 49 * default values. At the bottom we bring in the standard widget option 50 * defined for all widgets. 51 */ 52 53static Tk_OptionSpec SquareOptionSpecs[] = 54{ 55 WIDGET_TAKES_FOCUS, 56 57 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 58 DEFAULT_BORDERWIDTH, Tk_Offset(Square,square.borderWidthObj), -1, 59 0,0,GEOMETRY_CHANGED }, 60 {TK_OPTION_BORDER, "-foreground", "foreground", "Foreground", 61 DEFAULT_BACKGROUND, Tk_Offset(Square,square.foregroundObj), 62 -1, 0, 0, 0}, 63 64 {TK_OPTION_PIXELS, "-width", "width", "Width", 65 "50", Tk_Offset(Square,square.widthObj), -1, 0, 0, 66 GEOMETRY_CHANGED}, 67 {TK_OPTION_PIXELS, "-height", "height", "Height", 68 "50", Tk_Offset(Square,square.heightObj), -1, 0, 0, 69 GEOMETRY_CHANGED}, 70 71 {TK_OPTION_STRING, "-padding", "padding", "Pad", NULL, 72 Tk_Offset(Square,square.paddingObj), -1, 73 TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, 74 75 {TK_OPTION_RELIEF, "-relief", "relief", "Relief", 76 NULL, Tk_Offset(Square,square.reliefObj), -1, TK_OPTION_NULL_OK, 0, 0}, 77 78 {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", 79 NULL, Tk_Offset(Square,square.anchorObj), -1, TK_OPTION_NULL_OK, 0, 0}, 80 81 WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs) 82}; 83 84/* 85 * Almost all of the widget functionality is handled by the default Ttk 86 * widget code and the contained element. The one thing that we must handle 87 * is the -anchor option which positions the square element within the parcel 88 * of space available for the widget. 89 * To do this we must find out the layout preferences for the square 90 * element and adjust its position within our region. 91 * 92 * Note that if we do not have a "square" elememt then just the default 93 * layout will be done. So if someone places a label element into the 94 * widget layout it will still be handled but the -anchor option will be 95 * passed onto the label element instead of handled here. 96 */ 97 98static void 99SquareDoLayout(void *clientData) 100{ 101 WidgetCore *corePtr = (WidgetCore *)clientData; 102 Ttk_Box winBox; 103 Ttk_Element squareNode; 104 105 squareNode = Ttk_FindElement(corePtr->layout, "square"); 106 winBox = Ttk_WinBox(corePtr->tkwin); 107 Ttk_PlaceLayout(corePtr->layout, corePtr->state, winBox); 108 109 /* 110 * Adjust the position of the square element within the widget according 111 * to the -anchor option. 112 */ 113 114 if (squareNode) { 115 Square *squarePtr = clientData; 116 Tk_Anchor anchor = TK_ANCHOR_CENTER; 117 Ttk_Box b; 118 119 b = Ttk_ElementParcel(squareNode); 120 if (squarePtr->square.anchorObj != NULL) 121 Tk_GetAnchorFromObj(NULL, squarePtr->square.anchorObj, &anchor); 122 b = Ttk_AnchorBox(winBox, b.width, b.height, anchor); 123 124 Ttk_PlaceElement(corePtr->layout, squareNode, b); 125 } 126} 127 128/* 129 * Widget commands. A widget is impelemented as an ensemble and the 130 * subcommands are listed here. Ttk provides default implementations 131 * that are sufficient for our needs. 132 */ 133 134static const Ttk_Ensemble SquareCommands[] = { 135 { "configure", TtkWidgetConfigureCommand,0 }, 136 { "cget", TtkWidgetCgetCommand,0 }, 137 { "identify", TtkWidgetIdentifyCommand,0 }, 138 { "instate", TtkWidgetInstateCommand,0 }, 139 { "state", TtkWidgetStateCommand,0 }, 140 { 0,0,0 } 141}; 142 143/* 144 * The Widget specification structure holds all the implementation 145 * information about this widget and this is what must be registered 146 * with Tk in the package initialization code (see bottom). 147 */ 148 149static WidgetSpec SquareWidgetSpec = 150{ 151 "TSquare", /* className */ 152 sizeof(Square), /* recordSize */ 153 SquareOptionSpecs, /* optionSpecs */ 154 SquareCommands, /* subcommands */ 155 TtkNullInitialize, /* initializeProc */ 156 TtkNullCleanup, /* cleanupProc */ 157 TtkCoreConfigure, /* configureProc */ 158 TtkNullPostConfigure, /* postConfigureProc */ 159 TtkWidgetGetLayout, /* getLayoutProc */ 160 TtkWidgetSize, /* sizeProc */ 161 SquareDoLayout, /* layoutProc */ 162 TtkWidgetDisplay /* displayProc */ 163}; 164 165/* ---------------------------------------------------------------------- 166 * Square element 167 * 168 * In this section we demonstrate what is required to create a new themed 169 * element. 170 */ 171 172typedef struct 173{ 174 Tcl_Obj *borderObj; 175 Tcl_Obj *foregroundObj; 176 Tcl_Obj *borderWidthObj; 177 Tcl_Obj *reliefObj; 178 Tcl_Obj *widthObj; 179 Tcl_Obj *heightObj; 180} SquareElement; 181 182static Ttk_ElementOptionSpec SquareElementOptions[] = 183{ 184 { "-background", TK_OPTION_BORDER, Tk_Offset(SquareElement,borderObj), 185 DEFAULT_BACKGROUND }, 186 { "-foreground", TK_OPTION_BORDER, Tk_Offset(SquareElement,foregroundObj), 187 DEFAULT_BACKGROUND }, 188 { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(SquareElement,borderWidthObj), 189 DEFAULT_BORDERWIDTH }, 190 { "-relief", TK_OPTION_RELIEF, Tk_Offset(SquareElement,reliefObj), 191 "raised" }, 192 { "-width", TK_OPTION_PIXELS, Tk_Offset(SquareElement,widthObj), "20"}, 193 { "-height", TK_OPTION_PIXELS, Tk_Offset(SquareElement,heightObj), "20"}, 194 { NULL, 0, 0, NULL } 195}; 196 197/* 198 * The element geometry function is called when the layout code wishes to 199 * find out how big this element wants to be. We must return our preferred 200 * size and padding information 201 */ 202 203static void SquareElementSize( 204 void *clientData, void *elementRecord, Tk_Window tkwin, 205 int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) 206{ 207 SquareElement *square = elementRecord; 208 int borderWidth = 0; 209 210 Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth); 211 *paddingPtr = Ttk_UniformPadding((short)borderWidth); 212 Tk_GetPixelsFromObj(NULL, tkwin, square->widthObj, widthPtr); 213 Tk_GetPixelsFromObj(NULL, tkwin, square->heightObj, heightPtr); 214} 215 216/* 217 * Draw the element in the box provided. 218 */ 219 220static void SquareElementDraw( 221 void *clientData, void *elementRecord, Tk_Window tkwin, 222 Drawable d, Ttk_Box b, unsigned int state) 223{ 224 SquareElement *square = elementRecord; 225 Tk_3DBorder border = NULL, foreground = NULL; 226 int borderWidth = 1, relief = TK_RELIEF_FLAT; 227 228 border = Tk_Get3DBorderFromObj(tkwin, square->borderObj); 229 foreground = Tk_Get3DBorderFromObj(tkwin, square->foregroundObj); 230 Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth); 231 Tk_GetReliefFromObj(NULL, square->reliefObj, &relief); 232 233 Tk_Fill3DRectangle(tkwin, d, foreground, 234 b.x, b.y, b.width, b.height, borderWidth, relief); 235} 236 237static Ttk_ElementSpec SquareElementSpec = 238{ 239 TK_STYLE_VERSION_2, 240 sizeof(SquareElement), 241 SquareElementOptions, 242 SquareElementSize, 243 SquareElementDraw 244}; 245 246/* ---------------------------------------------------------------------- 247 * 248 * Layout section. 249 * 250 * Every widget class needs a layout style that specifies which elements 251 * are part of the widget and how they should be placed. The element layout 252 * engine is similar to the Tk pack geometry manager. Read the documentation 253 * for the details. In this example we just need to have the square element 254 * that has been defined for this widget placed on a background. We will 255 * also need some padding to keep it away from the edges. 256 */ 257 258TTK_BEGIN_LAYOUT(SquareLayout) 259 TTK_NODE("Square.background", TTK_FILL_BOTH) 260 TTK_GROUP("Square.padding", TTK_FILL_BOTH, 261 TTK_NODE("Square.square", 0)) 262TTK_END_LAYOUT 263 264/* ---------------------------------------------------------------------- 265 * 266 * Widget initialization. 267 * 268 * This file defines a new element and a new widget. We need to register 269 * the element with the themes that will need it. In this case we will 270 * register with the default theme that is the root of the theme inheritance 271 * tree. This means all themes will find this element. 272 * We then need to register the widget class style. This is the layout 273 * specification. If a different theme requires an alternative layout, we 274 * could register that here. For instance, in some themes the scrollbars have 275 * one uparrow, in other themes there are two uparrow elements. 276 * Finally we register the widget itself. This step creates a tcl command so 277 * that we can actually create an instance of this class. The widget is 278 * linked to a particular style by the widget class name. This is important 279 * to realise as the programmer may change the classname when creating a 280 * new instance. If this is done, a new layout will need to be created (which 281 * can be done at script level). Some widgets may require particular elements 282 * to be present but we try to avoid this where possible. In this widget's C 283 * code, no reference is made to any particular elements. The programmer is 284 * free to specify a new style using completely different elements. 285 */ 286 287/* public */ MODULE_SCOPE int 288TtkSquareWidget_Init(Tcl_Interp *interp) 289{ 290 Ttk_Theme theme = Ttk_GetDefaultTheme(interp); 291 292 /* register the new elements for this theme engine */ 293 Ttk_RegisterElement(interp, theme, "square", &SquareElementSpec, NULL); 294 295 /* register the layout for this theme */ 296 Ttk_RegisterLayout(theme, "TSquare", SquareLayout); 297 298 /* register the widget */ 299 RegisterWidget(interp, "ttk::square", &SquareWidgetSpec); 300 301 return TCL_OK; 302} 303 304#endif /* TTK_SQUARE_WIDGET */ 305 306