summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog17
-rw-r--r--doc/ttk_spinbox.n88
-rw-r--r--generic/ttk/ttkEntry.c418
-rw-r--r--library/ttk/altTheme.tcl7
-rw-r--r--library/ttk/clamTheme.tcl10
-rw-r--r--library/ttk/classicTheme.tcl6
-rw-r--r--library/ttk/defaults.tcl7
-rw-r--r--library/ttk/entry.tcl4
-rw-r--r--library/ttk/spinbox.tcl121
-rw-r--r--library/ttk/ttk.tcl3
-rw-r--r--library/ttk/winTheme.tcl4
-rw-r--r--library/ttk/xpTheme.tcl22
-rw-r--r--tests/ttk/spinbox.test239
-rw-r--r--win/ttkWinTheme.c22
-rw-r--r--win/ttkWinXPTheme.c48
15 files changed, 933 insertions, 83 deletions
diff --git a/ChangeLog b/ChangeLog
index c403c4d..ce74f34 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2008-11-01 Pat Thoyts <patthoyts@users.sourceforge.net>
+
+ * generic/ttk/ttkEntry.c: Implemented the themed spinbox
+ * library/ttk/altTheme.tcl: widget.
+ * library/ttk/clamTheme.tcl:
+ * library/ttk/classicTheme.tcl:
+ * library/ttk/defaults.tcl:
+ * library/ttk/entry.tcl:
+ * library/ttk/ttk.tcl:
+ * library/ttk/winTheme.tcl:
+ * library/ttk/xpTheme.tcl:
+ * library/ttk/spinbox.tcl:
+ * win/ttkWinTheme.c:
+ * win/ttkWinXPTheme.c:
+ * doc/ttk_spinbox.n:
+ * tests/ttk/spinbox.test:
+
2008-10-31 Joe English <jenglish@users.sourceforge.net>
* generic/widget.c: Temporary workaround for [Bug 2207435]
diff --git a/doc/ttk_spinbox.n b/doc/ttk_spinbox.n
new file mode 100644
index 0000000..350cfed
--- /dev/null
+++ b/doc/ttk_spinbox.n
@@ -0,0 +1,88 @@
+'\"
+'\" Copyright (c) 2008 Pat Thoyts
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: ttk_spinbox.n,v 1.1 2008/11/01 15:34:24 patthoyts Exp $
+'\"
+.so man.macros
+.TH ttk::spinbox n 8.5 Tk "Tk Themed Widget"
+.BS
+.SH NAME
+ttk::spinbox \- Selecting text field widget
+.SH SYNOPSIS
+\fBttk::spinbox\fR \fIpathName \fR?\fIoptions\fR?
+.BE
+.SH DESCRIPTION
+.PP
+A \fBttk::spinbox\fR widget is a \fBttk::entry\fR widget with built-in
+up and down buttons that are used to either modify a numeric value or
+to select among a set of values. The widget implements all the features
+of the \fBttk::entry\fR widget including support of the
+\fB\-textvariable\fR option to link the value displayed by the widget
+to a Tcl variable.
+.SO ttk_widget
+\-class \-cursor \-style
+\-takefocus \-xscrollcommand
+.SE
+.SO ttk::entry
+\-validate \-validatecommand
+.SE
+.SH "WIDGET-SPECIFIC OPTIONS"
+.OP \-from from From
+A floating\-point value specifying the lowest value for the spinbox. This is
+used in conjunction with \fI\-to\fR and \fI\-increment\fR to set a numerical
+range.
+.OP \-to to To
+A floating\-point value specifying the highest permissible value for the
+widget. See also \fI\-from\fR and \fI\-increment\fR.
+range.
+.OP \-increment increment Increment
+A floating\-point value specifying the change in value to be applied each
+time one of the widget spin buttons is pressed. The up button applies a
+positive increment, the down button applies a negative increment.
+.OP \-values values Values
+This must be a Tcl list of values. If this option is set then this will
+override any range set using the \fI\-from\fR, \fI\-to\fR and
+\fI\-increment\fR options. The widget will instead use the values
+specified beginning with the first value.
+.OP \-wrap wrap Wrap
+Must be a proper boolean value. If on, the spinbox will wrap around the
+values of data in the widget.
+.OP \-format format Format
+Specifies an alternate format to use when setting the string value
+when using the \fB\-from\fR and \fB\-to\fR range.
+This must be a format specifier of the form \fB%<pad>.<pad>f\fR,
+as it will format a floating-point number.
+.OP \-command command Command
+Specifies a Tcl command to be invoked whenever a spinbutton is invoked.
+.SH "INDICES"
+.PP
+See the \fBttk::entry\fR manual for information about indexing characters.
+.SH "VALIDATION"
+.PP
+See the \fBttk::entry\fR manual for information about using the
+\fI\-validate\fR and \fI\-validatecommand\fR options.
+.SH "WIDGET COMMAND"
+.PP
+The following subcommands are possible for spinbox widgets in addition to
+the commands described for the \fBttk::entry\fR widget:
+.TP
+\fIpathName \fBcurrent \fIindex\fR
+.TP
+\fIpathName \fBget\fR
+Returns the spinbox's current value.
+.TP
+\fIpathName \fBset \fIvalue\fR
+Set the spinbox string to \fIvalue\fR. If a \fI\-format\fR option has
+been configured then this format will be applied. If formatting fails
+or is not set or the \fI\-values\fR option has been used then the value
+is set directly.
+.SH "SEE ALSO"
+ttk::widget(n), ttk::entry(n), spinbox(n)
+.SH KEYWORDS
+entry, spinbox, widget, text field
+'\" Local Variables:
+'\" mode: nroff
+'\" End:
diff --git a/generic/ttk/ttkEntry.c b/generic/ttk/ttkEntry.c
index d41010d..7397dfb 100644
--- a/generic/ttk/ttkEntry.c
+++ b/generic/ttk/ttkEntry.c
@@ -1,5 +1,5 @@
/*
- * $Id: ttkEntry.c,v 1.9 2007/05/18 21:46:11 jenglish Exp $
+ * $Id: ttkEntry.c,v 1.10 2008/11/01 15:34:24 patthoyts Exp $
*
* DERIVED FROM: tk/generic/tkEntry.c r1.35.
*
@@ -13,6 +13,7 @@
#include <string.h>
#include <tk.h>
#include <X11/Xatom.h>
+#include <math.h>
#include "ttkTheme.h"
#include "ttkWidget.h"
@@ -26,6 +27,12 @@
#define VALIDATION_SET_VALUE (WIDGET_USER_FLAG<<4)
/*
+ * inline equality test for doubles
+ */
+#define MIN_DBL_VAL 1E-9
+#define DOUBLES_EQ(d1, d2) (fabs((d1) - (d2)) < MIN_DBL_VAL)
+
+/*
* Definitions for -validate option values:
*/
typedef enum validateMode {
@@ -144,9 +151,12 @@ typedef struct
/*
* Extra mask bits for Tk_SetOptions()
*/
-#define STATE_CHANGED (0x100) /* -state option changed */
-#define TEXTVAR_CHANGED (0x200) /* -textvariable option changed */
-#define SCROLLCMD_CHANGED (0x400) /* -xscrollcommand option changed */
+#define STATE_CHANGED (0x0100) /* -state option changed */
+#define TEXTVAR_CHANGED (0x0200) /* -textvariable option changed */
+#define SCROLLCMD_CHANGED (0x0400) /* -xscrollcommand option changed */
+#define VALUES_CHANGED (0x0800) /* -values option changed */
+#define FORMAT_CHANGED (0x1000) /* -format option changed (spinbox) */
+#define RANGE_CHANGED (0x2000) /* -from or -to option changed */
/*
* Default option values:
@@ -1679,19 +1689,127 @@ static WidgetSpec EntryWidgetSpec =
};
/*------------------------------------------------------------------------
+ * +++ Values entry widget widget record.
+ *
+ * This record and the command function are shared by the combobox and
+ * spinbox which both have support for a -values option and [current]
+ * command.
+ */
+
+typedef struct {
+ Tcl_Obj *valuesObj;
+ int currentIndex;
+} ValuesPart;
+
+typedef struct {
+ WidgetCore core;
+ EntryPart entry;
+ ValuesPart values;
+} Values;
+
+#define ENTRY_VALUES_OPTION \
+ {TK_OPTION_STRING, "-values", "values", "Values", \
+ "", Tk_Offset(Values, values.valuesObj), -1, 0, 0, VALUES_CHANGED}
+
+static int
+ValuesInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Values *valPtr = recordPtr;
+ valPtr->values.currentIndex = -1;
+ return TCL_OK;
+}
+
+static int
+ValuesValidate(Tcl_Interp *interp, void *recordPtr, int *indexPtr)
+{
+ Values *valPtr = recordPtr;
+ int currentIndex = valPtr->values.currentIndex;
+ const char *currentValue = valPtr->entry.string;
+ int eltc;
+ Tcl_Obj **eltv;
+
+
+ if (Tcl_ListObjLength(interp,valPtr->values.valuesObj,&eltc) != TCL_OK)
+ return TCL_ERROR;
+
+ Tcl_ListObjGetElements(interp,valPtr->values.valuesObj,&eltc,&eltv);
+
+ if ( currentIndex < 0
+ || currentIndex >= eltc
+ || strcmp(currentValue, Tcl_GetString(eltv[currentIndex]))) {
+ /*
+ * Not valid. Check current value against each element in -values:
+ */
+ for (currentIndex = 0; currentIndex < eltc; ++currentIndex) {
+ if (!strcmp(currentValue,Tcl_GetString(eltv[currentIndex]))) {
+ break;
+ }
+ }
+ if (currentIndex >= eltc) {
+ /* Not found */
+ currentIndex = -1;
+ }
+ }
+ *indexPtr = currentIndex;
+ return TCL_OK;
+}
+
+/* $widget current ?newIndex? -- get or set current index.
+ * Setting the current index updates the combobox value,
+ * but the value and -values may be changed independently
+ * of the index. Instead of trying to keep currentIndex
+ * in sync at all times, [$cb current] double-checks
+ */
+static int ValuesCurrentCommand(
+ Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
+{
+ Values *valPtr = recordPtr;
+ int currentIndex = valPtr->values.currentIndex;
+ const char *currentValue = valPtr->entry.string;
+ int nValues;
+ Tcl_Obj **values;
+
+ Tcl_ListObjGetElements(interp,valPtr->values.valuesObj,&nValues,&values);
+
+ if (objc == 2) {
+ if (ValuesValidate(interp, recordPtr, &currentIndex) != TCL_OK)
+ return TCL_ERROR;
+ valPtr->values.currentIndex = currentIndex;
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(currentIndex));
+ } else if (objc == 3) {
+ if (Tcl_GetIntFromObj(interp, objv[2], &currentIndex) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (currentIndex < 0 || currentIndex >= nValues) {
+ Tcl_AppendResult(interp,
+ "Index ", Tcl_GetString(objv[2]), " out of range",
+ NULL);
+ return TCL_ERROR;
+ }
+
+ valPtr->values.currentIndex = currentIndex;
+
+ return EntrySetValue(recordPtr, Tcl_GetString(values[currentIndex]));
+ } else {
+ Tcl_WrongNumArgs(interp, 2, objv, "?newIndex?");
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*------------------------------------------------------------------------
* +++ Combobox widget record.
*/
typedef struct {
Tcl_Obj *postCommandObj;
- Tcl_Obj *valuesObj;
Tcl_Obj *heightObj;
- int currentIndex;
} ComboboxPart;
typedef struct {
WidgetCore core;
EntryPart entry;
+ ValuesPart values;
ComboboxPart combobox;
} Combobox;
@@ -1703,9 +1821,7 @@ static Tk_OptionSpec ComboboxOptionSpecs[] =
{TK_OPTION_STRING, "-postcommand", "postCommand", "PostCommand",
"", Tk_Offset(Combobox, combobox.postCommandObj), -1,
0,0,0 },
- {TK_OPTION_STRING, "-values", "values", "Values",
- "", Tk_Offset(Combobox, combobox.valuesObj), -1,
- 0,0,0 },
+ ENTRY_VALUES_OPTION,
WIDGET_INHERIT_OPTIONS(EntryOptionSpecs)
};
@@ -1716,8 +1832,8 @@ static int
ComboboxInitialize(Tcl_Interp *interp, void *recordPtr)
{
Combobox *cb = recordPtr;
- cb->combobox.currentIndex = -1;
TtkTrackElementState(&cb->core);
+ ValuesInitialize(interp, recordPtr);
return EntryInitialize(interp, recordPtr);
}
@@ -1728,86 +1844,241 @@ static int
ComboboxConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
{
Combobox *cbPtr = recordPtr;
- int unused;
+ int currentIndex = 0;
/* Make sure -values is a valid list:
*/
- if (Tcl_ListObjLength(interp,cbPtr->combobox.valuesObj,&unused) != TCL_OK)
- return TCL_ERROR;
+ if (mask & VALUES_CHANGED) {
+ if (ValuesValidate(interp, recordPtr, &currentIndex) != TCL_OK)
+ return TCL_ERROR;
+ cbPtr->values.currentIndex = currentIndex;
+ }
return EntryConfigure(interp, recordPtr, mask);
}
-/* $cb current ?newIndex? -- get or set current index.
- * Setting the current index updates the combobox value,
- * but the value and -values may be changed independently
- * of the index. Instead of trying to keep currentIndex
- * in sync at all times, [$cb current] double-checks
+/*------------------------------------------------------------------------
+ * +++ Combobox widget definition.
*/
-static int ComboboxCurrentCommand(
- Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
+static WidgetCommandSpec ComboboxCommands[] =
{
- Combobox *cbPtr = recordPtr;
- int currentIndex = cbPtr->combobox.currentIndex;
- const char *currentValue = cbPtr->entry.string;
- int nValues;
- Tcl_Obj **values;
+ { "bbox", EntryBBoxCommand },
+ { "cget", TtkWidgetCgetCommand },
+ { "configure", TtkWidgetConfigureCommand },
+ { "current", ValuesCurrentCommand },
+ { "delete", EntryDeleteCommand },
+ { "get", EntryGetCommand },
+ { "icursor", EntryICursorCommand },
+ { "identify", TtkWidgetIdentifyCommand },
+ { "index", EntryIndexCommand },
+ { "insert", EntryInsertCommand },
+ { "instate", TtkWidgetInstateCommand },
+ { "selection", EntrySelectionCommand },
+ { "state", TtkWidgetStateCommand },
+ { "set", EntrySetCommand },
+ { "xview", EntryXViewCommand },
+ {0,0}
+};
- Tcl_ListObjGetElements(interp,cbPtr->combobox.valuesObj,&nValues,&values);
+static WidgetSpec ComboboxWidgetSpec =
+{
+ "TCombobox", /* className */
+ sizeof(Combobox), /* recordSize */
+ ComboboxOptionSpecs, /* optionSpecs */
+ ComboboxCommands, /* subcommands */
+ ComboboxInitialize, /* initializeProc */
+ EntryCleanup, /* cleanupProc */
+ ComboboxConfigure, /* configureProc */
+ EntryPostConfigure, /* postConfigureProc */
+ TtkWidgetGetLayout, /* getLayoutProc */
+ TtkWidgetSize, /* sizeProc */
+ EntryDoLayout, /* layoutProc */
+ EntryDisplay /* displayProc */
+};
- if (objc == 2) {
- /* Check if currentIndex still valid:
- */
- if ( currentIndex < 0
- || currentIndex >= nValues
- || strcmp(currentValue,Tcl_GetString(values[currentIndex]))
- )
- {
- /* Not valid. Check current value against each element in -values:
- */
- for (currentIndex = 0; currentIndex < nValues; ++currentIndex) {
- if (!strcmp(currentValue,Tcl_GetString(values[currentIndex]))) {
- break;
- }
+/*------------------------------------------------------------------------
+ * +++ Spinbox widget record.
+ */
+
+typedef struct {
+ Tcl_Obj *commandObj;
+ double fromValue;
+ double toValue;
+ int valueCount;
+ Tcl_Obj *formatObj;
+ Tcl_Obj *incrementObj;
+ Tcl_Obj *wrapObj;
+ char formatBuffer[TCL_INTEGER_SPACE + 4];
+} SpinboxPart;
+
+typedef struct {
+ WidgetCore core;
+ EntryPart entry;
+ ValuesPart values;
+ SpinboxPart spinbox;
+} Spinbox;
+
+static Tk_OptionSpec SpinboxOptionSpecs[] =
+{
+ {TK_OPTION_STRING, "-command", "command", "Command",
+ "", Tk_Offset(Spinbox, spinbox.commandObj), -1,
+ 0,0,0 },
+ {TK_OPTION_STRING, "-format", "format", "Format", "",
+ Tk_Offset(Spinbox, spinbox.formatObj), -1, 0, 0, FORMAT_CHANGED },
+ {TK_OPTION_DOUBLE, "-from", "from", "From", "0",
+ -1, Tk_Offset(Spinbox,spinbox.fromValue), 0, 0, RANGE_CHANGED },
+ {TK_OPTION_DOUBLE, "-to", "to", "To", "0",
+ -1, Tk_Offset(Spinbox,spinbox.toValue), 0, 0, RANGE_CHANGED },
+ {TK_OPTION_DOUBLE, "-increment", "increment", "Increment", "1",
+ Tk_Offset(Spinbox,spinbox.incrementObj), -1, 0, 0, 0},
+ {TK_OPTION_BOOLEAN, "-wrap", "wrap", "Wrap", "0",
+ Tk_Offset(Spinbox,spinbox.wrapObj), -1, 0, 0, 0},
+ ENTRY_VALUES_OPTION,
+ WIDGET_INHERIT_OPTIONS(EntryOptionSpecs)
+};
+
+/*
+ * SpinboxInitialize --
+ * Initialization hook for spinbox widgets.
+ */
+
+static int
+SpinboxInitialize(Tcl_Interp *interp, void *recordPtr)
+{
+ Spinbox *sbPtr = recordPtr;
+ sbPtr->spinbox.valueCount = 0;
+ TtkTrackElementState(&sbPtr->core);
+ ValuesInitialize(interp, recordPtr);
+ return EntryInitialize(interp, recordPtr);
+}
+
+/*
+ * If the format option has not been set manually then we calculate
+ * an appropriate format here based upon the -from and -to options
+ * If -values has been set this is not called.
+ */
+
+static const char *
+SpinboxCalculateFormat(Spinbox *sbPtr)
+{
+ const char *formatString = "%f";
+ if (Tcl_GetCharLength(sbPtr->spinbox.formatObj) > 0) {
+ formatString = Tcl_GetString(sbPtr->spinbox.formatObj);
+ } else {
+ double increment = 0;
+ int len;
+ if (Tcl_GetDoubleFromObj(NULL, sbPtr->spinbox.incrementObj, &increment) != TCL_OK)
+ increment = 1;
+ len = (int)floor(fabs(log10(fabs(increment))));
+ sprintf(sbPtr->spinbox.formatBuffer, "%%.%df", len);
+ formatString = sbPtr->spinbox.formatBuffer;
+ }
+ return formatString;
+}
+
+/* SpinboxConfigure --
+ * Configuration hook for spinbox widgets.
+ */
+static int
+SpinboxConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
+{
+ Spinbox *sbPtr = recordPtr;
+ int currentIndex = 0, needsUpdate = 0;
+ double d = 0;
+
+ needsUpdate = (mask & FORMAT_CHANGED);
+
+ if (mask & RANGE_CHANGED) {
+ if (Tcl_GetDouble(NULL, sbPtr->entry.string, &d) != TCL_OK) {
+ d = sbPtr->spinbox.fromValue;
+ needsUpdate = 1;
+ } else {
+ if (d < sbPtr->spinbox.fromValue) {
+ d = sbPtr->spinbox.fromValue;
+ needsUpdate = 1;
}
- if (currentIndex >= nValues) {
- /* Not found */
- currentIndex = -1;
+ if (d > sbPtr->spinbox.toValue) {
+ d = sbPtr->spinbox.toValue;
+ needsUpdate = 1;
}
}
- cbPtr->combobox.currentIndex = currentIndex;
- Tcl_SetObjResult(interp, Tcl_NewIntObj(currentIndex));
- return TCL_OK;
- } else if (objc == 3) {
- if (Tcl_GetIntFromObj(interp, objv[2], &currentIndex) != TCL_OK) {
+ }
+
+ /* Make sure -values is a valid list:
+ */
+ if (mask & VALUES_CHANGED) {
+ if (ValuesValidate(interp, recordPtr, &currentIndex) != TCL_OK)
return TCL_ERROR;
- }
- if (currentIndex < 0 || currentIndex >= nValues) {
- Tcl_AppendResult(interp,
- "Index ", Tcl_GetString(objv[2]), " out of range",
- NULL);
+ if (Tcl_ListObjLength(interp,sbPtr->values.valuesObj,
+ &sbPtr->spinbox.valueCount) != TCL_OK)
return TCL_ERROR;
+ if (sbPtr->spinbox.valueCount > 0) {
+ if (sbPtr->values.currentIndex == -1
+ || sbPtr->values.currentIndex != currentIndex) {
+ Tcl_Obj *valueObj;
+ if (currentIndex == -1) currentIndex = 0;
+ Tcl_ListObjIndex(interp, sbPtr->values.valuesObj,
+ currentIndex, &valueObj);
+ EntrySetValue(recordPtr, Tcl_GetString(valueObj));
+ }
+ sbPtr->values.currentIndex = currentIndex;
}
+ }
- cbPtr->combobox.currentIndex = currentIndex;
+ if (needsUpdate && sbPtr->spinbox.valueCount < 1) {
+ Tcl_Obj *strObj, *valueObj = NULL;
+ const char *formatString = SpinboxCalculateFormat(sbPtr);
+ strObj = Tcl_NewDoubleObj(d);
+ valueObj = Tcl_Format(interp, formatString, 1, &strObj);
+ if (valueObj) {
+ Tcl_IncrRefCount(valueObj);
+ EntrySetValue(recordPtr, Tcl_GetString(valueObj));
+ Tcl_DecrRefCount(valueObj);
+ }
+ }
+
+ return EntryConfigure(interp, recordPtr, mask);
+}
- return EntrySetValue(recordPtr, Tcl_GetString(values[currentIndex]));
- } else {
- Tcl_WrongNumArgs(interp, 2, objv, "?newIndex?");
+/* $spinbox set $value
+ * Sets the value of a spinbox widget.
+ * We need to take account of any -format option.
+ */
+static int SpinboxSetCommand(
+ Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr)
+{
+ Spinbox *sbPtr = recordPtr;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "value");
return TCL_ERROR;
}
+ if (sbPtr->spinbox.valueCount < 1) {
+ Tcl_Obj *valueObj = NULL;
+ const char *formatString = SpinboxCalculateFormat(sbPtr);
+ valueObj = Tcl_Format(interp, formatString, 1, &objv[2]);
+ if (valueObj) {
+ Tcl_IncrRefCount(valueObj);
+ EntrySetValue(recordPtr, Tcl_GetString(valueObj));
+ Tcl_DecrRefCount(valueObj);
+ } else {
+ EntrySetValue(recordPtr, Tcl_GetString(objv[2]));
+ }
+ } else {
+ EntrySetValue(recordPtr, Tcl_GetString(objv[2]));
+ }
return TCL_OK;
}
/*------------------------------------------------------------------------
- * +++ Combobox widget definition.
+ * +++ Spinbox widget definition.
*/
-static WidgetCommandSpec ComboboxCommands[] =
+
+static WidgetCommandSpec SpinboxCommands[] =
{
{ "bbox", EntryBBoxCommand },
{ "cget", TtkWidgetCgetCommand },
{ "configure", TtkWidgetConfigureCommand },
- { "current", ComboboxCurrentCommand },
+ { "current", ValuesCurrentCommand },
{ "delete", EntryDeleteCommand },
{ "get", EntryGetCommand },
{ "icursor", EntryICursorCommand },
@@ -1817,20 +2088,21 @@ static WidgetCommandSpec ComboboxCommands[] =
{ "instate", TtkWidgetInstateCommand },
{ "selection", EntrySelectionCommand },
{ "state", TtkWidgetStateCommand },
- { "set", EntrySetCommand },
+ { "set", SpinboxSetCommand },
+ { "validate", EntryValidateCommand },
{ "xview", EntryXViewCommand },
{0,0}
};
-static WidgetSpec ComboboxWidgetSpec =
+static WidgetSpec SpinboxWidgetSpec =
{
- "TCombobox", /* className */
- sizeof(Combobox), /* recordSize */
- ComboboxOptionSpecs, /* optionSpecs */
- ComboboxCommands, /* subcommands */
- ComboboxInitialize, /* initializeProc */
+ "TSpinbox", /* className */
+ sizeof(Spinbox), /* recordSize */
+ SpinboxOptionSpecs, /* optionSpecs */
+ SpinboxCommands, /* subcommands */
+ SpinboxInitialize, /* initializeProc */
EntryCleanup, /* cleanupProc */
- ComboboxConfigure, /* configureProc */
+ SpinboxConfigure, /* configureProc */
EntryPostConfigure, /* postConfigureProc */
TtkWidgetGetLayout, /* getLayoutProc */
TtkWidgetSize, /* sizeProc */
@@ -1902,6 +2174,14 @@ TTK_BEGIN_LAYOUT(ComboboxLayout)
TTK_NODE("Combobox.textarea", TTK_FILL_BOTH)))
TTK_END_LAYOUT
+TTK_BEGIN_LAYOUT(SpinboxLayout)
+ TTK_GROUP("Spinbox.field", TTK_FILL_BOTH,
+ TTK_GROUP("Spinbox.padding", TTK_FILL_BOTH,
+ TTK_NODE("Spinbox.textarea", TTK_PACK_LEFT|TTK_EXPAND))
+ TTK_NODE("Spinbox.uparrow", TTK_PACK_TOP|TTK_STICK_E)
+ TTK_NODE("Spinbox.downarrow", TTK_PACK_BOTTOM|TTK_STICK_E))
+TTK_END_LAYOUT
+
/*------------------------------------------------------------------------
* +++ Initialization.
*/
@@ -1915,9 +2195,11 @@ void TtkEntry_Init(Tcl_Interp *interp)
Ttk_RegisterLayout(themePtr, "TEntry", EntryLayout);
Ttk_RegisterLayout(themePtr, "TCombobox", ComboboxLayout);
+ Ttk_RegisterLayout(themePtr, "TSpinbox", SpinboxLayout);
RegisterWidget(interp, "ttk::entry", &EntryWidgetSpec);
RegisterWidget(interp, "ttk::combobox", &ComboboxWidgetSpec);
+ RegisterWidget(interp, "ttk::spinbox", &SpinboxWidgetSpec);
}
/*EOF*/
diff --git a/library/ttk/altTheme.tcl b/library/ttk/altTheme.tcl
index e604373..0a4ce05 100644
--- a/library/ttk/altTheme.tcl
+++ b/library/ttk/altTheme.tcl
@@ -1,5 +1,5 @@
#
-# $Id: altTheme.tcl,v 1.7 2008/05/23 20:20:05 jenglish Exp $
+# $Id: altTheme.tcl,v 1.8 2008/11/01 15:34:24 patthoyts Exp $
#
# Ttk widget set: Alternate theme
#
@@ -60,6 +60,11 @@ namespace eval ttk::theme::alt {
ttk::style map TCombobox -fieldbackground \
[list readonly $colors(-frame) disabled $colors(-frame)]
+ ttk::style configure TSpinbox -arrowsize 10 -padding {2 0 10 0}
+ ttk::style map TSpinbox -fieldbackground \
+ [list readonly $colors(-frame) disabled $colors(-frame)] \
+ -arrowcolor [list disabled $colors(-disabledfg)]
+
ttk::style configure Toolbutton -relief flat -padding 2
ttk::style map Toolbutton -relief \
{disabled flat selected sunken pressed sunken active raised}
diff --git a/library/ttk/clamTheme.tcl b/library/ttk/clamTheme.tcl
index 799b6ae..8498f1e 100644
--- a/library/ttk/clamTheme.tcl
+++ b/library/ttk/clamTheme.tcl
@@ -1,5 +1,5 @@
#
-# $Id: clamTheme.tcl,v 1.7 2008/05/23 20:20:06 jenglish Exp $
+# $Id: clamTheme.tcl,v 1.8 2008/11/01 15:34:24 patthoyts Exp $
#
# "Clam" theme.
#
@@ -106,6 +106,14 @@ namespace eval ttk::theme::clam {
-foreground [list {readonly focus} $colors(-selectfg)] \
;
+ ttk::style configure TSpinbox -arrowsize 10 -padding {2 0 10 0}
+ ttk::style map TSpinbox \
+ -background [list readonly $colors(-frame)] \
+ -bordercolor [list focus $colors(-selectbg)] \
+ -lightcolor [list focus "#6f9dc6"] \
+ -darkcolor [list focus "#6f9dc6"] \
+ -arrowcolor [list disabled $colors(-disabledfg)]
+
ttk::style configure TNotebook.Tab -padding {6 2 6 2}
ttk::style map TNotebook.Tab \
-padding [list selected {6 4 6 2}] \
diff --git a/library/ttk/classicTheme.tcl b/library/ttk/classicTheme.tcl
index 48c3df8..72473df 100644
--- a/library/ttk/classicTheme.tcl
+++ b/library/ttk/classicTheme.tcl
@@ -1,5 +1,5 @@
#
-# $Id: classicTheme.tcl,v 1.7 2008/05/23 20:20:06 jenglish Exp $
+# $Id: classicTheme.tcl,v 1.8 2008/11/01 15:34:24 patthoyts Exp $
#
# "classic" Tk theme.
#
@@ -71,6 +71,10 @@ namespace eval ttk::theme::classic {
ttk::style map TCombobox -fieldbackground \
[list readonly $colors(-frame) disabled $colors(-frame)]
+ ttk::style configure TSpinbox -arrowsize 10 -padding {2 0 10 0}
+ ttk::style map TSpinbox -fieldbackground \
+ [list readonly $colors(-frame) disabled $colors(-frame)]
+
ttk::style configure TLabelframe -borderwidth 2 -relief groove
ttk::style configure TScrollbar -relief raised
diff --git a/library/ttk/defaults.tcl b/library/ttk/defaults.tcl
index dfb6654..98185ee5 100644
--- a/library/ttk/defaults.tcl
+++ b/library/ttk/defaults.tcl
@@ -1,5 +1,5 @@
#
-# $Id: defaults.tcl,v 1.7 2008/05/23 20:20:06 jenglish Exp $
+# $Id: defaults.tcl,v 1.8 2008/11/01 15:34:24 patthoyts Exp $
#
# Settings for default theme.
#
@@ -66,6 +66,11 @@ namespace eval ttk::theme::default {
ttk::style map TCombobox -fieldbackground \
[list readonly $colors(-frame) disabled $colors(-frame)]
+ ttk::style configure TSpinbox -arrowsize 10 -padding {2 0 10 0}
+ ttk::style map TSpinbox -fieldbackground \
+ [list readonly $colors(-frame) disabled $colors(-frame)] \
+ -arrowcolor [list disabled $colors(-disabledfg)]
+
ttk::style configure TLabelframe \
-relief groove -borderwidth 2
diff --git a/library/ttk/entry.tcl b/library/ttk/entry.tcl
index 360954e..d54328a 100644
--- a/library/ttk/entry.tcl
+++ b/library/ttk/entry.tcl
@@ -1,5 +1,5 @@
#
-# $Id: entry.tcl,v 1.5 2008/10/28 20:02:03 jenglish Exp $
+# $Id: entry.tcl,v 1.6 2008/11/01 15:34:24 patthoyts Exp $
#
# DERIVED FROM: tk/library/entry.tcl r1.22
#
@@ -228,7 +228,7 @@ proc ttk::entry::See {w {index insert}} {
# position following the next end-of-word position.
#
set ::ttk::entry::State(startNext) \
- [string equal $tcl_platform(platform) "windows"]
+ [string equal $::tcl_platform(platform) "windows"]
proc ttk::entry::NextWord {w start} {
variable State
diff --git a/library/ttk/spinbox.tcl b/library/ttk/spinbox.tcl
new file mode 100644
index 0000000..579a22c
--- /dev/null
+++ b/library/ttk/spinbox.tcl
@@ -0,0 +1,121 @@
+#
+# $Id: spinbox.tcl,v 1.1 2008/11/01 15:34:24 patthoyts Exp $
+#
+# Tile widget set: spinbox bindings.
+#
+#
+
+namespace eval ttk::spinbox {
+ variable Values ;# Values($cb) is -listvariable of listbox widget
+
+ variable State
+ set State(entryPress) 0
+}
+
+### Spinbox bindings.
+#
+# Duplicate the Entry bindings, override if needed:
+#
+
+ttk::copyBindings TEntry TSpinbox
+
+bind TSpinbox <Double-Button-1> {ttk::spinbox::Select %W %x %y word}
+bind TSpinbox <Triple-Button-1> {ttk::spinbox::Select %W %x %y line}
+
+bind TSpinbox <ButtonPress-1> { ttk::spinbox::Press %W %x %y }
+bind TSpinbox <ButtonRelease-1> { ttk::spinbox::Release %W %x %y }
+bind TSpinbox <MouseWheel> {ttk::spinbox::Change %W [expr {%D/-120}] line}
+bind TSpinbox <Up> {ttk::spinbox::Change %W +[%W cget -increment] line}
+bind TSpinbox <Down> {ttk::spinbox::Change %W -[%W cget -increment] line}
+
+
+proc ttk::spinbox::Press {w x y} {
+ if {[$w instate disabled]} { return }
+ variable State
+ set State(xPress) $x
+ set State(yPress) $y
+ focus $w
+ switch -glob -- [$w identify $x $y] {
+ *uparrow {
+ ttk::Repeatedly Change $w +[$w cget -increment] line
+ }
+ *downarrow {
+ ttk::Repeatedly Change $w -[$w cget -increment] line
+ }
+ *textarea {
+ set State(entryPress) [$w instate !readonly]
+ if {$State(entryPress)} {
+ ttk::entry::Press $w $x
+ }
+ }
+ }
+}
+
+proc ttk::spinbox::Release {w x y} {
+ variable State
+ unset -nocomplain State(xPress) State(yPress)
+ ttk::CancelRepeat
+}
+
+proc ttk::spinbox::Change {w n units} {
+ if {[set vlen [llength [$w cget -values]]] != 0} {
+ set index [expr {[$w current] + $n}]
+ if {[catch {$w current $index}]} {
+ if {[$w cget -wrap]} {
+ if {$index == -1} {
+ set index [llength [$w cget -values]]
+ incr index -1
+ } else {
+ set index 0
+ }
+ $w current $index
+ }
+ }
+ } else {
+ if {![catch {expr {[$w get] + $n}} v]} {
+ if {$v < [$w cget -from]} {
+ if {[$w cget -wrap]} {
+ set v [$w cget -to]
+ } else {
+ set v [$w cget -from]
+ }
+ } elseif {$v > [$w cget -to]} {
+ if {[$w cget -wrap]} {
+ set v [$w cget -from]
+ } else {
+ set v [$w cget -to]
+ }
+ }
+ $w set $v
+ }
+ }
+ ::ttk::entry::Select $w 0 $units
+
+ # Run -command callback:
+ #
+ uplevel #0 [$w cget -command]
+
+}
+
+# Spinbox double-click on the arrows needs interception, otherwise
+# pass to the TEntry handler
+proc ttk::spinbox::Select {w x y mode} {
+ if {[$w instate disabled]} { return }
+ variable State
+ set State(xPress) $x
+ set State(yPress) $y
+ switch -glob -- [$w identify $x $y] {
+ *uparrow {
+ ttk::Repeatedly Change $w +[$w cget -increment] units
+ }
+ *downarrow {
+ ttk::Repeatedly Change $w -[$w cget -increment] units
+ }
+ *textarea {
+ return [::ttk::entry::Select $w $x $mode]
+ }
+ }
+ return -code continue
+}
+
+#*EOF*
diff --git a/library/ttk/ttk.tcl b/library/ttk/ttk.tcl
index c4d0ff1..8bc1478 100644
--- a/library/ttk/ttk.tcl
+++ b/library/ttk/ttk.tcl
@@ -1,5 +1,5 @@
#
-# $Id: ttk.tcl,v 1.8 2007/12/13 15:27:08 dgp Exp $
+# $Id: ttk.tcl,v 1.9 2008/11/01 15:34:24 patthoyts Exp $
#
# Ttk widget set initialization script.
#
@@ -105,6 +105,7 @@ source [file join $::ttk::library notebook.tcl]
source [file join $::ttk::library panedwindow.tcl]
source [file join $::ttk::library entry.tcl]
source [file join $::ttk::library combobox.tcl] ;# dependency: entry.tcl
+source [file join $::ttk::library spinbox.tcl] ;# dependency: entry.tcl
source [file join $::ttk::library treeview.tcl]
source [file join $::ttk::library sizegrip.tcl]
diff --git a/library/ttk/winTheme.tcl b/library/ttk/winTheme.tcl
index 03ca6d2..c27c45a 100644
--- a/library/ttk/winTheme.tcl
+++ b/library/ttk/winTheme.tcl
@@ -1,5 +1,5 @@
#
-# $Id: winTheme.tcl,v 1.7 2008/05/23 20:20:06 jenglish Exp $
+# $Id: winTheme.tcl,v 1.8 2008/11/01 15:34:24 patthoyts Exp $
#
# Settings for 'winnative' theme.
#
@@ -45,6 +45,8 @@ namespace eval ttk::theme::winnative {
-focusfill [list {readonly focus} SystemHighlight] \
;
+ ttk::style configure TSpinbox -padding {2 0 16 0}
+
ttk::style configure TLabelframe -borderwidth 2 -relief groove
ttk::style configure Toolbutton -relief flat -padding {8 4}
diff --git a/library/ttk/xpTheme.tcl b/library/ttk/xpTheme.tcl
index 691dcea..c204233 100644
--- a/library/ttk/xpTheme.tcl
+++ b/library/ttk/xpTheme.tcl
@@ -1,5 +1,5 @@
#
-# $Id: xpTheme.tcl,v 1.9 2008/05/23 20:20:06 jenglish Exp $
+# $Id: xpTheme.tcl,v 1.10 2008/11/01 15:34:24 patthoyts Exp $
#
# Settings for 'xpnative' theme
#
@@ -52,6 +52,12 @@ namespace eval ttk::theme::xpnative {
-focusfill [list {readonly focus} SystemHighlight] \
;
+ ttk::style configure TSpinbox -padding {2 0 14 0}
+ ttk::style map TSpinbox \
+ -selectbackground [list !focus SystemWindow] \
+ -selectforeground [list !focus SystemWindowText] \
+ ;
+
ttk::style configure Toolbutton -padding {4 4}
# Vista requires some style modifications. There are some
@@ -75,6 +81,20 @@ namespace eval ttk::theme::xpnative {
}
}
+ # EDIT EP_EDITBORDER_HVSCROLL
+ ttk::style configure TSpinbox -padding {2 0 15 1}
+ ttk::style element create Vista.Spinbox.field vsapi \
+ EDIT 9 {disabled 4 focus 3 active 2 {} 1} \
+ -padding {1 1 1 2}
+ ttk::style layout TSpinbox {
+ Vista.Spinbox.field -sticky nswe -children {
+ Spinbox.padding -sticky nswe -children {
+ Spinbox.textarea -expand 1 -sticky {}
+ }
+ Spinbox.uparrow -side top -sticky ens
+ Spinbox.downarrow -side bottom -sticky ens
+ }
+ }
}
}
}
diff --git a/tests/ttk/spinbox.test b/tests/ttk/spinbox.test
new file mode 100644
index 0000000..66a7652
--- /dev/null
+++ b/tests/ttk/spinbox.test
@@ -0,0 +1,239 @@
+#
+# ttk::spinbox widget tests
+#
+
+package require Tk 8.5
+package require tcltest ; namespace import -force tcltest::*
+loadTestedCommands
+
+set PI [expr {acos(-1)}]
+
+test spinbox-1.0 "Spinbox tests -- setup" -body {
+ ttk::spinbox .sb
+} -cleanup { destroy .sb } -result .sb
+
+test spinbox-1.1 "Bad -values list" -setup {
+ ttk::spinbox .sb
+} -body {
+ .sb configure -values "bad \{list"
+} -cleanup {
+ destroy .sb
+} -returnCodes error -result "unmatched open brace in list"
+
+test spinbox-1.2.1 "starts within range" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 0
+
+test spinbox-1.2.2 "starts within range" -setup {
+ ttk::spinbox .sb -from 100 -to 110
+} -body {
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 100
+
+test spinbox-1.3.1 "get retrieves value" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb set 50
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 50
+
+test spinbox-1.3.2 "get retrieves value" -setup {
+ ttk::spinbox .sb -from 0 -to 100 -values 55
+} -body {
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 55
+
+test spinbox-1.4.1 "set changes value" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb set 33
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 33
+
+test spinbox-1.4.2 "set changes value" -setup {
+ ttk::spinbox .sb -from 0 -to 100 -values 55
+} -body {
+ .sb set 33
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 33
+
+test spinbox-1.5.1 "format accounts for -increment" -setup {
+ ttk::spinbox .sb -from 0 -to 10 -increment 0.1
+} -body {
+ ::ttk::spinbox::Change .sb [expr {acos(-1)}] line
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 3.1
+
+test spinbox-1.5.2 "format accounts for -increment" -setup {
+ ttk::spinbox .sb -from 0 -to 10 -increment 0.1
+} -body {
+ .sb set $PI
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 3.1
+
+test spinbox-1.6.1 "insert start" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb set 5
+ .sb insert 0 4
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 45
+
+test spinbox-1.6.2 "insert end" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb set 5
+ .sb insert end 4
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 54
+
+test spinbox-1.6.3 "insert invalid index" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb set 5
+ .sb insert 100 4
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result 54
+
+test spinbox-1.7.1 "-command option: set doesnt fire" -setup {
+ ttk::spinbox .sb -from 0 -to 100 -command {set ::spinbox_test 1}
+} -body {
+ set ::spinbox_test 0
+ .sb set 50
+ set ::spinbox_test
+} -cleanup {
+ destroy .sb
+} -result 0
+
+test spinbox-1.7.2 "-command option: button handler will fire" -setup {
+ ttk::spinbox .sb -from 0 -to 100 -command {set ::spinbox_test 1}
+} -body {
+ set ::spinbox_test 0
+ .sb set 50
+ ::ttk::spinbox::Change .sb [expr {acos(-1)}] line
+ set ::spinbox_test
+} -cleanup {
+ destroy .sb
+} -result 1
+
+test spinbox-1.8.1 "option -validate" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb configure -validate all
+ .sb cget -validate
+} -cleanup {
+ destroy .sb
+} -result {all}
+
+test spinbox-1.8.2 "option -validate" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb configure -validate key
+ .sb configure -validate focus
+ .sb configure -validate focusin
+ .sb configure -validate focusout
+ .sb configure -validate none
+ .sb cget -validate
+} -cleanup {
+ destroy .sb
+} -result {none}
+
+test spinbox-1.8.3 "option -validate" -setup {
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb configure -validate bogus
+} -cleanup {
+ destroy .sb
+} -returnCodes error -result {bad validate "bogus": must be all, key, focus, focusin, focusout, or none}
+
+test spinbox-1.8.4 "-validate option: " -setup {
+ set ::spinbox_test {}
+ ttk::spinbox .sb -from 0 -to 100
+} -body {
+ .sb configure -validate all -validatecommand {lappend ::spinbox_test %P}
+ pack .sb
+ .sb set 50
+ focus .sb
+ after 100 {set ::spinbox_wait 1} ; vwait ::spinbox_wait
+ set ::spinbox_test
+} -cleanup {
+ destroy .sb
+} -result {50}
+
+
+test spinbox-2.0 "current command -- unset should be 0" -setup {
+ ttk::spinbox .sb -values [list a b c d e a]
+} -body {
+ .sb current
+} -cleanup {
+ destroy .sb
+} -result 0
+
+test spinbox-2.1 "current command -- set index" -setup {
+ ttk::spinbox .sb -values [list a b c d e a]
+} -body {
+ .sb current 5
+ .sb get
+} -cleanup {
+ destroy .sb
+} -result a
+
+test spinbox-2.2 "current command -- change -values" -setup {
+ ttk::spinbox .sb -values [list a b c d e a]
+} -body {
+ .sb current 5
+ .sb configure -values [list c b a d e]
+ .sb current
+} -cleanup {
+ destroy .sb
+} -result 2
+
+test spinbox-2.3 "current command -- change value" -setup {
+ ttk::spinbox .sb -values [list c b a d e]
+} -body {
+ .sb current 2
+ .sb set "b"
+ .sb current
+} -cleanup {
+ destroy .sb
+} -result 1
+
+test spinbox-2.4 "current command -- value not in list" -setup {
+ ttk::spinbox .sb -values [list c b a d e]
+} -body {
+ .sb current 2
+ .sb set "z"
+ .sb current
+} -cleanup {
+ destroy .sb
+} -result -1
+
+tcltest::cleanupTests
+
+# Local variables:
+# mode: tcl
+# End:
diff --git a/win/ttkWinTheme.c b/win/ttkWinTheme.c
index 749fe3f..e224f57 100644
--- a/win/ttkWinTheme.c
+++ b/win/ttkWinTheme.c
@@ -1,6 +1,6 @@
/* winTheme.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
*
- * $Id: ttkWinTheme.c,v 1.13 2008/01/08 17:01:00 jenglish Exp $
+ * $Id: ttkWinTheme.c,v 1.14 2008/11/01 15:34:24 patthoyts Exp $
*/
#ifdef _MSC_VER
@@ -104,10 +104,12 @@ typedef struct {
Ttk_Padding margins; /* additional placement padding */
} FrameControlElementData;
-#define _FIXEDSIZE 0x8000
+#define _FIXEDSIZE 0x80000000L
+#define _HALFMETRIC 0x40000000L
#define FIXEDSIZE(id) (id|_FIXEDSIZE)
+#define HALFMETRIC(id) (id|_HALFMETRIC)
#define GETMETRIC(m) \
- ((m) & _FIXEDSIZE ? (m) & ~_FIXEDSIZE : GetSystemMetrics(m))
+ ((m) & _FIXEDSIZE ? (m) & ~_FIXEDSIZE : GetSystemMetrics((m)&0x0fffffff))
static FrameControlElementData FrameControlElements[] = {
{ "Checkbutton.indicator",
@@ -131,6 +133,12 @@ static FrameControlElementData FrameControlElements[] = {
{ "sizegrip",
DFC_SCROLL, DFCS_SCROLLSIZEGRIP, SM_CXVSCROLL, SM_CYHSCROLL,
arrow_statemap, {0,0,0,0} },
+ { "Spinbox.uparrow",
+ DFC_SCROLL, DFCS_SCROLLUP, SM_CXVSCROLL, HALFMETRIC(SM_CYVSCROLL),
+ arrow_statemap, {0,0,0,0} },
+ { "Spinbox.downarrow",
+ DFC_SCROLL, DFCS_SCROLLDOWN, SM_CXVSCROLL, HALFMETRIC(SM_CYVSCROLL),
+ arrow_statemap, {0,0,0,0} },
{ 0,0,0,0,0,0, {0,0,0,0} }
};
@@ -142,8 +150,12 @@ static void FrameControlElementSize(
int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
FrameControlElementData *p = clientData;
- *widthPtr = GETMETRIC(p->cxId) + Ttk_PaddingWidth(p->margins);
- *heightPtr = GETMETRIC(p->cyId) + Ttk_PaddingHeight(p->margins);
+ int cx = GETMETRIC(p->cxId);
+ int cy = GETMETRIC(p->cyId);
+ if (p->cxId & _HALFMETRIC) cx /= 2;
+ if (p->cyId & _HALFMETRIC) cy /= 2;
+ *widthPtr = cx + Ttk_PaddingWidth(p->margins);
+ *heightPtr = cy + Ttk_PaddingHeight(p->margins);
}
static void FrameControlElementDraw(
diff --git a/win/ttkWinXPTheme.c b/win/ttkWinXPTheme.c
index 03eaab9..27416e0 100644
--- a/win/ttkWinXPTheme.c
+++ b/win/ttkWinXPTheme.c
@@ -1,5 +1,5 @@
/*
- * $Id: ttkWinXPTheme.c,v 1.20 2008/04/27 22:39:17 dkf Exp $
+ * $Id: ttkWinXPTheme.c,v 1.21 2008/11/01 15:34:24 patthoyts Exp $
*
* Tk theme engine which uses the Windows XP "Visual Styles" API
* Adapted from Georgios Petasis' XP theme patch.
@@ -316,6 +316,14 @@ static Ttk_StateTable rightarrow_statemap[] =
{ ABS_RIGHTNORMAL, 0, 0 }
};
+static Ttk_StateTable spinbutton_statemap[] =
+{
+ { DNS_DISABLED, TTK_STATE_DISABLED, 0 },
+ { DNS_PRESSED, TTK_STATE_PRESSED, 0 },
+ { DNS_HOT, TTK_STATE_ACTIVE, 0 },
+ { DNS_NORMAL, 0, 0 },
+};
+
/*
* Trackbar thumb: (Tk: "scale slider")
*/
@@ -586,6 +594,36 @@ static Ttk_ElementSpec GenericSizedElementSpec = {
};
/*----------------------------------------------------------------------
+ * +++ Spinbox arrow element.
+ * These are half-height scrollbar buttons.
+ */
+
+static void
+SpinboxArrowElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+{
+ ElementData *elementData = clientData;
+
+ if (!InitElementData(elementData, tkwin, 0))
+ return;
+
+ GenericSizedElementSize(clientData, elementRecord, tkwin,
+ widthPtr, heightPtr, paddingPtr);
+
+ /* force the arrow button height to half size */
+ *heightPtr /= 2;
+}
+
+static Ttk_ElementSpec SpinboxArrowElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(NullElement),
+ TtkNullElementOptions,
+ SpinboxArrowElementSize,
+ GenericElementDraw
+};
+
+/*----------------------------------------------------------------------
* +++ Scrollbar thumb element.
* Same as a GenericElement, but don't draw in the disabled state.
*/
@@ -964,6 +1002,14 @@ static ElementInfo ElementInfoTable[] = {
HP_HEADERITEM, header_statemap, PAD(4,0,4,0),0 },
{ "sizegrip", &GenericElementSpec, L"STATUS",
SP_GRIPPER, null_statemap, NOPAD,0 },
+ { "Spinbox.field", &GenericElementSpec, L"EDIT",
+ EP_EDITTEXT, edittext_statemap, PAD(1, 1, 1, 1), 0 },
+ { "Spinbox.uparrow", &SpinboxArrowElementSpec, L"SPIN",
+ SPNP_UP, spinbutton_statemap, NOPAD,
+ PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) },
+ { "Spinbox.downarrow", &SpinboxArrowElementSpec, L"SPIN",
+ SPNP_DOWN, spinbutton_statemap, NOPAD,
+ PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) },
#if BROKEN_TEXT_ELEMENT
{ "Labelframe.text", &TextElementSpec, L"BUTTON",