Files
cpython-source-deps/generic/ttk/ttkScrollbar.c
2021-03-30 00:54:10 +01:00

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*/