345 lines
8.9 KiB
C
345 lines
8.9 KiB
C
/*
|
|
* Copyright (c) 2003, Joe English
|
|
*
|
|
* ttk::scrollbar widget.
|
|
*/
|
|
|
|
#include "tkInt.h"
|
|
#include "ttkTheme.h"
|
|
#include "ttkWidget.h"
|
|
|
|
/*------------------------------------------------------------------------
|
|
* +++ Scrollbar widget record.
|
|
*/
|
|
typedef struct
|
|
{
|
|
Tcl_Obj *commandObj;
|
|
|
|
int orient;
|
|
Tcl_Obj *orientObj;
|
|
|
|
double first; /* top fraction */
|
|
double last; /* bottom fraction */
|
|
|
|
Ttk_Box troughBox; /* trough parcel */
|
|
int minSize; /* minimum size of thumb */
|
|
} ScrollbarPart;
|
|
|
|
typedef struct
|
|
{
|
|
WidgetCore core;
|
|
ScrollbarPart scrollbar;
|
|
} Scrollbar;
|
|
|
|
static Tk_OptionSpec ScrollbarOptionSpecs[] =
|
|
{
|
|
{TK_OPTION_STRING, "-command", "command", "Command", "",
|
|
Tk_Offset(Scrollbar,scrollbar.commandObj), -1, 0,0,0},
|
|
|
|
{TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", "vertical",
|
|
Tk_Offset(Scrollbar,scrollbar.orientObj),
|
|
Tk_Offset(Scrollbar,scrollbar.orient),
|
|
0, (void *)ttkOrientStrings, STYLE_CHANGED },
|
|
|
|
WIDGET_TAKEFOCUS_FALSE,
|
|
WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs)
|
|
};
|
|
|
|
/*------------------------------------------------------------------------
|
|
* +++ Widget hooks.
|
|
*/
|
|
|
|
static void
|
|
ScrollbarInitialize(Tcl_Interp *dummy, void *recordPtr)
|
|
{
|
|
Scrollbar *sb = (Scrollbar *)recordPtr;
|
|
(void)dummy;
|
|
|
|
sb->scrollbar.first = 0.0;
|
|
sb->scrollbar.last = 1.0;
|
|
|
|
TtkTrackElementState(&sb->core);
|
|
}
|
|
|
|
static Ttk_Layout ScrollbarGetLayout(
|
|
Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr)
|
|
{
|
|
Scrollbar *sb = (Scrollbar *)recordPtr;
|
|
return TtkWidgetGetOrientedLayout(
|
|
interp, theme, recordPtr, sb->scrollbar.orientObj);
|
|
}
|
|
|
|
/*
|
|
* ScrollbarDoLayout --
|
|
* Layout hook. Adjusts the position of the scrollbar thumb.
|
|
*
|
|
* Side effects:
|
|
* Sets sb->troughBox and sb->minSize.
|
|
*/
|
|
static void ScrollbarDoLayout(void *recordPtr)
|
|
{
|
|
Scrollbar *sb = (Scrollbar *)recordPtr;
|
|
WidgetCore *corePtr = &sb->core;
|
|
Ttk_Element thumb;
|
|
Ttk_Box thumbBox;
|
|
int thumbWidth, thumbHeight;
|
|
double first, last, size;
|
|
int minSize;
|
|
|
|
/*
|
|
* Use generic layout manager to compute initial layout:
|
|
*/
|
|
Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin));
|
|
|
|
/*
|
|
* Locate thumb element, extract parcel and requested minimum size:
|
|
*/
|
|
thumb = Ttk_FindElement(corePtr->layout, "thumb");
|
|
if (!thumb) /* Something has gone wrong -- bail */
|
|
return;
|
|
|
|
sb->scrollbar.troughBox = thumbBox = Ttk_ElementParcel(thumb);
|
|
Ttk_LayoutNodeReqSize(
|
|
corePtr->layout, thumb, &thumbWidth,&thumbHeight);
|
|
|
|
/*
|
|
* Adjust thumb element parcel:
|
|
*/
|
|
first = sb->scrollbar.first;
|
|
last = sb->scrollbar.last;
|
|
|
|
if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) {
|
|
minSize = thumbHeight;
|
|
size = thumbBox.height - minSize;
|
|
thumbBox.y += (int)(size * first);
|
|
thumbBox.height = (int)(size * last) + minSize - (int)(size * first);
|
|
} else {
|
|
minSize = thumbWidth;
|
|
size = thumbBox.width - minSize;
|
|
thumbBox.x += (int)(size * first);
|
|
thumbBox.width = (int)(size * last) + minSize - (int)(size * first);
|
|
}
|
|
sb->scrollbar.minSize = minSize;
|
|
Ttk_PlaceElement(corePtr->layout, thumb, thumbBox);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------
|
|
* +++ Widget commands.
|
|
*/
|
|
|
|
/* $sb set $first $last --
|
|
* Set the position of the scrollbar.
|
|
*/
|
|
static int
|
|
ScrollbarSetCommand(
|
|
void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
|
|
{
|
|
Scrollbar *scrollbar = (Scrollbar *)recordPtr;
|
|
Tcl_Obj *firstObj, *lastObj;
|
|
double first, last;
|
|
|
|
if (objc != 4) {
|
|
Tcl_WrongNumArgs(interp, 2, objv, "first last");
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
firstObj = objv[2];
|
|
lastObj = objv[3];
|
|
if (Tcl_GetDoubleFromObj(interp, firstObj, &first) != TCL_OK
|
|
|| Tcl_GetDoubleFromObj(interp, lastObj, &last) != TCL_OK)
|
|
return TCL_ERROR;
|
|
|
|
/* Range-checks:
|
|
*/
|
|
if (first < 0.0) {
|
|
first = 0.0;
|
|
} else if (first > 1.0) {
|
|
first = 1.0;
|
|
}
|
|
|
|
if (last < first) {
|
|
last = first;
|
|
} else if (last > 1.0) {
|
|
last = 1.0;
|
|
}
|
|
|
|
/* ASSERT: 0.0 <= first <= last <= 1.0 */
|
|
|
|
scrollbar->scrollbar.first = first;
|
|
scrollbar->scrollbar.last = last;
|
|
if (first <= 0.0 && last >= 1.0) {
|
|
scrollbar->core.state |= TTK_STATE_DISABLED;
|
|
} else {
|
|
scrollbar->core.state &= ~TTK_STATE_DISABLED;
|
|
}
|
|
|
|
TtkRedisplayWidget(&scrollbar->core);
|
|
|
|
return TCL_OK;
|
|
}
|
|
|
|
/* $sb get --
|
|
* Returns the last thing passed to 'set'.
|
|
*/
|
|
static int
|
|
ScrollbarGetCommand(
|
|
void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
|
|
{
|
|
Scrollbar *scrollbar = (Scrollbar *)recordPtr;
|
|
Tcl_Obj *result[2];
|
|
|
|
if (objc != 2) {
|
|
Tcl_WrongNumArgs(interp, 2, objv, "");
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
result[0] = Tcl_NewDoubleObj(scrollbar->scrollbar.first);
|
|
result[1] = Tcl_NewDoubleObj(scrollbar->scrollbar.last);
|
|
Tcl_SetObjResult(interp, Tcl_NewListObj(2, result));
|
|
|
|
return TCL_OK;
|
|
}
|
|
|
|
/* $sb delta $dx $dy --
|
|
* Returns the percentage change corresponding to a mouse movement
|
|
* of $dx, $dy.
|
|
*/
|
|
static int
|
|
ScrollbarDeltaCommand(
|
|
void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
|
|
{
|
|
Scrollbar *sb = (Scrollbar *)recordPtr;
|
|
double dx, dy;
|
|
double delta = 0.0;
|
|
|
|
if (objc != 4) {
|
|
Tcl_WrongNumArgs(interp, 2, objv, "dx dy");
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
if (Tcl_GetDoubleFromObj(interp, objv[2], &dx) != TCL_OK
|
|
|| Tcl_GetDoubleFromObj(interp, objv[3], &dy) != TCL_OK)
|
|
{
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
delta = 0.0;
|
|
if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) {
|
|
int size = sb->scrollbar.troughBox.height - sb->scrollbar.minSize;
|
|
if (size > 0) {
|
|
delta = (double)dy / (double)size;
|
|
}
|
|
} else {
|
|
int size = sb->scrollbar.troughBox.width - sb->scrollbar.minSize;
|
|
if (size > 0) {
|
|
delta = (double)dx / (double)size;
|
|
}
|
|
}
|
|
|
|
Tcl_SetObjResult(interp, Tcl_NewDoubleObj(delta));
|
|
return TCL_OK;
|
|
}
|
|
|
|
/* $sb fraction $x $y --
|
|
* Returns a real number between 0 and 1 indicating where the
|
|
* point given by x and y lies in the trough area of the scrollbar.
|
|
*/
|
|
static int
|
|
ScrollbarFractionCommand(
|
|
void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
|
|
{
|
|
Scrollbar *sb = (Scrollbar *)recordPtr;
|
|
Ttk_Box b = sb->scrollbar.troughBox;
|
|
int minSize = sb->scrollbar.minSize;
|
|
double x, y;
|
|
double fraction = 0.0;
|
|
|
|
if (objc != 4) {
|
|
Tcl_WrongNumArgs(interp, 2, objv, "x y");
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK
|
|
|| Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK)
|
|
{
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
fraction = 0.0;
|
|
if (sb->scrollbar.orient == TTK_ORIENT_VERTICAL) {
|
|
if (b.height > minSize) {
|
|
fraction = (double)(y - b.y) / (double)(b.height - minSize);
|
|
}
|
|
} else {
|
|
if (b.width > minSize) {
|
|
fraction = (double)(x - b.x) / (double)(b.width - minSize);
|
|
}
|
|
}
|
|
|
|
Tcl_SetObjResult(interp, Tcl_NewDoubleObj(fraction));
|
|
return TCL_OK;
|
|
}
|
|
|
|
static const Ttk_Ensemble ScrollbarCommands[] = {
|
|
{ "configure", TtkWidgetConfigureCommand,0 },
|
|
{ "cget", TtkWidgetCgetCommand,0 },
|
|
{ "delta", ScrollbarDeltaCommand,0 },
|
|
{ "fraction", ScrollbarFractionCommand,0 },
|
|
{ "get", ScrollbarGetCommand,0 },
|
|
{ "identify", TtkWidgetIdentifyCommand,0 },
|
|
{ "instate", TtkWidgetInstateCommand,0 },
|
|
{ "set", ScrollbarSetCommand,0 },
|
|
{ "state", TtkWidgetStateCommand,0 },
|
|
{ 0,0,0 }
|
|
};
|
|
|
|
/*------------------------------------------------------------------------
|
|
* +++ Widget specification.
|
|
*/
|
|
static WidgetSpec ScrollbarWidgetSpec =
|
|
{
|
|
"TScrollbar", /* className */
|
|
sizeof(Scrollbar), /* recordSize */
|
|
ScrollbarOptionSpecs, /* optionSpecs */
|
|
ScrollbarCommands, /* subcommands */
|
|
ScrollbarInitialize, /* initializeProc */
|
|
TtkNullCleanup, /* cleanupProc */
|
|
TtkCoreConfigure, /* configureProc */
|
|
TtkNullPostConfigure, /* postConfigureProc */
|
|
ScrollbarGetLayout, /* getLayoutProc */
|
|
TtkWidgetSize, /* sizeProc */
|
|
ScrollbarDoLayout, /* layoutProc */
|
|
TtkWidgetDisplay /* displayProc */
|
|
};
|
|
|
|
TTK_BEGIN_LAYOUT(VerticalScrollbarLayout)
|
|
TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_Y,
|
|
TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_TOP)
|
|
TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM)
|
|
TTK_NODE("Vertical.Scrollbar.thumb", TTK_FILL_BOTH))
|
|
TTK_END_LAYOUT
|
|
|
|
TTK_BEGIN_LAYOUT(HorizontalScrollbarLayout)
|
|
TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_X,
|
|
TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_LEFT)
|
|
TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT)
|
|
TTK_NODE("Horizontal.Scrollbar.thumb", TTK_FILL_BOTH))
|
|
TTK_END_LAYOUT
|
|
|
|
/*------------------------------------------------------------------------
|
|
* +++ Initialization.
|
|
*/
|
|
|
|
MODULE_SCOPE
|
|
void TtkScrollbar_Init(Tcl_Interp *interp)
|
|
{
|
|
Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
|
|
|
|
Ttk_RegisterLayout(theme,"Vertical.TScrollbar",VerticalScrollbarLayout);
|
|
Ttk_RegisterLayout(theme,"Horizontal.TScrollbar",HorizontalScrollbarLayout);
|
|
|
|
RegisterWidget(interp, "ttk::scrollbar", &ScrollbarWidgetSpec);
|
|
}
|
|
|
|
/*EOF*/
|