-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathivy-canvas.rkt
213 lines (192 loc) · 7.49 KB
/
ivy-canvas.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#lang racket
;
;
(require (only-in plot/utils
clamp-real
ivl)
(only-in racket/gui/base
canvas%)
(only-in "base.rkt"
+root-path+
color-black
get-index
image-bmp-master
image-dir
image-path
load-first-image
load-image
load-last-image
load-next-image
load-previous-image
macosx?
pfs
supported-file?))
(provide (all-defined-out))
(define ivy-canvas%
(class canvas%
(super-new)
(init-field focus-tag-tfield
insert-tag-tfield-comma
paint-callback
set-fullscreen
status-bar-position
status-bar-zoom
toggle-fullscreen
[canvas-backgorund color-black])
(define mouse-x 0)
(define mouse-y 0)
; whether the last zoom operation was "fit"
(define fit #f)
(define/public (get-mouse-pos)
(values mouse-x mouse-y))
(define (do-on-paint)
(when paint-callback
(paint-callback this (send this get-dc))))
(define/override (on-paint)
(do-on-paint))
; proc: ((is-a?/c canvas%) (is-a?/c dc<%>) . -> . any)
(define/public (set-on-paint! proc)
(set! paint-callback proc))
(define/override (on-drop-file pathname)
; append the image to the current collection
(define-values (base name must-be-dir?) (split-path pathname))
(define directory? (directory-exists? pathname))
(cond
; empty collection
[(equal? (first (pfs)) +root-path+)
(cond [directory?
(define files
(for/fold ([lst empty])
([p (in-directory pathname)])
(if (supported-file? p)
(append lst (list p))
lst)))
(image-dir pathname)
(pfs files)
(image-path (first files))
(load-image (first files))]
[else
(image-dir base)
(pfs (list pathname))
(image-path pathname)
(load-image pathname)])]
; collection has images; appending to collection
[else
(define files
(if (directory-exists? pathname)
(for/fold ([lst empty])
([p (in-directory pathname)])
(if (supported-file? p)
(append lst (list p))
lst))
(list pathname)))
; no duplicate paths allowed!
(pfs (remove-duplicates (append (pfs) files)))
; change label because it usually isn't called until
; (load-image) is called and we want to see the changes now
(send (status-bar-position) set-label
(format "~a / ~a"
(+ (get-index (image-path) (pfs)) 1)
(length (pfs))))]))
(define/override (on-event evt)
(define type (send evt get-event-type))
(case type
; track where the mouse is
[(enter motion)
(set! mouse-x (send evt get-x))
(set! mouse-y (send evt get-y))]))
(define/override (on-char key)
(define type (send key get-key-code))
(case type
[(wheel-down)
; do nothing if we've pressed ctrl+n
(unless (equal? (image-path) +root-path+)
(send this zoom-by -0.05))]
[(wheel-up)
; do nothing if we've pressed ctrl+n
(unless (equal? (image-path) +root-path+)
(send this zoom-by 0.05))]
; osx does things a little different
[(f11) (unless macosx?
(toggle-fullscreen this))]
; only do something if we're fullscreened,
; since the tag bar isn't available in fullscreen anyway
[(escape) (when (not macosx?)
(set-fullscreen #f))]
[(left) (load-previous-image)]
[(right) (load-next-image)]
[(home) (load-first-image)]
[(end) (load-last-image)]
[(#\,) (focus-tag-tfield)
(insert-tag-tfield-comma)]
[(#\return) (focus-tag-tfield)]))
(define/private (configure-scrollbars zoom-factor)
(let* ([img-w (send image-bmp-master get-width)]
[img-h (send image-bmp-master get-height)]
[zoomed-img-w (inexact->exact (round (* img-w zoom-factor)))]
[zoomed-img-h (inexact->exact (round (* img-h zoom-factor)))]
[client-w (send this get-width)]
[client-h (send this get-height)]
[virtual-w (max client-w zoomed-img-w)]
[virtual-h (max client-h zoomed-img-h)]
[scroll-x 0.5] ; TODO
[scroll-y 0.5]) ; TODO
(send this init-auto-scrollbars virtual-w virtual-h scroll-x scroll-y)
(send this show-scrollbars
(> zoomed-img-w client-w)
(> zoomed-img-h client-h))))
; zooms to a specific zoom-factor (1.0 == "no zoom"),
; with optional staus bar label override
(define/public (zoom-to factor [status-label #f])
(set! fit #f) ; always make sure this is cleared when setting a new zoom level
(define dc (send this get-dc))
(send dc set-scale factor factor)
(configure-scrollbars factor)
(send this refresh-now)
(send (status-bar-zoom) set-label
(cond [status-label status-label]
[(not (= factor 1.0)) (format "@ ~aX" (~r factor #:precision 2))]
[else ""])))
; zooms view by a specified increment (positive or negative)
(define/public (zoom-by inc)
(define dc (send this get-dc))
(define-values [cur-scale-x cur-scale-y]
(send dc get-scale))
(define new-scale
(clamp-real (+ cur-scale-x inc) (ivl 0.1 4.0)))
(send this zoom-to new-scale))
; adjusts zoom level so the entire image fits, and at least one dimension
; will be the same size as the window.
(define/public (zoom-to-fit)
(let* ([client-w (send this get-width)]
[client-h (send this get-height)]
[img-w (send image-bmp-master get-width)]
[img-h (send image-bmp-master get-height)]
[new-zoom (min (/ client-w img-w)
(/ client-h img-h))])
(send this zoom-to new-zoom "[Fit]")
; must set this *after* calling zoom-to, where it is reset to false
(set! fit #t)))
; only zooms out if the image is too big to fit on either dimension
(define/public (center-fit)
(let ([client-w (send this get-width)]
[client-h (send this get-height)]
[img-w (send image-bmp-master get-width)]
[img-h (send image-bmp-master get-height)])
(cond [(or (> img-w client-w)
(> img-h client-h))
(send this zoom-to-fit)]
[else (send this zoom-to 1.0)])))
(define/override (on-size width height)
(recenter-origin width height)
(if fit
(send this zoom-to-fit)
(send this refresh-now)))
(define/private (recenter-origin width height)
(define dc (send this get-dc))
(send dc set-origin
(/ width 2)
(/ height 2)))
(define/public (recenter)
(define-values [virtual-w virtual-h] (send this get-virtual-size))
(recenter-origin virtual-w virtual-h))))