Wondering why the buttons in a BWidgets SpinBox looked different from a native spinbox on Win32, I found the button in the SpinBox was just a rectangle drawn on a canvas but the Win32 button had a "beveled" edge with a 2-step shadow. One solution to making the
BWidgets SpinBox look better was to make the lines beveled. This draft code draws beveled lines on a canvas. Feedback welcome... --
CLN -
proc bevelLine { canvas c0 c1 c2 c3 width x0 y0 x1 y1 } {
set halfWidth [expr {$width/2}]
set d [expr {$x1-$x0}]
if {$d > 0} {
set side UpperLeft
} elseif {$d < 0} {
set side LowerRight
} else {
set d [expr {$y1-$y0}]
if {$d > 0} {
set side LowerRight
} else {
set side UpperLeft
}
}
switch -- $side {
UpperLeft {
set outside $c0
set inside $c1
}
LowerRight {
set inside $c2
set outside $c3
}
}
# The "main" line
set id1 [$canvas create line $x0 $y0 $x1 $y1 \
-fill $inside -width $width]
# The highlight line, half the width, slightly offset (moved below)
set id2 [$canvas create line $x0 $y0 $x1 $y1 \
-fill $outside -width $halfWidth]
# Figure out which way to move the accent line off the center of
# the main line.
#
# We want to move perpendicular to the main line. Noting that Tk
# uses a left-handed coordinate system (x to the right, y down),
# the transformation matrix for a 90 degree counter-clockwise
# rotation is:
# / 0 -1 \
# \ 1 0 /
# For example, a line from (0,0) down and right to (2,1) has a
# perpendicular vector from (0,0) to (1,-2) because:
# (2 1) * / 0 -1 \ = (2*0 + 1*1 2*-1 + 1*0) = (1 -2)
# \ 1 0 /
# Simplifying, we get:
# xp = y
# yp = -x
# Get the vector perpendicular to this line segment
set xp [expr {$y1 - $y0}]
set yp [expr {$x0 - $x1}]
# Get the offset for the accent line
if {$xp == 0} {
# Perpendicular has no X component, so the line is horizontal.
# dx is 0, dy is 1/2 halfWidth
set dx 0
set dy [expr {$halfWidth/2}]
} elseif {$yp == 0} {
# Perpendicular has no Y component, so the line is vertical.
# dx is 1/2 halfWidth, dy is 0
set dx [expr {$halfWidth/2}]
set dy 0
} else {
# Line is neither horizontal nor vertical,
# scale the perpendicular.
# Get the length of the vector
set l [expr {sqrt($xp*$xp + $yp*$yp)}]
# Figure out how much to scale the perp. to get the offset
set scale [expr {2*$l/$halfWidth}]
set dx [expr {int($xp / $scale)}]
set dy [expr {int($yp / $scale)}]
}
.c move $id2 $dx $dy
# return [list $id1 $id2]
}
# bevelLine
# WUZ - it might be nice if this returned a composit tag that could
# be used to manipulate the whole polyline as a unit.
proc bevelPolyLine { canvas c0 c1 c2 c3 width args } {
if {[llength $args] < 4
|| [llength $args]%2} {
error "Must be an even number of coordinates, four or greater"
}
set x1 [lindex $args 0]
set y1 [lindex $args 1]
set args [lrange $args 2 end]
while {[llength $args]} {
set x0 $x1
set y0 $y1
set x1 [lindex $args 0]
set y1 [lindex $args 1]
set args [lrange $args 2 end]
# WUZ - We need a way to get all the accent lines for a
# polyline above the base lines.
bevelLine $canvas $c0 $c1 $c2 $c3 $width $x0 $y0 $x1 $y1
}
}
# bevelPolyLine
proc bevelRect { canvas c0 c1 c2 c3 width x1 y1 x2 y2 } {
bevelPolyLine $canvas $c0 $c1 $c2 $c3 $width \
$x1 $y1 \
$x1 $y2 \
$x2 $y2 \
$x2 $y1 \
$x1 $y1
}
# bevelRect
# Need a canvas to work on
pack [canvas .c]
# A rectangle (e.g., a button)
bevelRect .c white lightgray darkgray black 2 10 70 70 10
# An octagon
bevelPolyLine .c white lightgray darkgray black 2 \
20 44 20 30 30 20 44 20 \
54 30 54 44 44 54 30 54 \
20 44