/* square.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>
*
* Minimal sample tile widget.
*
* $Id: square.c,v 1.3 2004/12/31 01:32:57 jenglish Exp $
*/
#include <tk.h>
#include "tkTheme.h"
#include "widget.h"
#ifndef DEFAULT_BORDERWIDTH
#define DEFAULT_BORDERWIDTH "2"
#endif
/*
* First, we setup the widget record. The Tile package provides a structure
* that contains standard widget data so it is only necessary to define
* a structure that holds the data required for our widget. We do this by
* defining a widget part and then specifying the widget record as the
* concatenation of the two structures.
*/
typedef struct
{
Tcl_Obj *widthObj;
Tcl_Obj *heightObj;
Tcl_Obj *reliefObj;
Tcl_Obj *borderWidthObj;
Tcl_Obj *foregroundObj;
Tcl_Obj *paddingObj;
Tcl_Obj *anchorObj;
} SquarePart;
typedef struct
{
WidgetCore core;
SquarePart square;
} Square;
/*
* Widget options.
*
*/
static Tk_OptionSpec SquareOptionSpecs[] =
{
WIDGET_TAKES_FOCUS,
{TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
DEFAULT_BORDERWIDTH, Tk_Offset(Square,square.borderWidthObj), -1,
0,0,GEOMETRY_CHANGED },
{TK_OPTION_BORDER, "-foreground", "foreground", "Foreground",
"#eeaaaa", Tk_Offset(Square,square.foregroundObj),
-1, 0, 0, 0},
{TK_OPTION_PIXELS, "-width", "width", "Width",
"50", Tk_Offset(Square,square.widthObj), -1, 0, 0,
GEOMETRY_CHANGED},
{TK_OPTION_PIXELS, "-height", "height", "Height",
"50", Tk_Offset(Square,square.heightObj), -1, 0, 0,
GEOMETRY_CHANGED},
{TK_OPTION_STRING, "-padding", "padding", "Pad", NULL,
Tk_Offset(Square,square.paddingObj), -1,
TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
{TK_OPTION_RELIEF, "-relief", "relief", "Relief",
NULL, Tk_Offset(Square,square.reliefObj), -1, TK_OPTION_NULL_OK, 0, 0},
{TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
NULL, Tk_Offset(Square,square.anchorObj), -1, TK_OPTION_NULL_OK, 0, 0},
WIDGET_INHERIT_OPTIONS(CoreOptionSpecs)
};
/*
* Widget commands:
*/
static WidgetCommandSpec SquareCommands[] =
{
{ "configure", WidgetConfigureCommand },
{ "cget", WidgetCgetCommand },
{ "instate", WidgetInstateCommand },
{ "state", WidgetStateCommand },
{ NULL, NULL }
};
/*
* Widget specification:
*/
WidgetSpec SquareWidgetSpec =
{
"TSquare", /* className */
sizeof(Square), /* recordSize */
SquareOptionSpecs, /* optionSpecs */
SquareCommands, /* subcommands */
NullInitialize, /* initializeProc */
NullCleanup, /* cleanupProc */
CoreConfigure, /* configureProc */
NullPostConfigure, /* postConfigureProc */
WidgetGetLayout, /* getLayoutProc */
WidgetSize, /* sizeProc */
WidgetDoLayout, /* layoutProc */
WidgetDisplay /* displayProc */
};
/* ----------------------------------------------------------------------
* Square element
*
* In this section we demonstrate what is required to create a new themed
* element.
*/
typedef struct
{
Tcl_Obj *borderObj;
Tcl_Obj *foregroundObj;
Tcl_Obj *borderWidthObj;
Tcl_Obj *reliefObj;
Tcl_Obj *widthObj;
Tcl_Obj *heightObj;
} SquareElement;
static Ttk_ElementOptionSpec SquareElementOptions[] =
{
{ "-background", TK_OPTION_BORDER, Tk_Offset(SquareElement,borderObj),
DEFAULT_BACKGROUND },
{ "-foreground", TK_OPTION_BORDER, Tk_Offset(SquareElement,foregroundObj),
DEFAULT_BACKGROUND },
{ "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(SquareElement,borderWidthObj),
DEFAULT_BORDERWIDTH },
{ "-relief", TK_OPTION_RELIEF, Tk_Offset(SquareElement,reliefObj),
"flat" },
{ "-width", TK_OPTION_PIXELS, Tk_Offset(SquareElement,widthObj), "20"},
{ "-height", TK_OPTION_PIXELS, Tk_Offset(SquareElement,heightObj), "20"},
{ NULL }
};
static void
SquareElementGeometry(
void *clientData, void *elementRecord,
Tk_Window tkwin, int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
SquareElement *square = elementRecord;
int borderWidth = 0;
Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth);
*paddingPtr = Ttk_UniformPadding((short)borderWidth);
Tk_GetPixelsFromObj(NULL, tkwin, square->widthObj, widthPtr);
Tk_GetPixelsFromObj(NULL, tkwin, square->heightObj, heightPtr);
}
static void
SquareElementDraw(void *clientData, void *elementRecord,
Tk_Window tkwin, Drawable d, Ttk_Box b, unsigned int state)
{
SquareElement *square = elementRecord;
Tk_3DBorder border = NULL, foreground = NULL;
int borderWidth = 1, relief = TK_RELIEF_FLAT;
border = Tk_Get3DBorderFromObj(tkwin, square->borderObj);
foreground = Tk_Get3DBorderFromObj(tkwin, square->foregroundObj);
Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth);
Tk_GetReliefFromObj(NULL, square->reliefObj, &relief);
Tk_Fill3DRectangle(tkwin, d, foreground,
b.x, b.y, b.width, b.height, borderWidth, relief);
}
static Ttk_ElementSpec SquareElementSpec =
{
TK_STYLE_VERSION_2,
sizeof(SquareElement),
SquareElementOptions,
SquareElementGeometry,
SquareElementDraw
};
/* ----------------------------------------------------------------------
*
* Layout section.
*
* Every widget class needs a layout style that specifies which elements
* are part of the widget and how they should be placed. The element layout
* engine is similar to the Tk pack geometry manager. Read the documentation
* for the details. In this example we just need to have the square element
* that has been defined for this widget placed on a background. We will
* also need some padding to keep it away from the edges.
*/
TTK_BEGIN_LAYOUT(SquareLayout)
TTK_NODE("Square.background", TTK_FILL_BOTH)
TTK_GROUP("Square.padding", TTK_FILL_BOTH,
TTK_NODE("Square.square", TTK_PACK_TOP))
TTK_END_LAYOUT
/* ----------------------------------------------------------------------
*
* Widget initialization.
*
* This file defines a new element and a new widget. We need to register
* the element with the themes that will need it. In this case we will
* register with the default theme that is the root of the theme inheritance
* tree. This means all themes will find this element.
* We then need to register the widget class style. This is the layout
* specification. If a different theme requires an alternative layout, we
* could register that here. For instance, in some themes the scrollbars have
* one uparrow, in other themes there are two uparrow elements.
* Finally we register the widget itself. This step creates a tcl command so
* that we can actually create an instance of this class. The widget is
* linked to a particular style by the widget class name. This is important
* to realise as the programmer may change the classname when creating a
* new instance. If this is done, a new layout will need to be created (which
* can be done at script level). Some widgets may require particular elements
* to be present but we try to avoid this where possible. In this widget's C
* code, no reference is made to any particular elements. The programmer is
* free to specify a new style using completely different elements.
*/
/* public */ int
SquareWidget_Init(Tcl_Interp *interp)
{
Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
/* register the new elements for this theme engine */
Ttk_RegisterElement(interp, theme, "square", &SquareElementSpec, NULL);
/* register the layout for this theme */
Ttk_RegisterLayout(theme, "TSquare", SquareLayout);
/* register the widget */
RegisterWidget(interp, "ttk::square", &SquareWidgetSpec);
return TCL_OK;
}