forked from go-qamel/qamel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
viewer.go
360 lines (299 loc) · 10.2 KB
/
viewer.go
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
package qamel
// #include <stdint.h>
// #include <stdlib.h>
// #include <string.h>
// #include <stdbool.h>
// #include "viewer.h"
import "C"
import (
"fmt"
"os"
fp "path/filepath"
"strings"
"time"
"unsafe"
"github.com/fsnotify/fsnotify"
"github.com/sirupsen/logrus"
)
// Viewer is the QML viewer which wraps QQuickView
type Viewer struct {
ptr unsafe.Pointer
}
// NewViewer constructs a QQuickView.
func NewViewer() Viewer {
ptr := C.Viewer_NewViewer()
return Viewer{ptr: ptr}
}
// NewViewerWithSource constructs a QQuickView with the given QML source.
func NewViewerWithSource(source string) Viewer {
view := NewViewer()
view.SetSource(source)
return view
}
// SetSource sets the source to the url, loads the QML component and instantiates it.
// The source could be a Qt resource path (qrc://icon) or a file path (file://path/to/icon).
// However, it must be a valid path.
func (view Viewer) SetSource(url string) {
if view.ptr == nil {
return
}
cURL := C.CString(url)
defer C.free(unsafe.Pointer(cURL))
C.Viewer_SetSource(view.ptr, cURL)
}
// SetResizeMode sets whether the view should resize the window contents.
// If this property is set to SizeViewToRootObject (the default), the view resizes
// to the size of the root item in the QML. If this property is set to
// SizeRootObjectToView, the view will automatically resize the root item to the
// size of the view.
func (view Viewer) SetResizeMode(resizeMode ResizeMode) {
if view.ptr == nil {
return
}
C.Viewer_SetResizeMode(view.ptr, C.int(resizeMode))
}
// SetFlags sets the flags of the window. The window flags control the window's appearance
// in the windowing system, whether it's a dialog, popup, or a regular window, and whether it
// should have a title bar, etc. The actual window flags might differ from the flags set with
// setFlags() if the requested flags could not be fulfilled.
func (view Viewer) SetFlags(flags WindowFlags) {
if view.ptr == nil {
return
}
C.Viewer_SetFlags(view.ptr, C.int(flags))
}
// SetHeight sets the height of the window.
func (view Viewer) SetHeight(height int) {
if view.ptr == nil {
return
}
C.Viewer_SetHeight(view.ptr, C.int(int32(height)))
}
// SetWidth sets the width of the window.
func (view Viewer) SetWidth(width int) {
if view.ptr == nil {
return
}
C.Viewer_SetWidth(view.ptr, C.int(int32(width)))
}
// SetMaximumHeight sets the maximum height of the window.
func (view Viewer) SetMaximumHeight(height int) {
if view.ptr == nil {
return
}
C.Viewer_SetMaximumHeight(view.ptr, C.int(int32(height)))
}
// SetMaximumWidth sets the maximum width of the window.
func (view Viewer) SetMaximumWidth(width int) {
if view.ptr == nil {
return
}
C.Viewer_SetMaximumWidth(view.ptr, C.int(int32(width)))
}
// SetMinimumHeight sets the minimum height of the window.
func (view Viewer) SetMinimumHeight(height int) {
if view.ptr == nil {
return
}
C.Viewer_SetMinimumHeight(view.ptr, C.int(int32(height)))
}
// SetMinimumWidth sets the minimum width of the window.
func (view Viewer) SetMinimumWidth(width int) {
if view.ptr == nil {
return
}
C.Viewer_SetMinimumWidth(view.ptr, C.int(int32(width)))
}
// SetOpacity sets the opacity of the window in the windowing system. If the windowing system supports
// window opacity, this can be used to fade the window in and out, or to make it semitransparent.
// A value of 1.0 or above is treated as fully opaque, whereas a value of 0.0 or below is treated as
// fully transparent. Values inbetween represent varying levels of translucency between the two extremes.
// The default value is 1.0.
func (view Viewer) SetOpacity(opacity float64) {
if view.ptr == nil {
return
}
C.Viewer_SetOpacity(view.ptr, C.double(opacity))
}
// SetTitle sets the window's title in the windowing system. The window title might appear in the title
// area of the window decorations, depending on the windowing system and the window flags. It might also
// be used by the windowing system to identify the window in other contexts, such as in the task switcher.
func (view Viewer) SetTitle(title string) {
if view.ptr == nil {
return
}
cTitle := C.CString(title)
defer C.free(unsafe.Pointer(cTitle))
C.Viewer_SetTitle(view.ptr, cTitle)
}
// SetVisible sets whether the window is visible or not. This property controls the visibility of the
// window in the windowing system. By default, the window is not visible, you must call setVisible(true),
// or show() or similar to make it visible.
func (view Viewer) SetVisible(visible bool) {
if view.ptr == nil {
return
}
C.Viewer_SetVisible(view.ptr, C.bool(visible))
}
// SetPosition sets the position of the window on the desktop to x, y.
func (view Viewer) SetPosition(x int, y int) {
if view.ptr == nil {
return
}
C.Viewer_SetPosition(view.ptr, C.int(int32(x)), C.int(int32(y)))
}
// SetIcon sets the window's icon in the windowing system. The window icon might be used by the windowing
// system for example to decorate the window, and/or in the task switcher.
// Note: On macOS, the window title bar icon is meant for windows representing documents, and will only
// show up if a file path is also set.
func (view Viewer) SetIcon(fileName string) {
if view.ptr == nil {
return
}
cFileName := C.CString(fileName)
defer C.free(unsafe.Pointer(cFileName))
C.Viewer_SetIcon(view.ptr, cFileName)
}
// Show shows the window. This is equivalent to calling showFullScreen(), showMaximized(), or
// showNormal(), depending on the platform's default behavior for the window type and flags.
func (view Viewer) Show() {
if view.ptr == nil {
return
}
C.Viewer_Show(view.ptr)
}
// ShowMaximized shows the window as maximized.
// Equivalent to calling setWindowStates(WindowMaximized) and then setVisible(true).
func (view Viewer) ShowMaximized() {
if view.ptr == nil {
return
}
C.Viewer_ShowMaximized(view.ptr)
}
// ShowMinimized shows the window as minimized.
// Equivalent to calling setWindowStates(WindowMinimized) and then setVisible(true).
func (view Viewer) ShowMinimized() {
if view.ptr == nil {
return
}
C.Viewer_ShowMinimized(view.ptr)
}
// ShowFullScreen shows the window as fullscreen.
// Equivalent to calling setWindowStates(WindowFullScreen) and then setVisible(true).
func (view Viewer) ShowFullScreen() {
if view.ptr == nil {
return
}
C.Viewer_ShowFullScreen(view.ptr)
}
// ShowNormal shows the window as normal, i.e. neither maximized, minimized, nor fullscreen.
// Equivalent to calling setWindowStates(WindowNoState) and then setVisible(true).
func (view Viewer) ShowNormal() {
if view.ptr == nil {
return
}
C.Viewer_ShowNormal(view.ptr)
}
// SetWindowStates sets the screen-occupation state of the window. The window state represents whether
// the window appears in the windowing system as maximized, minimized and/or fullscreen. The window can
// be in a combination of several states. For example, if the window is both minimized and maximized,
// the window will appear minimized, but clicking on the task bar entry will restore it to the
// maximized state.
func (view Viewer) SetWindowStates(state WindowStates) {
if view.ptr == nil {
return
}
C.Viewer_SetWindowStates(view.ptr, C.int(state))
}
// ClearComponentCache clears the engine's internal component cache. This function causes the property
// metadata of all components previously loaded by the engine to be destroyed. All previously loaded
// components and the property bindings for all extant objects created from those components will cease
// to function. This function returns the engine to a state where it does not contain any loaded
// component data. This may be useful in order to reload a smaller subset of the previous component set,
// or to load a new version of a previously loaded component. Once the component cache has been cleared,
// components must be loaded before any new objects can be created.
func (view Viewer) ClearComponentCache() {
if view.ptr == nil {
return
}
C.Viewer_ClearComponentCache(view.ptr)
}
// Reload reloads the active QML view.
func (view Viewer) Reload() {
if view.ptr == nil {
return
}
C.Viewer_Reload(view.ptr)
}
// WatchResourceDir watches for change inside the specified resource dir.
// When change happened, the view will be reloaded immediately.
// The directory path must be absolute.
// Only use this in development environment.
func (view Viewer) WatchResourceDir(dirPath string) {
// Make sure directory is exists
dirInfo, err := os.Stat(dirPath)
if os.IsNotExist(err) || !dirInfo.IsDir() {
logrus.Fatalf("directory %s does not exist\n", dirPath)
}
// Make sure directory path is absolute
if !fp.IsAbs(dirPath) {
logrus.Fatalln("path to directory must be absolute")
}
// Create watcher
watcher, err := fsnotify.NewWatcher()
if err != nil {
logrus.Fatalln("failed to create watcher:", err)
}
defer watcher.Close()
// Add all subdir inside resource dir to watcher
err = fp.Walk(dirPath, func(path string, info os.FileInfo, _ error) error {
if info.IsDir() {
return watcher.Add(path)
}
return nil
})
if err != nil {
logrus.Fatalln("failed to scan resource dir:", err)
}
// Watch for files change
logrus.Infoln("File watcher enabled for", dirPath)
logrus.Infoln("Only use it in safe environment")
lastEvent := struct {
Name string
Time time.Time
}{}
for {
select {
case event := <-watcher.Events:
// Make sure the file is not qmlc or jsc
fileName := event.Name
if fp.Ext(fileName) == ".qmlc" || strings.Contains(fileName, ".qmlc.") ||
fp.Ext(fileName) == ".jsc" || strings.Contains(fileName, ".jsc.") {
continue
}
// In some OS, the write events fired twice.
// To fix this, check if current event is happened less than one sec before.
// If yes, skip this event.
now := time.Now()
eventName := fmt.Sprintf("%s: %s", event.Op.String(), fileName)
if lastEvent.Name == eventName && now.Sub(lastEvent.Time).Seconds() <= 1.0 {
continue
}
// Also make sure that file is not empty
if info, err := os.Stat(fileName); err != nil || info.Size() == 0 {
continue
}
// Else, save this event and reload view.
lastEvent = struct {
Name string
Time time.Time
}{Name: eventName, Time: now}
logrus.Println(eventName)
view.Reload()
case err := <-watcher.Errors:
if err != nil {
logrus.Errorln("Watcher error:", err)
}
}
}
}