diff --git a/doc/html/box.html b/doc/html/box.html index d391ccc5..597e0805 100644 --- a/doc/html/box.html +++ b/doc/html/box.html @@ -106,6 +106,14 @@
+ In the Tcl/Tk GUI wrapper, the Cursor tool also supports creating + a box by pressing the left mouse button and dragging to the + opposite corner. The box is normalized while dragging, so the + initial point may be any corner; dragging toward the left, right, + top, or bottom produces the corresponding lower-left and upper-right + coordinates. The upper-right corner is updated continuously until + the button is released.
+ For a discussion of valid directions, see the page direction. Note that special cases box grow center and diff --git a/doc/html/maketoolbar.html b/doc/html/maketoolbar.html index ce5e1c33..d02fb4ac 100644 --- a/doc/html/maketoolbar.html +++ b/doc/html/maketoolbar.html @@ -42,12 +42,21 @@The maketoolbar command generates the toolbar for the GUI layout window. The toolbar contains a set of buttons - representing each layer type in the technology file. - Each toolbar button has bindings for mouse buttons and - keys to implement shortcut commands in magic. - While the mouse pointer is inside the boundary of the - toolbar button, the name of the layer represented by the - toolbar is printed in the title bar of the window.+ The toolbar contains a set of interaction-tool buttons and a + set of buttons representing each layer type in the technology file. + The interaction buttons select the Cursor, Wire, Net, and Pick + tools. The Ruler button creates a ruler from the current cursor + box. Selecting a different tool while an unfinished Wire or Pick + interaction is active cancels that interaction before switching. + Each layer toolbar button has bindings for mouse buttons and keys + to implement shortcut commands in magic. While the mouse + pointer is inside the boundary of a toolbar button, its name is + printed in the title bar of the window.
+ + The Options menu also provides the Embedded Console toggle. + It opens a Tcl command console docked at the bottom of the layout + window and hides it again when disabled. The console supports + command history and displays Magic output and errors.
diff --git a/doc/html/zoom.html b/doc/html/zoom.html index 2e717064..64d00539 100644 --- a/doc/html/zoom.html +++ b/doc/html/zoom.html @@ -44,7 +44,11 @@ Usage:
Summary:
The zoom command implements view scaling in the layout - window in and out by the indicated magnification factor.+ window in and out by the indicated magnification factor. When + invoked from a layout window, the point under the cursor remains + fixed on the screen while the view is scaled. This makes it + possible to zoom into a particular layout feature without first + recentering the window.
Note that several other commands effect a change in the view scale factor, including findbox zoom and view with diff --git a/tcltk/toolbar.tcl b/tcltk/toolbar.tcl index d8f68e22..4c5ac22b 100644 --- a/tcltk/toolbar.tcl +++ b/tcltk/toolbar.tcl @@ -40,11 +40,14 @@ proc magic::maketoolbar {framename} { set all_layers [concat $special_layers [magic::tech unlocked]] } + # Add clickable buttons for the available interaction tools. + magic::makeToolButtons $framename + # Create a canvas for the toolbar if {![winfo exists ${framename}.toolbar.canvas]} { canvas ${framename}.toolbar.canvas } - grid ${framename}.toolbar.canvas -row 0 -column 0 -sticky "news" + grid ${framename}.toolbar.canvas -row 1 -column 0 -sticky "news" # Add a frame to the canvas, on which the layer buttons and # labels are placed @@ -90,7 +93,7 @@ proc magic::maketoolbar {framename} { scrollbar ${framename}.toolbar.vscroll -orient "vertical" \ -command [list ${framename}.toolbar.canvas yview] - grid ${framename}.toolbar.vscroll -row 0 -column 1 -sticky "nws" + grid ${framename}.toolbar.vscroll -row 1 -column 1 -sticky "nws" # Configure the canvas to use the scrollbar ${framename}.toolbar.canvas configure -yscrollcommand \ @@ -100,6 +103,60 @@ proc magic::maketoolbar {framename} { bind ${framename}
"updateCanvasScrollRegion ${framename}" } +# Create the compact interaction-tool strip above the layer toolbar. +proc magic::makeToolButtons {framename} { + global Opts + set Opts(toolframe) $framename + + if {[winfo exists ${framename}.toolbar.tools]} { + destroy ${framename}.toolbar.tools + } + frame ${framename}.toolbar.tools -relief groove -borderwidth 1 + grid ${framename}.toolbar.tools -row 0 -column 0 -columnspan 2 -sticky "ew" + + set tools { + {box "Cursor" "Select and move the cursor box"} + {wiring "Wire" "Draw and edit wires"} + {nettool "Net" "Select and edit nets"} + {pick "Pick" "Select and move layout objects"} + } + foreach toolinfo $tools { + lassign $toolinfo tool label help + set button ${framename}.toolbar.tools.$tool + button $button -text $label -width 7 -padx 2 -pady 1 \ + -command [list magic::selectToolFromToolbar $tool] + pack $button -side top -fill x -padx 2 -pady 1 + bind $button [list ${framename}.titlebar.message configure -text $help] + bind $button [list ${framename}.titlebar.message configure -text ""] + } + + # Ruler is an action rather than a persistent button tool. It uses the + # current cursor box and therefore does not change the active tool. + set ruler ${framename}.toolbar.tools.ruler + button $ruler -text "Ruler" -width 7 -padx 2 -pady 1 \ + -command {magic::ruler} + pack $ruler -side top -fill x -padx 2 -pady 1 + bind $ruler [list ${framename}.titlebar.message configure \ + -text "Create a ruler from the cursor box"] + bind $ruler [list ${framename}.titlebar.message configure -text ""] + + magic::updateToolButtons $framename +} + +# Keep the selected tool visibly highlighted after a click or keyboard change. +proc magic::updateToolButtons {framename} { + global Opts + foreach tool {box wiring nettool pick} { + set button ${framename}.toolbar.tools.$tool + if {![winfo exists $button]} { continue } + if {[info exists Opts(tool)] && $Opts(tool) == $tool} { + $button configure -relief sunken -bg lightblue + } else { + $button configure -relief raised -bg [${framename}.toolbar cget -background] + } + } +} + # Function to place layer frame with a button and label # on the toolbar, at a specific row $i proc createLayerFrame {framename layername i} { diff --git a/tcltk/tools.tcl b/tcltk/tools.tcl index 6cfc0089..934f490a 100644 --- a/tcltk/tools.tcl +++ b/tcltk/tools.tcl @@ -693,6 +693,35 @@ proc magic::cancelselect {window} { } } +# Switch tools from the GUI toolbar. A toolbar click is an explicit request +# to leave the current interaction, so cancel an unfinished wire or pick first. +proc magic::selectToolFromToolbar {type} { + global Opts + + if {[info exists Opts(motion)] && $Opts(motion) != {}} { + # Opts(focus) is the layout toplevel (for example .layout1). + # The Magic canvas is nested below the paned window. + if {[info exists Opts(focus)]} { + set window ${Opts(focus)}.pane.top.magic + if {[info exists Opts(tool)] && $Opts(tool) == "wiring"} { + magic::trackwire $window cancel + } elseif {[info exists Opts(tool)] && $Opts(tool) == "pick"} { + magic::cancelselect $window + } + } + } + magic::tool $type + + # Keep keyboard shortcuts such as `g` (grid) directed to the layout + # canvas after a toolbar button is clicked. + if {[info exists Opts(focus)]} { + set window ${Opts(focus)}.pane.top.magic + if {[winfo exists $window]} { + focus $window + } + } +} + #--------------------------------------------------------------------- # tool --- A scripted replacement for the "tool" # command, as handling of button events has been modified @@ -782,7 +811,8 @@ proc magic::tool {{type next}} { } } - # Update window captions with the new tool info + # Update window captions and the clickable toolbar state. catch {magic::captions} + catch {magic::updateToolButtons $Opts(toolframe)} return } diff --git a/tcltk/wrapper.tcl b/tcltk/wrapper.tcl index f1ea6cec..a63865fa 100644 --- a/tcltk/wrapper.tcl +++ b/tcltk/wrapper.tcl @@ -648,6 +648,54 @@ proc magic::repaintwrapper { win } { } +# Start a modern click-drag box gesture. Return "break" only for the +# box tool so that wiring, picking, and netlist tools retain their bindings. +proc magic::startboxdrag {win x y} { + global Opts + if {![info exists Opts(tool)] || $Opts(tool) != "box"} {return} + *bypass setpoint $x $y $win + set point [${win} cursor internal] + ${win} box values {*}$point {*}$point + set Opts(boxdrag) $win + set Opts(boxdrag_start) $point + return break +} + +proc magic::handlelayoutmotion {win x y} { + *bypass setpoint $x $y $win + set result [magic::updateboxdrag $win $x $y] + magic::cursorview $win + return $result +} + +proc magic::updateboxdrag {win x y} { + global Opts + if {![info exists Opts(boxdrag)] || $Opts(boxdrag) != $win} {return} + + set start $Opts(boxdrag_start) + set current [${win} cursor internal] + set x1 [lindex $start 0] + set y1 [lindex $start 1] + set x2 [lindex $current 0] + set y2 [lindex $current 1] + set llx [expr {min($x1, $x2)}] + set lly [expr {min($y1, $y2)}] + set urx [expr {max($x1, $x2)}] + set ury [expr {max($y1, $y2)}] + ${win} box values $llx $lly $urx $ury + return break +} + +proc magic::endboxdrag {win x y} { + global Opts + if {![info exists Opts(boxdrag)] || $Opts(boxdrag) != $win} {return} + *bypass setpoint $x $y $win + magic::updateboxdrag $win $x $y + set Opts(boxdrag) {} + set Opts(boxdrag_start) {} + return break +} + # Coordinate display callback function # Because "box" calls "box", use the "info level" command to avoid # infinite recursion. @@ -683,8 +731,13 @@ proc magic::boxview {win {cmdstr ""}} { if {[expr {$blly == int($blly)}]} {set blly [expr {int($blly)}]} if {[expr {$burx == int($burx)}]} {set burx [expr {int($burx)}]} if {[expr {$bury == int($bury)}]} {set bury [expr {int($bury)}]} - set titletext [format "box (%+g %+g) to (%+g %+g) microns" \ - $bllx $blly $burx $bury] + set width [expr {$burx - $bllx}] + set height [expr {$bury - $blly}] + set area [expr {$width * $height}] + set centerx [expr {($bllx + $burx) / 2.0}] + set centery [expr {($blly + $bury) / 2.0}] + set titletext [format "box LL (%+g,%+g) UR (%+g,%+g) | size %g x %g um | center (%+g,%+g) | area %g um2" \ + $bllx $blly $burx $bury $width $height $centerx $centery $area] units {*}$curunits ${framename}.titlebar.pos configure -text $titletext } @@ -712,13 +765,18 @@ proc magic::cursorview {win} { if {$gotbox} { set curunits [${win} units list] ${win} units microns noprint - set dlst [${win} box position] - - set dx [expr {$olstx - [lindex $dlst 0]}] - set dy [expr {$olsty - [lindex $dlst 1]}] - if {[expr {$dx == int($dx)}]} {set dx [expr {int($dx)}]} - if {[expr {$dy == int($dy)}]} {set dy [expr {int($dy)}]} - set titletext [format "(%+g %+g) %+g %+g microns" $olstx $olsty $dx $dy] + set bval [${win} box values] + set bllx [lindex $bval 0] + set blly [lindex $bval 1] + set burx [lindex $bval 2] + set bury [lindex $bval 3] + set width [expr {$burx - $bllx}] + set height [expr {$bury - $blly}] + set area [expr {$width * $height}] + set dx [expr {$olstx - $bllx}] + set dy [expr {$olsty - $blly}] + set titletext [format "cursor (%+g,%+g) um | box LL (%+g,%+g) UR (%+g,%+g) | size %g x %g um | delta %+g,%+g | area %g um2" \ + $olstx $olsty $bllx $blly $burx $bury $width $height $dx $dy $area] ${framename}.titlebar.pos configure -text $titletext ${win} units {*}$curunits } else { @@ -1186,14 +1244,15 @@ proc magic::openwrapper {{cell ""} {framename ""}} { magic::repaintwrapper ${layoutframe} grid ${layoutframe}.titlebar -row 0 -column 0 -columnspan 3 -sticky news - grid ${layoutframe}.yscroll -row 1 -column 0 -sticky ns - grid $winname -row 1 -column 1 -sticky news - grid ${layoutframe}.zb -row 2 -column 0 - grid ${layoutframe}.xscroll -row 2 -column 1 -sticky ew + grid ${layoutframe}.toolbar -row 1 -column 0 -rowspan 2 -sticky nws + grid ${layoutframe}.yscroll -row 1 -column 1 -sticky ns + grid $winname -row 1 -column 2 -sticky news + grid ${layoutframe}.zb -row 2 -column 1 + grid ${layoutframe}.xscroll -row 2 -column 2 -sticky ew # The toolbar is not attached by default grid rowconfigure ${layoutframe} 1 -weight 1 - grid columnconfigure ${layoutframe} 1 -weight 1 + grid columnconfigure ${layoutframe} 2 -weight 1 grid ${layoutframe}.titlebar.mbuttons -row 0 -column 0 -sticky news grid ${layoutframe}.titlebar.drcbutton -row 0 -column 1 -sticky news @@ -1210,8 +1269,16 @@ proc magic::openwrapper {{cell ""} {framename ""}} { # this command with the "bypass" command such that it does not # reset any current input redirection to the terminal. - bind ${winname} "*bypass setpoint %x %y ${winname}; \ - magic::cursorview ${winname}" + bind ${winname} \ + [list magic::handlelayoutmotion $winname %x %y] + + # Modern box-tool gesture: left-drag creates a new box from the + # press point to the current cursor position. Other tools continue + # to use their normal button macros. + bind $winname \ + [list magic::startboxdrag $winname %x %y] + bind $winname \ + [list magic::endboxdrag $winname %x %y] if {[catch {set Winopts(${framename},toolbar)}]} { set Winopts(${framename},toolbar) 1 @@ -1373,7 +1440,7 @@ proc magic::openwrapper {{cell ""} {framename ""}} { $m add check -label "Toolbar" -variable Winopts(${framename},toolbar) \ -command [subst {if { \$Winopts(${framename},toolbar) } { \ magic::maketoolbar ${layoutframe} ; \ - grid ${layoutframe}.toolbar -row 1 -column 2 -rowspan 2 -sticky new ; \ + grid ${layoutframe}.toolbar -row 1 -column 0 -rowspan 2 -sticky nws ; \ } else { \ grid forget ${layoutframe}.toolbar } }] @@ -1439,12 +1506,9 @@ proc magic::openwrapper {{cell ""} {framename ""}} { magic::render3d \[${winname} cellname list window\] \ } }] - $m add check -label "Window Command Entry" \ + $m add check -label "Embedded Console" \ -variable Winopts(${framename},cmdentry) \ - -command [subst { if { \$Winopts(${framename},cmdentry) } { \ - addcommandentry $framename \ - } else { \ - deletecommandentry $framename } }] + -command [list magic::togglecommandentry $framename] $m add check -label "Crosshair" \ -variable Opts(crosshair) \ @@ -1462,7 +1526,7 @@ proc magic::openwrapper {{cell ""} {framename ""}} { # If the toolbar is turned on, invoke the toolbar button if { $Winopts(${framename},toolbar) == 1} { magic::maketoolbar ${layoutframe} - grid ${layoutframe}.toolbar -row 1 -column 2 -rowspan 2 -sticky new + grid ${layoutframe}.toolbar -row 1 -column 0 -rowspan 2 -sticky nws } # If the command entry window is enabled, create it now @@ -1535,8 +1599,20 @@ proc magic::closewrapper { framename } { destroy $framename } +# Toggle the embedded Tcl console in the bottom pane of a wrapper window. +# This is the in-application alternative to the external TkCon window. +proc magic::togglecommandentry { framename } { + global Winopts + + if {$Winopts(${framename},cmdentry)} { + magic::addcommandentry $framename + } else { + magic::deletecommandentry $framename + } +} + # This procedure adds a command-line entry window to the bottom of -# a wrapper window (rudimentary functionality---incomplete) +# a wrapper window. proc magic::addcommandentry { framename } { set commandframe ${framename}.pane.bot diff --git a/windows/windClient.c b/windows/windClient.c index b92fc39d..51efac4c 100644 --- a/windows/windClient.c +++ b/windows/windClient.c @@ -468,13 +468,13 @@ windFrameButtons(w, cmd) switch (cmd->tx_button) { case TX_LEFT_BUTTON: - WindZoom(w, 2.0); + WindZoomAt(w, 2.0, &cmd->tx_p); break; case TX_MIDDLE_BUTTON: WindView(w); break; case TX_RIGHT_BUTTON: - WindZoom(w, 0.5); + WindZoomAt(w, 0.5, &cmd->tx_p); break; } return TRUE; diff --git a/windows/windCmdSZ.c b/windows/windCmdSZ.c index 63bd3e6e..33663bad 100644 --- a/windows/windCmdSZ.c +++ b/windows/windCmdSZ.c @@ -1263,5 +1263,5 @@ windZoomCmd(w, cmd) return; } - WindZoom(w, factor); + WindZoomAt(w, factor, &cmd->tx_p); } diff --git a/windows/windView.c b/windows/windView.c index fb201dd5..253483dd 100644 --- a/windows/windView.c +++ b/windows/windView.c @@ -232,19 +232,31 @@ WindMove(w, surfaceArea) */ void -WindZoom(w, factor) +WindZoomAt(w, factor, screenPoint) MagWindow *w; /* the window to be zoomed */ - float factor; /* The amount to zoom by (1 is no change), - * greater than 1 is a larger magnification - * (zoom in), and less than 1 is less mag. - * (zoom out) ) - */ + float factor; /* The amount to zoom by (1 is no change), + * greater than 1 is a larger magnification + * (zoom in), and less than 1 is less mag. + * (zoom out) ) + */ + Point *screenPoint; /* Point to keep fixed on screen, or NULL. */ { int centerx, centery; + Point anchor; Rect newArea; - centerx = (w->w_surfaceArea.r_xbot + w->w_surfaceArea.r_xtop) / 2; - centery = (w->w_surfaceArea.r_ybot + w->w_surfaceArea.r_ytop) / 2; + if (screenPoint != NULL) + { + WindPointToSurface(w, screenPoint, &anchor, (Rect *) NULL); + } + else + { + anchor.p_x = (w->w_surfaceArea.r_xbot + w->w_surfaceArea.r_xtop) / 2; + anchor.p_y = (w->w_surfaceArea.r_ybot + w->w_surfaceArea.r_ytop) / 2; + } + + centerx = anchor.p_x; + centery = anchor.p_y; newArea.r_xbot = centerx - (centerx - w->w_surfaceArea.r_xbot) * factor; newArea.r_xtop = centerx + (w->w_surfaceArea.r_xtop - centerx) * factor; @@ -254,6 +266,14 @@ WindZoom(w, factor) WindMove(w, &newArea); } +void +WindZoom(w, factor) + MagWindow *w; + float factor; +{ + WindZoomAt(w, factor, (Point *) NULL); +} + /* * ---------------------------------------------------------------------------- * diff --git a/windows/windows.h b/windows/windows.h index cd1b2c09..40c694ab 100644 --- a/windows/windows.h +++ b/windows/windows.h @@ -263,6 +263,7 @@ extern MagWindow *WindSearchData(); /* procs for moving the surface inside of a window (changing the view) */ extern void WindZoom(); +extern void WindZoomAt(); extern void WindMove(); extern void WindView(); extern void WindScroll();