! Copyright (C) 2006, 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: arrays assocs io kernel math models namespaces make dlists deques sequences threads sequences words continuations init combinators hashtables concurrency.flags sets accessors calendar fry ui.gadgets ui.gadgets.worlds ui.gadgets.tracks ui.gestures ui.backend ui.render ; IN: ui ! Assoc mapping aliens to gadgets SYMBOL: windows : window ( handle -- world ) windows get-global at ; : window-focus ( handle -- gadget ) window world-focus ; : register-window ( world handle -- ) #! Add the new window just below the topmost window. Why? #! So that if the new window doesn't actually receive focus #! (eg, we're using focus follows mouse and the mouse is not #! in the new window when it appears) Factor doesn't get #! confused and send workspace operations to the new window, #! etc. swap 2array windows get-global push windows get-global dup length 1 > [ [ length 1- dup 1- ] keep exchange ] [ drop ] if ; : unregister-window ( handle -- ) windows global [ [ first = not ] with filter ] change-at ; : raised-window ( world -- ) windows get-global [ [ second eq? ] with find drop ] keep [ nth ] [ delete-nth ] [ nip ] 2tri push ; : focus-gestures ( new old -- ) drop-prefix T{ lose-focus } swap each-gesture T{ gain-focus } swap each-gesture ; : focus-world ( world -- ) t >>focused? dup raised-window focus-path f focus-gestures ; : unfocus-world ( world -- ) f >>focused? focus-path f swap focus-gestures ; M: world graft* [ (open-window) ] [ [ title>> ] keep set-title ] [ request-focus ] tri ; : reset-world ( world -- ) #! This is used when a window is being closed, but also #! when restoring saved worlds on image startup. [ fonts>> clear-assoc ] [ unfocus-world ] [ f >>handle drop ] tri ; : (ungraft-world) ( world -- ) { [ handle>> select-gl-context ] [ fonts>> free-fonts ] [ hand-clicked close-global ] [ hand-gadget close-global ] } cleave ; M: world ungraft* [ (ungraft-world) ] [ handle>> (close-window) ] [ reset-world ] tri ; : find-window ( quot -- world ) windows get values [ gadget-child swap call ] with find-last nip ; inline : init-ui ( -- ) \ graft-queue set-global \ layout-queue set-global \ gesture-queue set-global V{ } clone windows set-global ; : restore-gadget-later ( gadget -- ) dup graft-state>> { { { f f } [ ] } { { f t } [ ] } { { t t } [ { f f } >>graft-state ] } { { t f } [ dup unqueue-graft { f f } >>graft-state ] } } case graft-later ; : restore-gadget ( gadget -- ) dup restore-gadget-later children>> [ restore-gadget ] each ; : restore-world ( world -- ) dup reset-world restore-gadget ; : update-hand ( world -- ) dup hand-world get-global eq? [ hand-loc get-global swap move-hand ] [ drop ] if ; : layout-queued ( -- seq ) [ in-layout? on layout-queue [ dup layout find-world [ , ] when* ] slurp-deque ] { } make prune ; : redraw-worlds ( seq -- ) [ dup update-hand draw-world ] each ; : notify ( gadget -- ) dup graft-state>> [ first { f f } { t t } ? >>graft-state ] keep { { { f t } [ dup activate-control graft* ] } { { t f } [ dup deactivate-control ungraft* ] } } case ; : notify-queued ( -- ) graft-queue [ notify ] slurp-deque ; : send-queued-gestures ( -- ) gesture-queue [ send-queued-gesture notify-queued ] slurp-deque ; : update-ui ( -- ) [ [ notify-queued layout-queued redraw-worlds send-queued-gestures ] assert-depth ] [ ui-error ] recover ; SYMBOL: ui-thread : ui-running ( quot -- ) t \ ui-running set-global [ f \ ui-running set-global ] [ ] cleanup ; inline : ui-running? ( -- ? ) \ ui-running get-global ; : update-ui-loop ( -- ) [ ui-running? ui-thread get-global self eq? and ] [ ui-notify-flag get lower-flag update-ui ] [ ] while ; : start-ui-thread ( -- ) [ self ui-thread set-global update-ui-loop ] "UI update" spawn drop ; : open-world-window ( world -- ) dup pref-dim >>dim dup relayout graft ; : open-window ( gadget title -- ) f open-world-window ; : set-fullscreen? ( ? gadget -- ) find-world set-fullscreen* ; : fullscreen? ( gadget -- ? ) find-world fullscreen* ; : raise-window ( gadget -- ) find-world raise-window* ; HOOK: close-window ui-backend ( gadget -- ) M: object close-window find-world [ ungraft ] when* ; : start-ui ( quot -- ) call notify-ui-thread start-ui-thread ; [ f \ ui-running set-global ui-notify-flag set-global ] "ui" add-init-hook HOOK: (with-ui) ui-backend ( quot -- ) : restore-windows ( -- ) [ windows get [ values ] [ delete-all ] bi [ restore-world ] each forget-rollover ] (with-ui) ; : restore-windows? ( -- ? ) windows get empty? not ; : with-ui ( quot -- ) ui-running? [ call ] [ '[ init-ui @ ] (with-ui) ] if ; HOOK: beep ui-backend ( -- )