display: make function! [ "Displays widgets in a centered window with a title." title [string!] "Window title" spec [block!] "Block of widgets, attributes and keywords" /dialog "Displays widgets in a modal popup window with /parent option" /maximize "Maximize window" /parent "Force parent to be last window (default is first)" /position "Use an alternative positioning scheme" offset [pair! word! block!] "Offset pair or one or more of 'left 'right 'top 'bottom 'first 'second" /min-size "Specify a minimum OS window resize size" size [pair!] "Minimum display size (including window border/title)" /close "Handle window close event" closer [block!] "The close handler block" /local tooltip-time tooltip ][ ; prevent duplicate display being opened foreach window view*/screen-face/pane [all [title = window/text exit]] ; parse block spec into face object spec: layout spec spec/text: title ; position? if position [ either pair? offset [ spec/offset: max 0x0 offset ][ foreach word compose [(offset)] [ if word = 'first [word: either view*/screen-face/size/x > view*/screen-face/size/y ['left] ['top]] if word = 'second [word: either view*/screen-face/size/x > view*/screen-face/size/y ['right] ['bottom]] do select [ left [spec/offset/x: max 0 view*/screen-face/size/x / 2 - spec/size/x / 2] right [spec/offset/x: max 0 view*/screen-face/size/x / 2 - spec/size/x / 2 + (view*/screen-face/size/x / 2)] top [spec/offset/y: max 0 view*/screen-face/size/y / 2 - spec/size/y / 2] bottom [spec/offset/y: max 0 view*/screen-face/size/y / 2 - spec/size/y / 2 + (view*/screen-face/size/y / 2)] ] word ] ] ] ; make window after 1st a child of 1st (or last if parent refinement used) unless empty? view*/screen-face/pane [ either view*/screen-face/pane/1/type <> 'splash [ insert tail spec/options reduce ['parent either any [dialog parent] [last view*/screen-face/pane] [first view*/screen-face/pane]] ] [unview] ] ; resize window? either any [min-size maximize] [ insert tail spec/options 'resize all [maximize spec/changes: [maximize]] ][ ; do any sub-faces require resize? foreach sub-face spec/pane [ all [ sub-face/span not empty? intersect sub-face/span #HWXY insert tail spec/options 'resize break ] ] ] ; min-size? all [ find spec/options 'resize insert tail spec/options reduce ['min-size either min-size [size] [spec/size + view*/title-size + view*/resize-border]] ] ; popup or display? either dialog [ spec/type: 'popup spec/feel: system/words/face/feel show-popup spec ] [view/new spec] ; add window feel to handle resize, focus and/or keycodes all [close spec/action: make function! [face /local var] closer] spec/feel: make any [spec/feel widgets/default-feel] [ orig-size: spec/size mouse-offset: 0x0 ; tooltip code if all [not dialog effects/tooltip-delay] [ tooltip-time: now/time/precise insert tail spec/pane tooltip: make widgets/tooltip [type: 'tooltip offset: -10000x-10000 tip: none] ] detect: make function! [face event /local f] [ ; The following code prevents tooltips being lost after returning from ; a requestor. Seems like the requestor detect function replaces the ; calling display's detect function! if none? tooltip [ f: last face/pane if f/type = 'tooltip [ tooltip-time: now/time/precise tooltip: last face/pane ] ] if all [ face/type <> 'popup ;not dialog effects/tooltip-delay tooltip/data event/type <> 'time mouse-offset <> event/offset ] [ tooltip-time: now/time/precise tooltip/data: false tooltip/offset: -10000x-10000 show tooltip ] if all [ face/type <> 'popup ;not dialog effects/tooltip-delay not tooltip/data (now/time/precise - tooltip-time) > effects/tooltip-delay ] [ f: event/face while [f: find-face event/offset f] [ if all [f/type <> 'face f/tip] [ tooltip/text: f/tip tooltip/init tooltip/size: 10000x10000 tooltip/size: 8 + size-text tooltip poke tooltip/effect/draw 9 tooltip/size - 1x1 tooltip/offset: min event/face/size - tooltip/size - 2 max 2x2 event/offset - as-pair 0 tooltip/size/y tooltip/data: true if all [ tooltip/parent-face block? tooltip/parent-face/pane ][ remove find tooltip/parent-face/pane tooltip ] insert tail event/face/pane tooltip show tooltip break ] if function? get in f 'pane [break] unless f: f/pane [break] ] ] ; unfocus if a different widget is clicked on if find [down up alt-down alt-up] event/type [ if all [ view*/focal-face ; face <> view*/focal-face (hack borrowed from VID) not within? event/offset win-offset? view*/focal-face view*/focal-face/size ] [unless edit/unfocus [exit]] ] do select [ key [ case [ event/key = #"^-" [ if all [view*/focal-face viewed? view*/focal-face] [ ; find previous/next tabbable field f: either event/shift [edit/back-field view*/focal-face] [edit/next-field view*/focal-face] if find behaviors/action-on-tab view*/focal-face/type [ view*/focal-face/action/on-click view*/focal-face ] if :f [set-focus f] exit ] ] find [#" " #"^M"] event/key [ if all [view*/focal-face view*/focal-face/type = 'button] [ view*/focal-face/action/on-click view*/focal-face exit ] ] all [ find [f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12] event/key get in on-fkey event/key ][ on-fkey/(event/key) face event exit ] ; if key is assigned to a click action do it any [not view*/focal-face view*/focal-face/type = 'button] [ either f: select face/keycodes event/key [ f/action/on-click f exit ][ if event/key = #"^[" [ ; Hide popup on ESC if find view*/pop-list view*/pop-face [hide-popup exit] ; Hide choose if it currently doesn't have focus if all [view*/pop-face view*/pop-face/type = 'choose] [hide-popup exit] ; exit if closer action returns false all [get in face 'action not face/action face exit] ; prompt to quit if first window of multiple is closed if all [face = pick view*/screen-face/pane 1 1 <> length? view*/screen-face/pane] [ either question "Do you really want to quit this application?" [quit] [exit] ] unview/only face exit ] ] ] ] ] move [mouse-offset: event/offset] resize [ all [face/size <> orig-size span-resize face face/size - orig-size] show face orig-size: face/size exit ] close [ ; unfocus if view*/focal-face [ view*/focal-face: view*/caret: none edit/unlight-text ] ; exit if closer action returns false all [get in face 'action not face/action face exit] ; prompt to quit if first window of multiple is closed if all [face = pick view*/screen-face/pane 1 1 <> length? view*/screen-face/pane] [ either question "Do you really want to quit this application?" [quit] [exit] ] ] ] event/type event ] ] either dialog [do-events] [show spec spec] ]