qt.go 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155
  1. package qt
  2. /*
  3. #include <stdlib.h>
  4. #include "qtbinding/qtbinding.h"
  5. */
  6. // #cgo LDFLAGS: -L./build -lqtbinding -Wl,-rpath=\$ORIGIN/
  7. import "C"
  8. import (
  9. _ "embed"
  10. "fmt"
  11. "errors"
  12. "unsafe"
  13. "reflect"
  14. "unicode"
  15. "strings"
  16. "kumachan/standalone/qt/cgohelper"
  17. )
  18. // QObject
  19. type Object struct {
  20. ptr unsafe.Pointer
  21. }
  22. func (obj Object) findChild(name string) (Object, bool) {
  23. var new_str, del_all_str = str_alloc()
  24. defer del_all_str()
  25. var ptr = C.QtObjectFindChild(obj.ptr, new_str(name))
  26. if ptr != nil {
  27. return Object{ptr}, true
  28. } else {
  29. return Object{}, false
  30. }
  31. }
  32. func (obj Object) FindChild(name string, v reflect.Value) error {
  33. var child, ok = obj.findChild(name)
  34. if !(ok) {
  35. return errors.New(fmt.Sprintf("child Object %s not found", name))
  36. }
  37. var err = child.ExactlyAssignTo(v)
  38. if err != nil {
  39. return fmt.Errorf("child %s: %w", name, err)
  40. }
  41. return nil
  42. }
  43. func (obj Object) ExactlyAssignTo(v reflect.Value) error {
  44. if v.Kind() != reflect.Ptr { panic("invalid argument") }
  45. var class = obj.GetClassName()
  46. var required = (func() string {
  47. var type_name = v.Elem().Type().Name()
  48. if strings.HasPrefix(type_name, "Custom") {
  49. return strings.TrimPrefix(type_name, "Custom")
  50. } else {
  51. return ("Q" + type_name)
  52. }
  53. })()
  54. if class == required {
  55. var target = v.Elem().Field(0)
  56. for !(reflect.TypeOf(obj).AssignableTo(target.Type())) {
  57. target = target.Field(0)
  58. }
  59. target.Set(reflect.ValueOf((interface{})(obj)))
  60. return nil
  61. } else {
  62. return errors.New(fmt.Sprintf(
  63. "class not matching: %s (%s)", class, required))
  64. }
  65. }
  66. func (obj Object) ExactlyAssignChildrenTo(v reflect.Value) error {
  67. if v.Kind() != reflect.Ptr { panic("invalid argument") }
  68. if v.Elem().Kind() != reflect.Struct { panic("invalid argument") }
  69. var struct_v = v.Elem()
  70. var struct_t = struct_v.Type()
  71. for i := 0; i < struct_t.NumField(); i += 1 {
  72. var field_name = struct_t.Field(i).Name
  73. var field_type = struct_t.Field(i).Type
  74. var t = ([] rune)(field_type.Name())
  75. t[0] = unicode.ToLower(t[0])
  76. var field_type_name = string(t)
  77. var child_name = (field_type_name + field_name)
  78. var err = obj.FindChild(child_name, struct_v.Field(i).Addr())
  79. if err != nil { return err }
  80. }
  81. return nil
  82. }
  83. func (obj Object) BlockSignals() (error, func()) {
  84. C.QtBlockSignals(obj.ptr, 1)
  85. return nil, func() {
  86. // NOTE: previous blocking state NOT recovered (always unblock)
  87. C.QtBlockSignals(obj.ptr, 0)
  88. }
  89. }
  90. func (obj Object) BlockCallbacks() (error, func()) {
  91. C.QtBlockCallbacks(obj.ptr, 1)
  92. return nil, func() {
  93. // NOTE: previous blocking state NOT recovered (always unblock)
  94. C.QtBlockCallbacks(obj.ptr, 0)
  95. }
  96. }
  97. func (obj Object) GetClassName() string {
  98. return ConsumeString(String(C.QtObjectGetClassName(obj.ptr)))
  99. }
  100. func (obj Object) getPropQtString(prop string) String {
  101. var new_str, del_all_str = str_alloc()
  102. defer del_all_str()
  103. var value = C.QtObjectGetPropString(obj.ptr, new_str(prop))
  104. return String(value)
  105. }
  106. func (obj Object) setPropQtString(prop string, val String) {
  107. var new_str, del_all_str = str_alloc()
  108. defer del_all_str()
  109. C.QtObjectSetPropString(obj.ptr, new_str(prop), C.QtString(val))
  110. }
  111. func (obj Object) GetPropString(prop string) string {
  112. var value = obj.getPropQtString(prop)
  113. var value_runes = ConsumeString(value)
  114. return value_runes
  115. }
  116. func (obj Object) SetPropString(prop string, val string) {
  117. var q_val, del_str = NewString(val)
  118. defer del_str()
  119. obj.setPropQtString(prop, q_val)
  120. }
  121. func (obj Object) GetPropBool(prop string) bool {
  122. var new_str, del_all_str = str_alloc()
  123. defer del_all_str()
  124. var val = C.QtObjectGetPropBool(obj.ptr, new_str(prop))
  125. return getBool(val)
  126. }
  127. func (obj Object) SetPropBool(prop string, val bool) {
  128. var new_str, del_all_str = str_alloc()
  129. defer del_all_str()
  130. var int_val int
  131. if val {
  132. int_val = 1
  133. } else {
  134. int_val = 0
  135. }
  136. C.QtObjectSetPropBool(obj.ptr, new_str(prop), C.int(int_val))
  137. }
  138. func (obj Object) GetPropInt(prop string) int {
  139. var new_str, del_all_str = str_alloc()
  140. defer del_all_str()
  141. return int(C.QtObjectGetPropInt(obj.ptr, new_str(prop)))
  142. }
  143. func (obj Object) SetPropInt(prop string, val int) {
  144. var new_str, del_all_str = str_alloc()
  145. defer del_all_str()
  146. C.QtObjectSetPropInt(obj.ptr, new_str(prop), C.int(val))
  147. }
  148. // QWidget
  149. type Widget struct { Object }
  150. func (w Widget) Show() {
  151. C.QtWidgetShow(w.ptr)
  152. }
  153. func (w Widget) MoveToScreenCenter() {
  154. C.QtWidgetMoveToScreenCenter(w.ptr)
  155. }
  156. func (w Widget) FindChildAssumeWidget(name string) (Widget, bool) {
  157. var child, ok = w.findChild(name)
  158. if ok {
  159. return Widget{child}, true
  160. } else {
  161. return Widget{}, false
  162. }
  163. }
  164. func (w Widget) FindChildAssumeAction(name string) (Action, bool) {
  165. var child, ok = w.findChild(name)
  166. if ok {
  167. return Action{child}, true
  168. } else {
  169. return Action{}, false
  170. }
  171. }
  172. func (w Widget) SetEnabled(value bool) {
  173. w.SetPropBool("enabled", value)
  174. }
  175. func (w Widget) OnFocusOut(cb func()) func() {
  176. return Listen(w.Object, EventFocusOut(), false, func(_ Event) {
  177. cb()
  178. })
  179. }
  180. // Subclasses
  181. type _ struct {}
  182. type Action struct { Object }
  183. type MainWindow struct { Widget }
  184. type Dialog struct { Widget }
  185. type WebEngineView struct { Widget }
  186. type Label struct { Widget }
  187. type PushButton struct { Widget }
  188. type LineEdit struct { Widget }
  189. type PlainTextEdit struct { Widget }
  190. type ListWidget struct { Widget }
  191. type TreeWidget struct { Widget }
  192. type TextBrowser struct { Widget }
  193. type GroupBox struct { Widget }
  194. type WebWindow struct { MainWindow }
  195. type CustomWebView struct { WebEngineView }
  196. // Events
  197. type _ struct {}
  198. type Event C.QtEvent
  199. type EventKind uint
  200. func EventMove() EventKind { return EventKind(uint(C.QtEventMove)) }
  201. func EventResize() EventKind { return EventKind(uint(C.QtEventResize)) }
  202. func EventClose() EventKind { return EventKind(uint(C.QtEventClose)) }
  203. func EventFocusIn() EventKind { return EventKind(uint(C.QtEventFocusIn)) }
  204. func EventFocusOut() EventKind { return EventKind(uint(C.QtEventFocusOut)) }
  205. func EventDynamicPropertyChange() EventKind { return EventKind(uint(C.QtEventDynamicPropertyChange)) }
  206. func (ev Event) ResizeEventGetWidth() uint {
  207. return uint(C.QtResizeEventGetWidth(C.QtEvent(ev)))
  208. }
  209. func (ev Event) ResizeEventGetHeight() uint {
  210. return uint(C.QtResizeEventGetHeight(C.QtEvent(ev)))
  211. }
  212. func (ev Event) DynamicPropertyChangeEventGetPropertyName() string {
  213. return ConsumeString(String(C.QtDynamicPropertyChangeEventGetPropertyName(C.QtEvent(ev))))
  214. }
  215. // Utility Types
  216. type _ struct {}
  217. type String C.QtString
  218. type ByteArray C.QtByteArray
  219. type VariantMap C.QtVariantMap
  220. type Icon C.QtIcon
  221. type Pixmap C.QtPixmap
  222. type Point struct {
  223. X int
  224. Y int
  225. }
  226. type Color struct {
  227. H uint
  228. S uint
  229. L uint
  230. A uint
  231. }
  232. func getBool(number C.QtBool) bool {
  233. return (number != 0)
  234. }
  235. func makeQtBool(p bool) C.QtBool {
  236. if p {
  237. return C.int(int(1))
  238. } else {
  239. return C.int(int(0))
  240. }
  241. }
  242. func getPoint(p C.QtPoint) Point {
  243. return Point { X: int(C.QtPointGetX(p)), Y: int(C.QtPointGetY(p)) }
  244. }
  245. func makeQtPoint(p Point) C.QtPoint {
  246. return C.QtMakePoint(C.int(p.X), C.int(p.Y))
  247. }
  248. func NewString(data string) (String, func()) {
  249. var str C.QtString
  250. if len(data) > 0 {
  251. var hdr = (*reflect.StringHeader)(unsafe.Pointer(&data))
  252. var ptr = (*C.uint8_t)(unsafe.Pointer(hdr.Data))
  253. var size = (C.size_t)(len(data))
  254. str = C.QtNewStringUTF8(ptr, size)
  255. } else {
  256. str = C.QtNewStringUTF8(nil, 0)
  257. }
  258. return String(str), func() {
  259. C.QtDeleteString(str)
  260. }
  261. }
  262. func NewStringFromUtf8Binary(data ([] byte)) (String, func()) {
  263. var str C.QtString
  264. if len(data) > 0 {
  265. var ptr, size = addrlen(data)
  266. str = C.QtNewStringUTF8(ptr, size)
  267. } else {
  268. str = C.QtNewStringUTF8(nil, 0)
  269. }
  270. return String(str), func() {
  271. C.QtDeleteString(str)
  272. }
  273. }
  274. func copyString(str String) string {
  275. var q_str = (C.QtString)(str)
  276. var size16 = uint(C.QtStringUTF16Length(q_str))
  277. if size16 == 0 {
  278. return ""
  279. }
  280. var buf = make([] rune, size16)
  281. if size16 > 0 {
  282. var size32 = uint(C.QtStringWriteToUTF32Buffer(q_str,
  283. (*C.uint32_t)(unsafe.Pointer(&buf[0]))))
  284. buf = buf[:size32]
  285. }
  286. return string(buf)
  287. }
  288. func ConsumeString(str String) string {
  289. var go_str = copyString(str)
  290. C.QtDeleteString((C.QtString)(str))
  291. return go_str
  292. }
  293. func ConsumeByteArray(b ByteArray) ([] byte) {
  294. var buf = unsafe.Pointer(C.QtByteArrayGetBuffer(C.QtByteArray(b)))
  295. var size = int(C.QtByteArrayGetSize(C.QtByteArray(b)))
  296. var data = make([] byte, size, size)
  297. for i := 0; i < size; i += 1 {
  298. data[i] = *(*byte)(unsafe.Pointer(uintptr(buf) + uintptr(i)))
  299. }
  300. C.QtDeleteByteArray(C.QtByteArray(b))
  301. return data
  302. }
  303. func VariantMapGetString(m VariantMap, key String) string {
  304. var val = C.QtVariantMapGetString(C.QtVariantMap(m), C.QtString(key))
  305. var val_runes = ConsumeString(String(val))
  306. return val_runes
  307. }
  308. func VariantMapGetFloat(m VariantMap, key String) float64 {
  309. var val = C.QtVariantMapGetFloat(C.QtVariantMap(m), C.QtString(key))
  310. return float64(val)
  311. }
  312. func VariantMapGetBool(m VariantMap, key String) bool {
  313. var val = C.QtVariantMapGetBool(C.QtVariantMap(m), C.QtString(key))
  314. return getBool(val)
  315. }
  316. type ImageData struct {
  317. Bytes [] byte
  318. Format ImageDataFormat
  319. }
  320. type ImageDataFormat int
  321. const (
  322. PNG ImageDataFormat = iota
  323. JPEG
  324. )
  325. type iconPool (map[*ImageData] iconPoolItem)
  326. type iconPoolItem struct {
  327. icon Icon
  328. del func()
  329. }
  330. func makeIconPool() (iconPool, func()) {
  331. var p = make(iconPool)
  332. return p, func() {
  333. for _, item := range p {
  334. item.del()
  335. }
  336. }
  337. }
  338. func (p iconPool) GetIcon(img *ImageData) Icon {
  339. var cached, is_cached = p[img]
  340. if is_cached {
  341. return cached.icon
  342. } else {
  343. var new_icon, del_icon = (func() (Icon, func()) {
  344. if img != nil {
  345. var pm, del_pm = NewPixmap(img.Bytes, img.Format)
  346. defer del_pm()
  347. return NewIcon(pm)
  348. } else {
  349. return NewIconEmpty()
  350. }
  351. })()
  352. p[img] = iconPoolItem {
  353. icon: new_icon,
  354. del: del_icon,
  355. }
  356. return new_icon
  357. }
  358. }
  359. func NewPixmap(data ([] byte), format ImageDataFormat) (Pixmap, func()) {
  360. var buf, length = addrlen(data)
  361. if format == PNG {
  362. var pm = C.QtNewPixmapPNG(buf, length)
  363. return Pixmap(pm), func() { C.QtDeletePixmap(pm) }
  364. } else if format == JPEG {
  365. var pm = C.QtNewPixmapJPEG(buf, length)
  366. return Pixmap(pm), func() { C.QtDeletePixmap(pm) }
  367. } else {
  368. panic("qt pixmap: unsupported image format")
  369. }
  370. }
  371. func NewIcon(pm Pixmap) (Icon, func()) {
  372. var icon = C.QtNewIcon(C.QtPixmap(pm))
  373. return Icon(icon), func() {
  374. C.QtDeleteIcon(icon)
  375. }
  376. }
  377. func NewIconEmpty() (Icon, func()) {
  378. var icon = C.QtNewIconEmpty()
  379. return Icon(icon), func() {
  380. C.QtDeleteIcon(icon)
  381. }
  382. }
  383. // NOTE: should be called on main thread
  384. func Init() {
  385. C.QtInit()
  386. }
  387. // NOTE: should be called on main thread
  388. func Main() {
  389. C.QtMain()
  390. }
  391. // NOTE: should be called on main thread
  392. func Exit(code int32) {
  393. C.QtExit(C.int(code))
  394. }
  395. // Generate a UUID
  396. func UUID() string {
  397. var s = ConsumeString(String(C.QtNewUUID()))
  398. s = strings.TrimPrefix(s, "{")
  399. s = strings.TrimSuffix(s, "}")
  400. return s
  401. }
  402. // Invokes the `operation` callback in the Qt main thread.
  403. func CommitTask(operation func()) {
  404. var delete_callback (func() bool)
  405. var f = func() {
  406. operation()
  407. delete_callback()
  408. }
  409. callback, delete_callback := cgohelper.NewCallback(f)
  410. C.QtCommitTask(cgo_callback, C.size_t(callback))
  411. }
  412. // Loads `ui_xml` with `base_dir` and execute `k` in the Qt main thread.
  413. func LoadWidget(ui_xml string, base_dir string, k func(Widget)(error)) error {
  414. var new_str, del_all_str = str_alloc()
  415. defer del_all_str()
  416. var channel = make(chan error)
  417. CommitTask(func() {
  418. var ptr = C.QtLoadWidget(new_str(ui_xml), new_str(base_dir))
  419. if ptr != nil {
  420. channel <- k(Widget{Object{ptr}})
  421. } else {
  422. channel <- errors.New("failed to load widget from ui xml")
  423. }
  424. })
  425. return <- channel
  426. }
  427. // Makes a signal connection. `callback` will be called in the Qt main thread.
  428. func Connect(obj Object, signal string, callback func()) func() {
  429. var new_str, del_all_str = str_alloc()
  430. defer del_all_str()
  431. var cb, del_cb = cgohelper.NewCallback(callback)
  432. var channel = make(chan C.QtConnHandle)
  433. // Note: Although connect() is documented as "thread-safe",
  434. // it is not clear what will happen if the goroutine
  435. // is preempted and moved to another thread while
  436. // calling connect(), thus CommitTask() is used here.
  437. CommitTask(func() {
  438. var conn = C.QtConnect(obj.ptr, new_str(signal), cgo_callback, C.size_t(cb))
  439. channel <- conn
  440. })
  441. var conn = <- channel
  442. if getBool(C.QtIsConnectionValid(conn)) {
  443. return func() {
  444. var wait = make(chan struct{})
  445. CommitTask(func() {
  446. C.QtDisconnect(conn)
  447. // Note: Use CommitTask() to prevent pending callbacks
  448. // from being removed.
  449. CommitTask(func() {
  450. del_cb()
  451. })
  452. wait <- struct{}{}
  453. })
  454. <- wait
  455. }
  456. } else {
  457. panic("invalid connection")
  458. }
  459. }
  460. // Installs an event filter. `callback` will be called in the Qt main thread.
  461. func Listen(obj Object, kind EventKind, prevent bool, callback func(Event)) func() {
  462. var l C.QtEventListener
  463. var cb, del_cb = cgohelper.NewCallback(func() {
  464. var ev = C.QtGetCurrentEvent(l)
  465. callback(Event(ev))
  466. })
  467. var ok = make(chan struct{})
  468. CommitTask(func() {
  469. var prevent_flag = makeQtBool(prevent)
  470. l = C.QtAddEventListener(obj.ptr, C.size_t(kind), prevent_flag, cgo_callback, C.size_t(cb))
  471. ok <- struct{} {}
  472. })
  473. <- ok
  474. return func() {
  475. var wait = make(chan struct{})
  476. CommitTask(func() {
  477. C.QtRemoveEventListener(obj.ptr, l)
  478. // Note: Use CommitTask() to prevent pending callbacks
  479. // from being removed.
  480. CommitTask(func() {
  481. del_cb()
  482. })
  483. wait <- struct{}{}
  484. })
  485. <- wait
  486. }
  487. }
  488. func (a Action) OnTrigger(cb func()) func() {
  489. return Connect(a.Object, "triggered()", cb)
  490. }
  491. func (w MainWindow) OnClose(cb func()) func() {
  492. return Listen(w.Object, EventClose(), true, func(_ Event) {
  493. cb()
  494. })
  495. }
  496. func (w Dialog) OnClose(cb func()) func() {
  497. return Listen(w.Object, EventClose(), true, func(_ Event) {
  498. cb()
  499. })
  500. }
  501. func (w Dialog) OnRejected(cb func()) func() {
  502. return Connect(w.Object, "rejected()", cb)
  503. }
  504. func (w Dialog) Exec() {
  505. C.QtDialogExec(w.ptr)
  506. }
  507. func (w Dialog) Accept() {
  508. C.QtDialogAccept(w.ptr)
  509. }
  510. func (w Dialog) Reject() {
  511. C.QtDialogReject(w.ptr)
  512. }
  513. func (w Dialog) ShowModal() {
  514. C.QtDialogShowModal(w.ptr)
  515. }
  516. func (w Label) SetText(content string) {
  517. w.SetPropString("text", content)
  518. }
  519. func (w PushButton) OnClick(cb func()) func() {
  520. return Connect(w.Object, "clicked()", cb)
  521. }
  522. func (w LineEdit) SetText(content string) {
  523. _, unblock := w.BlockSignals()
  524. defer unblock()
  525. w.SetPropString("text", content)
  526. }
  527. func (w LineEdit) SetTextWithSignal(content string) {
  528. w.SetPropString("text", content)
  529. }
  530. func (w LineEdit) Text() string {
  531. return w.GetPropString("text")
  532. }
  533. func (w LineEdit) WatchText(cb func(string)) func() {
  534. return Connect(w.Object, "textChanged(const QString&)", func() {
  535. cb(w.Text())
  536. })
  537. }
  538. func (w PlainTextEdit) SetText(content string) {
  539. _, unblock := w.BlockSignals()
  540. defer unblock()
  541. w.SetPropString("plainText", content)
  542. }
  543. func (w PlainTextEdit) Text() string {
  544. return w.GetPropString("plainText")
  545. }
  546. func (w PlainTextEdit) WatchText(cb func(string)) func() {
  547. return Connect(w.Object, "textChanged()", func() {
  548. cb(w.Text())
  549. })
  550. }
  551. type ListWidgetItem struct {
  552. Key string
  553. Icon *ImageData
  554. Label string
  555. }
  556. func (w ListWidget) Clear() {
  557. C.QtListWidgetClear(w.ptr)
  558. }
  559. func (w ListWidget) CurrentItemKey() (string, bool) {
  560. if getBool(C.QtListWidgetHasCurrentItem(w.ptr)) {
  561. var raw_key = C.QtListWidgetGetCurrentItemKey(w.ptr)
  562. var key = ConsumeString(String(raw_key))
  563. return key, true
  564. } else {
  565. return "", false
  566. }
  567. }
  568. func (w ListWidget) WatchCurrentItemKey(cb func(string,bool)) func() {
  569. return Connect(w.Object, "currentItemChanged(QListWidgetItem*,QListWidgetItem*)", func() {
  570. cb(w.CurrentItemKey())
  571. })
  572. }
  573. func (w ListWidget) SetItems(items ([] ListWidgetItem), current *string) {
  574. var occurred_keys = make(map[string] bool)
  575. var current_key string
  576. var has_current = false
  577. for _, item := range items {
  578. if occurred_keys[item.Key] {
  579. panic(fmt.Sprintf("qt.ListWidget: duplicate item key %s", item.Key))
  580. }
  581. occurred_keys[item.Key] = true
  582. var is_current = (current != nil && item.Key == *current)
  583. if is_current {
  584. has_current = true
  585. current_key = item.Key
  586. }
  587. }
  588. var prev_current_key, prev_has_current = w.CurrentItemKey()
  589. (func() {
  590. if has_current {
  591. _, unblock := w.BlockSignals()
  592. defer unblock()
  593. }
  594. w.Clear()
  595. })()
  596. if has_current && prev_has_current {
  597. if current_key == prev_current_key {
  598. _, unblock := w.BlockSignals()
  599. defer unblock()
  600. }
  601. }
  602. var icon_pool, dispose = makeIconPool()
  603. defer dispose()
  604. for _, item := range items {
  605. var key, del_key = NewString(item.Key)
  606. var icon = icon_pool.GetIcon(item.Icon)
  607. var label, del_label = NewString(item.Label)
  608. var is_current = (has_current && (item.Key == current_key))
  609. C.QtListWidgetAddItem(
  610. w.ptr, C.QtString(key),
  611. C.QtIcon(icon), C.QtString(label),
  612. makeQtBool(is_current),
  613. )
  614. del_label()
  615. del_key()
  616. }
  617. }
  618. type TreeWidgetItem struct {
  619. Key string
  620. Icon *ImageData
  621. Labels [] string
  622. Children [] TreeWidgetItem
  623. }
  624. func (w TreeWidget) Clear() {
  625. C.QtTreeWidgetClear(w.ptr)
  626. }
  627. func (w TreeWidget) CurrentItemKey() (string, bool) {
  628. if getBool(C.QtTreeWidgetHasCurrentItem(w.ptr)) {
  629. var raw_key = C.QtTreeWidgetGetCurrentItemKey(w.ptr)
  630. var key = ConsumeString(String(raw_key))
  631. return key, true
  632. } else {
  633. return "", false
  634. }
  635. }
  636. func (w TreeWidget) WatchCurrentItemKey(cb func(string,bool)) func() {
  637. return Connect(w.Object, "currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)", func() {
  638. cb(w.CurrentItemKey())
  639. })
  640. }
  641. func (w TreeWidget) SetItems(items ([] TreeWidgetItem), current *string) {
  642. var occurred_keys = make(map[string] bool)
  643. var current_key string
  644. var has_current = false
  645. var check_keys func(TreeWidgetItem)
  646. check_keys = func(item TreeWidgetItem) {
  647. if occurred_keys[item.Key] {
  648. panic(fmt.Sprintf("qt.TreeWidget: duplicate item key %s", item.Key))
  649. }
  650. occurred_keys[item.Key] = true
  651. var is_current = (current != nil && item.Key == *current)
  652. if is_current {
  653. has_current = true
  654. current_key = item.Key
  655. }
  656. for _, child := range item.Children {
  657. check_keys(child)
  658. }
  659. }
  660. for _, item := range items {
  661. check_keys(item)
  662. }
  663. var prev_current_key, prev_has_current = w.CurrentItemKey()
  664. (func() {
  665. if has_current {
  666. _, unblock := w.BlockSignals()
  667. defer unblock()
  668. }
  669. w.Clear()
  670. })()
  671. if has_current && prev_has_current {
  672. if current_key == prev_current_key {
  673. _, unblock := w.BlockSignals()
  674. defer unblock()
  675. }
  676. }
  677. var column_count = int(C.QtTreeWidgetGetColumnCount(w.ptr))
  678. var icon_pool, dispose = makeIconPool()
  679. defer dispose()
  680. var add_item func(TreeWidgetItem, unsafe.Pointer)
  681. add_item = func(item TreeWidgetItem, parent unsafe.Pointer) {
  682. var key, del_key = NewString(item.Key)
  683. defer del_key()
  684. var icon = icon_pool.GetIcon(item.Icon)
  685. var labels = make([] String, column_count)
  686. var labels_del = make([] func(), column_count)
  687. defer (func() { for _, del := range labels_del { del() } })()
  688. if len(item.Labels) > column_count {
  689. panic("qt.TreeWidget: too many labels")
  690. }
  691. for i, label := range item.Labels {
  692. var label_q, label_del = NewString(label)
  693. labels[i] = label_q
  694. labels_del[i] = label_del
  695. }
  696. var is_current = (has_current && (item.Key == current_key))
  697. var labels_ptr unsafe.Pointer
  698. if len(labels) > 0 {
  699. labels_ptr = unsafe.Pointer(&labels[0])
  700. }
  701. var item_ptr = C.QtTreeWidgetAddItem(
  702. w.ptr, parent, C.QtString(key),
  703. C.QtIcon(icon), (*C.QtString)(labels_ptr),
  704. makeQtBool(is_current),
  705. )
  706. for _, child := range item.Children {
  707. add_item(child, item_ptr)
  708. }
  709. }
  710. for _, item := range items {
  711. add_item(item, nil)
  712. }
  713. }
  714. func (w TextBrowser) SetHtml(content string) {
  715. w.Object.SetPropString("html", content)
  716. }
  717. func (w WebEngineView) DisableContextMenu() {
  718. C.QtWebViewDisableContextMenu(w.ptr)
  719. }
  720. func (w WebEngineView) EnableLinkDelegation() {
  721. C.QtWebViewEnableLinkDelegation(w.ptr)
  722. }
  723. func (w WebEngineView) SetHTML(html String, base_url String) {
  724. C.QtWebViewSetHTML(w.ptr, C.QtString(html), C.QtString(base_url))
  725. }
  726. func (w WebEngineView) ScrollToAnchor(anchor String) {
  727. C.QtWebViewScrollToAnchor(w.ptr, C.QtString(anchor))
  728. }
  729. func (w WebEngineView) GetScroll() Point {
  730. return getPoint(C.QtWebViewGetScroll(w.ptr))
  731. }
  732. func (w WebEngineView) SetScroll(pos Point) {
  733. C.QtWebViewSetScroll(w.ptr, makeQtPoint(pos))
  734. }
  735. func WebGetAssetURL(key String) String {
  736. return String(C.WebGetAssetURL(C.QtString(key)))
  737. }
  738. func (view CustomWebView) LoadContent() {
  739. C.WebViewLoadContent(view.ptr)
  740. }
  741. func (view CustomWebView) RegisterAsset(key String, mime String, data ([] byte)) {
  742. var buf, length = addrlen(data)
  743. C.WebViewRegisterAsset(view.ptr, C.QtString(key), C.QtString(mime), buf, length)
  744. }
  745. func (view CustomWebView) InjectCSS(key String) {
  746. C.WebViewInjectCSS(view.ptr, C.QtString(key))
  747. }
  748. func (view CustomWebView) InjectJS(key String) {
  749. C.WebViewInjectJS(view.ptr, C.QtString(key))
  750. }
  751. func (view CustomWebView) InjectTTF(key String, family String, weight String, style String) {
  752. C.WebViewInjectTTF(view.ptr, C.QtString(key), C.QtString(family), C.QtString(weight), C.QtString(style))
  753. }
  754. func (view CustomWebView) PatchActualDOM(patch_data ([] byte)) {
  755. var str, del = NewStringFromUtf8Binary(patch_data)
  756. defer del()
  757. C.WebViewPatchActualDOM(view.ptr, C.QtString(str))
  758. }
  759. func (view CustomWebView) GetCurrentEventHandler() string {
  760. var raw_id = C.WebViewGetCurrentEventHandler(view.ptr)
  761. var id_str = ConsumeString(String(raw_id))
  762. return id_str
  763. }
  764. func (view CustomWebView) GetCurrentEventPayload() *WebEventPayload {
  765. return &WebEventPayload {
  766. VariantMap(C.WebViewGetCurrentEventPayload(view.ptr)),
  767. }
  768. }
  769. type WebEventPayload struct {
  770. Data VariantMap
  771. }
  772. func (ev *WebEventPayload) Consume(f func(*WebEventPayload) interface{}) interface{} {
  773. defer func() {
  774. C.QtDeleteVariantMap(C.QtVariantMap(ev.Data))
  775. } ()
  776. return f(ev)
  777. }
  778. func (ev *WebEventPayload) GetString(key string) string {
  779. var key_str, del = NewString(key)
  780. defer del()
  781. return VariantMapGetString(ev.Data, key_str)
  782. }
  783. func (ev *WebEventPayload) GetFloat(key string) float64 {
  784. var key_str, del = NewString(key)
  785. defer del()
  786. return VariantMapGetFloat(ev.Data, key_str)
  787. }
  788. func (ev *WebEventPayload) GetBool(key string) bool {
  789. var key_str, del = NewString(key)
  790. defer del()
  791. return VariantMapGetBool(ev.Data, key_str)
  792. }
  793. type WebWindowOptions struct {
  794. Title string
  795. Kind WindowKind
  796. Icon *ImageData
  797. Width uint
  798. Height uint
  799. CloseBtn bool
  800. Modal bool
  801. Debug bool
  802. }
  803. type WindowKind C.size_t
  804. func WindowKindOrdinary() WindowKind { return WindowKind(C.QtWindowKindOrdinary) }
  805. func WindowKindDialog() WindowKind { return WindowKind(C.QtWindowKindDialog) }
  806. func WindowKindPopup() WindowKind { return WindowKind(C.QtWindowKindPopup) }
  807. //go:embed default-window-icon.png
  808. var defaultWindowIconData ([] byte)
  809. func WebWindowCreate(parent Widget, opts WebWindowOptions) (WebWindow, CustomWebView, func()) {
  810. var title_str, del_title = NewString(opts.Title)
  811. defer del_title()
  812. var icon_data *ImageData
  813. if opts.Icon != nil {
  814. icon_data = opts.Icon
  815. } else {
  816. icon_data = &ImageData {
  817. Bytes: defaultWindowIconData,
  818. Format: PNG,
  819. }
  820. }
  821. var icon_pm, del_icon_pm = NewPixmap(icon_data.Bytes, icon_data.Format)
  822. defer del_icon_pm()
  823. var icon_obj, del_icon_obj = NewIcon(icon_pm)
  824. defer del_icon_obj()
  825. var w_ptr = C.WebWindowCreate (
  826. parent.ptr,
  827. C.size_t(opts.Kind),
  828. C.QtIcon(icon_obj),
  829. C.QtString(title_str),
  830. C.int(opts.Width),
  831. C.int(opts.Height),
  832. makeQtBool(opts.CloseBtn),
  833. makeQtBool(opts.Modal),
  834. makeQtBool(opts.Debug),
  835. )
  836. var view_ptr = C.WebWindowGetWebView(w_ptr)
  837. { var dialog = WebWindow{MainWindow{Widget{Object{w_ptr}}}}
  838. var view = CustomWebView{WebEngineView{Widget{Object{view_ptr}}}}
  839. var dispose = func() {
  840. var wait = make(chan struct{})
  841. CommitTask(func() {
  842. // behavior: hide and deleteLater
  843. C.WebWindowDispose(w_ptr)
  844. wait <- struct{}{}
  845. })
  846. <- wait
  847. }
  848. return dialog, view, dispose }
  849. }
  850. func (w WebWindow) Show(set_pos bool, x int32, y int32) {
  851. C.WebWindowShow(w.ptr, makeQtBool(set_pos), C.int(x), C.int(y))
  852. }
  853. func ColorDialog(parent Widget, title_ string, value Color) (Color, bool) {
  854. var title, del = NewString(title_)
  855. defer del()
  856. var ok = getBool(C.QtColorDialog(
  857. parent.ptr,
  858. C.QtString(title),
  859. (*C.size_t)(unsafe.Pointer(&(value.H))),
  860. (*C.size_t)(unsafe.Pointer(&(value.S))),
  861. (*C.size_t)(unsafe.Pointer(&(value.L))),
  862. (*C.size_t)(unsafe.Pointer(&(value.A))),
  863. ))
  864. if ok {
  865. return value, true
  866. } else {
  867. return Color {}, false
  868. }
  869. }
  870. type FileDialogOptions struct {
  871. Title string
  872. Cwd string
  873. Filter string
  874. }
  875. func fileDialogAdaptOptions(opts FileDialogOptions) (String, String, String, func()) {
  876. var title, del_title = NewString(opts.Title)
  877. var cwd, del_cwd = NewString(opts.Cwd)
  878. var filter, del_filter = NewString(opts.Filter)
  879. return title, cwd, filter, func() {
  880. del_title()
  881. del_cwd()
  882. del_filter()
  883. }
  884. }
  885. func FileDialogOpen(parent Widget, opts FileDialogOptions) string {
  886. var title, cwd, filter, del = fileDialogAdaptOptions(opts)
  887. defer del()
  888. var raw_path = C.QtFileDialogOpen(
  889. parent.ptr,
  890. C.QtString(title), C.QtString(cwd), C.QtString(filter),
  891. )
  892. return ConsumeString(String(raw_path))
  893. }
  894. func FileDialogOpenMultiple(parent Widget, opts FileDialogOptions) ([] string) {
  895. var title, cwd, filter, del = fileDialogAdaptOptions(opts)
  896. defer del()
  897. var raw_path_list = C.QtFileDialogOpenMultiple(
  898. parent.ptr,
  899. C.QtString(title), C.QtString(cwd), C.QtString(filter),
  900. )
  901. var path_list = make([] string, 0)
  902. var L = uint(C.QtStringListGetSize(raw_path_list))
  903. for i := uint(0); i < L; i += 1 {
  904. var raw_item = C.QtStringListGetItem(raw_path_list, C.size_t(i))
  905. var item = ConsumeString(String(raw_item))
  906. path_list = append(path_list, item)
  907. }
  908. C.QtDeleteStringList(raw_path_list)
  909. return path_list
  910. }
  911. func FileDialogSelectDirectory(parent Widget, opts FileDialogOptions) string {
  912. var title, cwd, _, del = fileDialogAdaptOptions(opts)
  913. defer del()
  914. var raw_path = C.QtFileDialogSelectDirectory(
  915. parent.ptr,
  916. C.QtString(title), C.QtString(cwd),
  917. )
  918. return ConsumeString(String(raw_path))
  919. }
  920. func FileDialogSave(parent Widget, opts FileDialogOptions) string {
  921. var title, cwd, filter, del = fileDialogAdaptOptions(opts)
  922. defer del()
  923. var raw_path = C.QtFileDialogSave(
  924. parent.ptr,
  925. C.QtString(title), C.QtString(cwd), C.QtString(filter),
  926. )
  927. return ConsumeString(String(raw_path))
  928. }
  929. func InputDialog(parent Widget, title string, prompt string, password bool, text *string) bool {
  930. var title_q, del_title = NewString(title)
  931. var prompt_q, del_content = NewString(prompt)
  932. var text_q, del_text = NewString(*text)
  933. defer del_title()
  934. defer del_content()
  935. defer del_text()
  936. var ok = getBool(C.QtInputDialog(
  937. parent.ptr,
  938. C.QtString(title_q),
  939. C.QtString(prompt_q),
  940. makeQtBool(password),
  941. C.QtString(text_q),
  942. ))
  943. if ok {
  944. *text = copyString(text_q)
  945. }
  946. return ok
  947. }
  948. func InputDialogInt32(parent Widget, title string, prompt string, min int32, max int32, step int32, value *int32) bool {
  949. var title_q, del_title = NewString(title)
  950. var prompt_q, del_content = NewString(prompt)
  951. var value_q C.int
  952. defer del_title()
  953. defer del_content()
  954. var ok = getBool(C.QtInputDialogInt(
  955. parent.ptr,
  956. C.QtString(title_q),
  957. C.QtString(prompt_q),
  958. C.int(min),
  959. C.int(max),
  960. C.int(step),
  961. &value_q,
  962. ))
  963. if ok {
  964. *value = int32(value_q)
  965. }
  966. return ok
  967. }
  968. func InputDialogFloat64(parent Widget, title string, prompt string, min float64, max float64, step float64, decimals int32, value *float64) bool {
  969. var title_q, del_title = NewString(title)
  970. var prompt_q, del_content = NewString(prompt)
  971. var value_q C.double
  972. defer del_title()
  973. defer del_content()
  974. var ok = getBool(C.QtInputDialogDouble(
  975. parent.ptr,
  976. C.QtString(title_q),
  977. C.QtString(prompt_q),
  978. C.double(min),
  979. C.double(max),
  980. C.double(step),
  981. C.int(decimals),
  982. &value_q,
  983. ))
  984. if ok {
  985. *value = float64(value_q)
  986. }
  987. return ok
  988. }
  989. func MessageBoxConfirm(parent Widget, title string, content string) bool {
  990. var title_q, del_title = NewString(title)
  991. var content_q, del_content = NewString(content)
  992. defer del_title()
  993. defer del_content()
  994. return getBool(C.QtMessageBoxConfirm(
  995. parent.ptr,
  996. C.QtString(title_q), C.QtString(content_q),
  997. ))
  998. }
  999. func MessageBoxError(parent Widget, title string, content string) {
  1000. var title_q, del_title = NewString(title)
  1001. var content_q, del_content = NewString(content)
  1002. defer del_title()
  1003. defer del_content()
  1004. C.QtMessageBoxError(
  1005. parent.ptr,
  1006. C.QtString(title_q), C.QtString(content_q),
  1007. )
  1008. }
  1009. type Kvdb struct {
  1010. ptr unsafe.Pointer
  1011. }
  1012. func KvdbOpen(path string, exclusive bool) (Kvdb, error) {
  1013. var path_q, del_path = NewString(path)
  1014. defer del_path()
  1015. var d Kvdb
  1016. var ok = getBool(
  1017. C.KvdbOpen(&(d.ptr), C.QtString(path_q), makeQtBool(exclusive)),
  1018. )
  1019. if ok {
  1020. return d, nil
  1021. } else {
  1022. return Kvdb {}, fmt.Errorf("kvdb: failed to open database: %s", path)
  1023. }
  1024. }
  1025. func (d Kvdb) Close() {
  1026. C.KvdbClose(d.ptr)
  1027. }
  1028. func (d Kvdb) Insert(key string, category int32, value ([] byte)) error {
  1029. var key_q, del_key = NewString(key)
  1030. defer del_key()
  1031. var vb, vl = addrlen(value)
  1032. var ok = getBool(
  1033. C.KvdbInsert(d.ptr, C.QtString(key_q), C.int(category), vb, vl),
  1034. )
  1035. if ok {
  1036. return nil
  1037. } else {
  1038. return errors.New("kvdb: failed to insert record")
  1039. }
  1040. }
  1041. func (d Kvdb) Remove(key string) error {
  1042. var key_q, del_key = NewString(key)
  1043. defer del_key()
  1044. var ok = getBool(
  1045. C.KvdbRemove(d.ptr, C.QtString(key_q)),
  1046. )
  1047. if ok {
  1048. return nil
  1049. } else {
  1050. return errors.New("kvdb: failed to remove record")
  1051. }
  1052. }
  1053. func (d Kvdb) Update(key string, value ([] byte)) error {
  1054. var key_q, del_key = NewString(key)
  1055. defer del_key()
  1056. var vb, vl = addrlen(value)
  1057. var ok = getBool(
  1058. C.KvdbUpdate(d.ptr, C.QtString(key_q), vb, vl),
  1059. )
  1060. if ok {
  1061. return nil
  1062. } else {
  1063. return errors.New("kvdb: failed to update record")
  1064. }
  1065. }
  1066. func (d Kvdb) Lookup(key string) ([] byte, bool, error) {
  1067. var key_q, del_key = NewString(key)
  1068. defer del_key()
  1069. var value ByteArray
  1070. var found C.QtBool
  1071. var ok = getBool(
  1072. C.KvdbLookup(d.ptr, C.QtString(key_q),
  1073. &found, (*C.QtByteArray)(&value)),
  1074. )
  1075. if ok {
  1076. if getBool(found) {
  1077. return ConsumeByteArray(value), true, nil
  1078. } else {
  1079. return nil, false, nil
  1080. }
  1081. } else {
  1082. return nil, false, errors.New("kvdb: failed to lookup record")
  1083. }
  1084. }
  1085. func (d Kvdb) ReadAll() (KvdbSelectQuery, error) {
  1086. var q KvdbSelectQuery
  1087. var ok = getBool(C.KvdbReadAll(d.ptr, (*C.KvdbSelectQuery)(&q)))
  1088. if ok {
  1089. return q, nil
  1090. } else {
  1091. return KvdbSelectQuery {}, errors.New("kvdb: failed to read records")
  1092. }
  1093. }
  1094. func (d Kvdb) ReadCategory(category int) (KvdbSelectQuery, error) {
  1095. var q KvdbSelectQuery
  1096. var ok = getBool(
  1097. C.KvdbReadCategory(d.ptr, C.int(category), (*C.KvdbSelectQuery)(&q)),
  1098. )
  1099. if ok {
  1100. return q, nil
  1101. } else {
  1102. return KvdbSelectQuery {}, errors.New("kvdb: failed to read records")
  1103. }
  1104. }
  1105. type KvdbSelectQuery C.KvdbSelectQuery
  1106. func (q KvdbSelectQuery) Consume(item func(string, ([] byte))) {
  1107. var key String
  1108. var value ByteArray
  1109. for getBool(C.KvdbSelectQueryNext(
  1110. C.KvdbSelectQuery(q),
  1111. (*C.QtString)(&key),
  1112. (*C.QtByteArray)(&value),
  1113. )) {
  1114. item(ConsumeString(key), ConsumeByteArray(value))
  1115. }
  1116. C.KvdbDeleteSelectQuery(C.KvdbSelectQuery(q))
  1117. }