1# File: widget.tcl 2 3# Purpose: commands for manipulating Widgets 4 5# 6# Copyright (c) 1997-2001 Tim Baker 7# 8# This software may be copied and distributed for educational, research, and 9# not for profit purposes provided that this copyright and statement are 10# included in all such copies. 11# 12 13namespace eval NSWidget { 14 15# namespace eval NSWidget 16} 17 18# NSWidget::InitModule -- 19# 20# One-time-only-ever initialization. 21# 22# Arguments: 23# arg1 about arg1 24# 25# Results: 26# What happened. 27 28proc NSWidget::InitModule {} { 29} 30 31# NSWidget::NSWidget -- 32# 33# Object constructor called by NSObject::New(). 34# 35# Arguments: 36# arg1 about arg1 37# 38# Results: 39# What happened. 40 41proc NSWidget::NSWidget {oop parent width height gwidth gheight} { 42 43 set widget $parent.widget$oop 44 45 widget $widget -width $width -height $height \ 46 -gwidth $gwidth -gheight $gheight 47 48 bind $widget <Enter> "NSWidget::Motion $oop %x %y" 49 bind $widget <Motion> "NSWidget::Motion $oop %x %y" 50 bind $widget <Leave> "NSWidget::Leave $oop" 51 52 # Hack -- When we point to a location, the Recall Window may be 53 # set with information, and we may want to interact with the 54 # Recall Window to see the information. But if the mouse moves 55 # over another grid (on the way to the Recall Window) the 56 # information in the Recall Window may change. So we don't 57 # examine cave locations when the Shift key is down. 58 bind $widget <Shift-Enter> break 59 bind $widget <Shift-Motion> break 60 61 # Shift-drag does nothing 62 bind $widget <Shift-Button1-Motion> break 63 64 bind $widget <ButtonPress-1> "NSWidget::TrackPress $oop %x %y" 65 bind $widget <Button1-Motion> "NSWidget::TrackOnce $oop %x %y" 66 67 # Disable tracking when dragging 68 bind $widget <Button1-Enter> break 69 bind $widget <Button1-Leave> break 70 71 bind $widget <MouseWheel> { 72 %W yview scroll [expr {- (%D / 120) * 4}] units 73 } 74 75 Info $oop widget $widget 76 Info $oop examined "" 77 Info $oop examineCmd "" 78 Info $oop leaveCmd "" 79 Info $oop scaleCmd "" 80 Info $oop xviewCmd "" 81 Info $oop yviewCmd "" 82 Info $oop track,mouseMoved 0 83 Info $oop caveyx 0 84 85 # Set the checkmark for the current scale 86 Info $oop scale $gwidth 87 88 return 89} 90 91# NSWidget::Info -- 92# 93# Query and modify info. 94# 95# Arguments: 96# arg1 about arg1 97# 98# Results: 99# What happened. 100 101proc NSWidget::Info {oop info args} { 102 103 global NSWidget 104 105 # Verify the object 106 NSObject::CheckObject NSWidget $oop 107 108 # Set info 109 if {[llength $args]} { 110 set NSWidget($oop,$info) [lindex $args 0] 111 # Get info 112 } else { 113 return $NSWidget($oop,$info) 114 } 115 116 return 117} 118 119# NSWidget::Motion -- 120# 121# Call the client's command when the mouse moves over a grid. 122# 123# Arguments: 124# oop OOP ID. 125# x x location in widget. 126# y y location in widget. 127# 128# Results: 129# What happened. 130 131proc NSWidget::Motion {oop x y} { 132 133 set pos [PointToCave $oop $x $y] 134 if {![string length $pos]} return 135 if {[string equal [Info $oop examined] $pos]} return 136 Info $oop examined $pos 137 138 set command [Info $oop examineCmd] 139 if {[string length $command]} { 140 uplevel #0 $command $oop $pos 141 } 142 143 return 144} 145 146# NSWidget::Leave -- 147# 148# Handle the <Leave> event. 149# 150# Arguments: 151# oop OOP ID. 152# 153# Results: 154# What happened. 155 156proc NSWidget::Leave {oop} { 157 158 Info $oop examined "" 159 160 set command [Info $oop leaveCmd] 161 if {[string length $command]} { 162 uplevel #0 $command $oop 163 } 164} 165 166# NSWidget::PointToCave -- 167# 168# Determine the cave y,x location based on the given 169# coordinates inside the given widget. 170# 171# Arguments: 172# oop OOP ID. 173# x x coordinate in Widget. 174# y y coordinate in Widget. 175# 176# Results: 177# Return "y x". 178 179proc NSWidget::PointToCave {oop x y} { 180 181 set widget [Info $oop widget] 182 183 # Normally, we want to know which grid the point is over, 184 # and for isometric view this requires accurate hittesting 185 # of the actual icons near the point, instead of just the 186 # floor tile. 187 if {![Info $oop caveyx]} { 188 set str [$widget hittest $x $y] 189 if {[string length $str]} { 190 scan $str "%d %d" cy cx 191 set str "$cy $cx" 192 } 193 return $str 194 } 195 196 # Vault editor wants floor tile. 197 return [$widget caveyx $x $y] 198} 199 200# NSWidget::SetScale -- 201# 202# Sets the resolution of the Widget, but doesn't let the Widget 203# get any larger than its original dimensions. 204# 205# Arguments: 206# arg1 about arg1 207# 208# Results: 209# What happened. 210 211proc NSWidget::SetScale {oop scale} { 212 213 set widget [Info $oop widget] 214 215 if {$scale == [$widget cget -gwidth]} return 216 217 $widget configure -gwidth $scale -gheight $scale 218 219 # Context menu 220 Info $oop scale $scale 221 222 # Hack -- Fully update the widget 223 $widget wipe 224 eval $widget center [$widget center] 225 226 set command [Info $oop scaleCmd] 227 if {[string length $command]} { 228 uplevel #0 $command 229 } 230 231 return 232} 233 234# NSWidget::Resize -- 235# 236# Change the size of the widget. 237# 238# Arguments: 239# arg1 about arg1 240# 241# Results: 242# What happened. 243 244proc NSWidget::Resize {oop width height} { 245 246 set widget [Info $oop widget] 247 248if 0 { 249 if {($width == [$widget cget -width]) && \ 250 ($height == [$widget cget -height])} { 251 return 0 252 } 253} 254 255 $widget configure -width $width -height $height 256 257if 0 { 258 # Hack -- Fully update the widget 259 $widget wipe 260 eval $widget center [$widget center] 261} 262 return 1 263} 264 265proc NSWidget::Size {oop _height _width} { 266 267 upvar $_height height 268 upvar $_width width 269 270 set widget [Info $oop widget] 271 272 scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max 273 set height [expr {$y_max - $y_min + 1}] 274 set width [expr {$x_max - $x_min + 1}] 275 276 return 277} 278 279proc NSWidget::CaveSize {oop _height _width} { 280 281 upvar $_height height 282 upvar $_width width 283 284 set widget [Info $oop widget] 285 286 set h [angband cave height] 287 set w [angband cave width] 288 289 Size $oop h2 w2 290 if {$h > $h2} { 291 incr h 2 292 } 293 if {$w > $w2} { 294 incr w 2 295 } 296 297 set height $h 298 set width $w 299 300 return 301} 302 303 304# NSWidget::yview -- 305# 306# Typical yview command 307# 308# Arguments: 309# arg1 about arg1 310# 311# Results: 312# What happened. 313 314proc NSWidget::yview {oop cmd args} { 315 316 set widget [Info $oop widget] 317 318 scan [$widget center] "%d %d" oy ox 319 320 scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max 321 set height [expr {$y_max - $y_min + 1}] 322 323 set caveHgt [angband cave height] 324 325 if {$caveHgt > $height} { 326 incr caveHgt 2 327 set fiddle -1 328 } else { 329 set fiddle 0 330 } 331 332 switch $cmd { 333 334 moveto { 335 set fraction [lindex $args 0] 336 if {$fraction > 1.0} { 337 set fraction 1.0 338 } elseif {$fraction < 0} { 339 set fraction 0 340 } 341 set top [expr {int($fraction * double($caveHgt) + 0.5)}] 342 incr top $fiddle 343 set ny [expr {$top + $height / 2}] 344 } 345 346 scroll { 347 348 set number [lindex $args 0] 349 set what [lindex $args 1] 350 351 switch $what { 352 353 units { 354 set ny [expr {$oy + $number}] 355 } 356 357 pages { 358 set pageSize [expr {$height - 10}] 359 set ny [expr {$oy + $number * $pageSize}] 360 } 361 } 362 } 363 } 364 365 set ny [ConstrainCenter $ny $caveHgt $height] 366 367 # Do nothing if position unchanged 368 if {$oy == $ny} return 369 370 $widget center $ny $ox 371 372 set command [Info $oop yviewCmd] 373 if {[string length $command]} { 374 uplevel #0 $command 375 } 376 377 return 378} 379 380# NSWidget::xview -- 381# 382# Typical xview command 383# 384# Arguments: 385# arg1 about arg1 386# 387# Results: 388# What happened. 389 390proc NSWidget::xview {oop cmd args} { 391 392 set widget [Info $oop widget] 393 394 scan [$widget center] "%d %d" oy ox 395 396 scan [$widget bounds] "%d %d %d %d" y_min x_min y_max x_max 397 set width [expr {$x_max - $x_min + 1}] 398 399 set caveWid [angband cave width] 400 401 if {$caveWid > $width} { 402 incr caveWid 2 403 set fiddle -1 404 } else { 405 set fiddle 0 406 } 407 408 switch $cmd { 409 410 moveto { 411 set fraction [lindex $args 0] 412 if {$fraction > 1.0} { 413 set fraction 1.0 414 } elseif {$fraction < 0} { 415 set fraction 0 416 } 417 set left [expr {int($fraction * double($caveWid) + 0.5)}] 418 incr left $fiddle 419 set nx [expr {$left + $width / 2}] 420 } 421 422 scroll { 423 424 set number [lindex $args 0] 425 set what [lindex $args 1] 426 427 switch $what { 428 429 units { 430 set nx [expr {$ox + $number}] 431 } 432 433 pages { 434 set pageSize [expr {$width - 10}] 435 set nx [expr {$ox + $number * $pageSize}] 436 } 437 } 438 } 439 } 440 441 set nx [ConstrainCenter $nx $caveWid $width] 442 443 # Do nothing if position unchanged 444 if {$ox == $nx} return 445 446 $widget center $oy $nx 447 448 set command [Info $oop xviewCmd] 449 if {[string length $command]} { 450 uplevel #0 $command 451 } 452 453 return 454} 455 456 457# NSWidget::TrackPress -- 458# 459# Handles <ButtonPress-1> events 460# 461# Arguments: 462# arg1 about arg1 463# 464# Results: 465# What happened. 466 467proc NSWidget::TrackPress {oop x y} { 468 469 Info $oop track,x $x 470 Info $oop track,y $y 471 Info $oop track,mouseMoved 0 472 473 return 474} 475 476# NSWidget::TrackOnce -- 477# 478# Handles <Button1-Motion> events 479# 480# Arguments: 481# arg1 about arg1 482# 483# Results: 484# What happened. 485 486proc NSWidget::TrackOnce {oop x y} { 487 488 # Get the widget 489 set widget [Info $oop widget] 490 491 # Get the scale 492 set scale [$widget cget -gwidth] 493 494 # Calculate the distance the pointer moved 495 set dx [expr {[Info $oop track,x] - $x}] 496 set dy [expr {[Info $oop track,y] - $y}] 497 498 # Require minimum movement 499 if {abs($dx) < $scale} { 500 set dx 0 501 } 502 if {abs($dy) < $scale} { 503 set dy 0 504 } 505 506 # If the pointer didn't move, do nothing 507 if {!$dx && !$dy} { 508 return 509 } 510 511 # Remember the pointer moved 512 Info $oop track,mouseMoved 1 513 514 # Remember the current center 515 scan [$widget center] "%d %d" oy ox 516 517 # We should scroll horizontally 518 if {$dx} { 519 520 # Convert from pixels to grid size 521 set dx [expr {$dx / $scale}] 522 523 # Scroll the Widget 524 xview $oop scroll $dx units 525 } 526 527 # We should scroll vertically 528 if {$dy} { 529 530 # Convert from pixels to grid size 531 set dy [expr {$dy / $scale}] 532 533 # Scroll the Widget 534 yview $oop scroll $dy units 535 } 536 537 # Get the new center 538 scan [$widget center] "%d %d" ny nx 539 540 # Remember the current pointer position 541 if {$nx != $ox} { 542 Info $oop track,x $x 543 } 544 545 # Remember the current pointer position 546 if {$ny != $oy} { 547 Info $oop track,y $y 548 } 549 550 return 551} 552 553# WidgetCenter -- 554# 555# When the character goes to a new level (or WOR back to a level) this 556# routine sets the center of the given widget. The widget is centered 557# on the character position. 558# 559# Arguments: 560# widget Widget to center 561# 562# Results: 563# What happened. 564 565proc WidgetCenter {widget} { 566 567 scan [angband player position] "%d %d" y x 568 569 $widget center $y $x 570 571 return "$y $x" 572} 573 574# ClipCenter -- 575# 576# Helper command used control scrolling of a widget when updating the 577# character's position. 578# 579# Arguments: 580# _coord Name of variable holding 581# center Current widget center. 582# units Cave height or width. 583# units2 Widget height or width. 584# 585# Results: 586# What happened. 587 588proc ClipCenter {_coord center units units2} { 589 590 upvar $_coord coord 591 592 set min [expr {$center - $units2 / 2}] 593 set max [expr {$min + $units2 - 1}] 594 set bord [expr {$units2 / 8}] 595 set pad [expr {$units2 / 4}] 596 if {$coord < $min + $bord} { 597 set coord [expr {($coord + $pad) - $units2 / 2}] 598 if {$units2 % 2 == 0} {incr coord} 599 set scroll 1 600 } elseif {$coord > $max - $bord} { 601 set coord [expr {($coord - $pad) + $units2 / 2}] 602 set scroll 1 603 } else { 604 set coord $center 605 set scroll 0 606 } 607 608 if {$scroll} { 609 if {$units > $units2} { 610 set centerMin [expr {$units2 / 2 - 1}] 611 set centerMax [expr {$units - $units2 / 2 + 1}] 612 if {$units2 & 1} {incr centerMax -1} 613 if {$coord < $centerMin} { 614 set coord $centerMin 615 } elseif {$coord > $centerMax} { 616 set coord $centerMax 617 } elseif {$coord == $centerMin + 1} { 618 set coord $centerMin 619 } elseif {$coord == $centerMax - 1} { 620 set coord $centerMax 621 } 622 } else { 623 set coord [expr {($units - $units2) / 2 + $units2 / 2}] 624 } 625 } 626 627 return $scroll 628} 629 630# ConstrainCenter -- 631# 632# Call this when you want to set the x/y center of a widget but do 633# not want the widget to scroll "too far". This calculation adds a 634# 1-grid border around the edge of the cave. 635# 636# Arguments: 637# arg1 about arg1 638# 639# Results: 640# What happened. 641 642proc ConstrainCenter {coord units units2} { 643 644 if {$units > $units2} { 645 set centerMin [expr {$units2 / 2 - 1}] 646 set centerMax [expr {$units - $units2 / 2 - 1}] 647 if {$units2 & 1} {incr centerMax -1} 648 if {$coord < $centerMin} { 649 set coord $centerMin 650 } elseif {$coord > $centerMax} { 651 set coord $centerMax 652 } 653 } else { 654 set coord [expr {($units - $units2) / 2 + $units2 / 2}] 655 } 656 657 return $coord 658} 659 660