Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit \o/

  • Loading branch information...
commit 485d8130ddf046ee769397fee8832ce11deda22a 0 parents
@lxn authored
Showing with 12,136 additions and 0 deletions.
  1. +16 −0 AUTHORS
  2. +23 −0 LICENSE
  3. +83 −0 README
  4. +184 −0 crutches/crutches.cpp
  5. +4 −0 crutches/crutches.def
  6. +123 −0 crutches/crutches.go
  7. +22 −0 crutches/crutches.h
  8. +11 −0 crutches/crutches.pro
  9. +144 −0 drawing/brush.go
  10. +23 −0 drawing/color.go
  11. +297 −0 drawing/font.go
  12. +179 −0 drawing/pen.go
  13. +9 −0 drawing/point.go
  14. +77 −0 drawing/rectangle.go
  15. +9 −0 drawing/size.go
  16. +191 −0 drawing/surface.go
  17. +80 −0 drawing/util.go
  18. +9 −0 examples/drawing.exe.manifest
  19. +106 −0 examples/drawing.go
  20. +189 −0 gui/action.go
  21. +132 −0 gui/actionlist.go
  22. +13 −0 gui/application.go
  23. +274 −0 gui/boxlayout.go
  24. +86 −0 gui/button.go
  25. +54 −0 gui/checkbox.go
  26. +81 −0 gui/combobox.go
  27. +25 −0 gui/comboboxitem.go
  28. +105 −0 gui/comboboxitemlist.go
  29. +73 −0 gui/commondialogs.go
  30. +99 −0 gui/composite.go
  31. +132 −0 gui/container.go
  32. +66 −0 gui/dialog.go
  33. +54 −0 gui/groupbox.go
  34. +18 −0 gui/gui.go
  35. +54 −0 gui/imagelist.go
  36. +50 −0 gui/label.go
  37. +73 −0 gui/lineedit.go
  38. +292 −0 gui/listview.go
  39. +80 −0 gui/listviewcolumn.go
  40. +105 −0 gui/listviewcolumnlist.go
  41. +52 −0 gui/listviewitem.go
  42. +105 −0 gui/listviewitemlist.go
  43. +234 −0 gui/mainwindow.go
  44. +118 −0 gui/menu.go
  45. +23 −0 gui/messagebox.go
  46. +157 −0 gui/observedwidgetlist.go
  47. +59 −0 gui/progressbar.go
  48. +60 −0 gui/pushbutton.go
  49. +54 −0 gui/radiobutton.go
  50. +79 −0 gui/simpletypes.go
  51. +78 −0 gui/splitter.go
  52. +54 −0 gui/textedit.go
  53. +177 −0 gui/toolbar.go
  54. +101 −0 gui/tooltip.go
  55. +105 −0 gui/treeview.go
  56. +47 −0 gui/treeviewitem.go
  57. +106 −0 gui/treeviewitemlist.go
  58. +96 −0 gui/util.go
  59. +708 −0 gui/widget.go
  60. +37 −0 path/path.go
  61. +80 −0 path/util.go
  62. +407 −0 printing/pageinfo.go
  63. +51 −0 printing/papersize.go
  64. +42 −0 printing/papersource.go
  65. +799 −0 printing/printerinfo.go
  66. +70 −0 printing/printing.go
  67. +37 −0 printing/resolution.go
  68. +96 −0 printing/util.go
  69. +56 −0 registry/registry.go
  70. +80 −0 registry/util.go
  71. +86 −0 winapi/advapi32/advapi32.go
  72. +189 −0 winapi/comctl32/comctl32.go
  73. +300 −0 winapi/comctl32/listview.go
  74. +182 −0 winapi/comctl32/toolbar.go
  75. +101 −0 winapi/comctl32/tooltip.go
  76. +188 −0 winapi/comctl32/treeview.go
  77. +269 −0 winapi/comdlg32/comdlg32.go
  78. +1,049 −0 winapi/gdi32/gdi32.go
  79. +141 −0 winapi/kernel32/kernel32.go
  80. +110 −0 winapi/shell32/shell32.go
  81. +84 −0 winapi/user32/combobox.go
  82. +82 −0 winapi/user32/edit.go
  83. +77 −0 winapi/user32/menu.go
  84. +1,240 −0 winapi/user32/user32.go
  85. +41 −0 winapi/uxtheme/uxtheme.go
  86. +78 −0 winapi/winapi.go
  87. +106 −0 winapi/winspool/winspool.go
16 AUTHORS
@@ -0,0 +1,16 @@
+# This is the official list of 'Walk' authors for copyright purposes.
+
+# Names should be added to this file as
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+# Please keep the list sorted.
+
+# Creator
+# =======
+
+Alexander Neumann <an2048@googlemail.com>
+
+# Contributors
+# ============
+
23 LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2010 The Walk Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
83 README
@@ -0,0 +1,83 @@
+About Walk
+==========
+
+Walk is a "Windows Application Library Kit" for the Go Programming Language.
+
+It includes packages for 2-D drawing, graphical user interfaces, printing and more.
+The goal is to have a library that makes developing a Windows Desktop application
+using the Go Programming Language an easy walk.
+
+For now it will be restricted to those apis that are available out of the box on
+Windows XP SP3.
+
+Is it ready for production work?
+================================
+
+No, there is a lot of stuff missing and some existing parts may need to be redone.
+Don't rely on anything here to be stable yet, it will take some time.
+
+Setup
+=====
+
+As the Go toolchain makes crosscompiling very easy, I code and build on Linux and
+use Windows only to run the executables. Follow this guide if you want to do the
+same: http://groups.google.com/group/golang-nuts/msg/c940bb15ef1d2b4e
+
+Now clone the Walk git repo:
+git clone http://github.com/lxn/walk.git
+
+Currently no makefiles are provided, because I prefer building my Go stuff with
+gobuild. For a list of Go build tools see http://go-lang.cat-v.org/dev-utils.
+
+With gobuild I use a directory structure like this:
+goprojects/
+ pgsql/
+ walk/
+ mycoolapp1/
+ subpkg1/
+ subpkg2/
+ subpkg3/
+ mycoolapp1.go
+ someotherapp/
+ subpkg1/
+ subpkg2/
+ someotherapp.go
+
+To build mycoolapp1 you will want to do something like this:
+cd goprojects
+gobuild -o mycoolapp1.exe mycoolapp1/mycoolapp1.go
+
+This assumes mycoolapp1.go contains the main func of that app.
+
+Presently there is one nasty caveat: Windows apps will need window procedures to do
+the nice gui stuff, which means Windows will have to call a callback function in
+your Go code. This is currently not possible though, so there is an ugly workaround
+in the form of a helper dll. The code comes as part of Walk in the crutches directory.
+Copy all non-go crutches.* files to a Windows box and build a dll from it.
+
+Once the crutches.dll is built, copy it into the same directory as your app executable.
+
+Here is hope the situation will improve soonish: http://codereview.appspot.com/1696051/
+
+Some features of the Walk gui package require an application manifest file alongside
+your executable, to make use of common controls 6.0. See the examples directory for
+such a file.
+
+Using Walk
+==========
+
+See the examples directory for inspiration. Docs may appear as time permits ;)
+
+You should be aware that this project is far from being mature - anything could break
+or change without warning.
+
+Please open an issue on the bug tracker if you encounter a bug.
+
+Contribute
+==========
+
+This project is quite an undertaking and cannot be done by a single developer (who
+has many other projects to complete btw). If this project is of utility to you and
+you want to see it improve, please consider to contribute.
+
+Thank You!
184 crutches/crutches.cpp
@@ -0,0 +1,184 @@
+#define _WIN32_WINNT 0x501 // XP
+#define _WIN32_IE 0x501
+
+#include "crutches.h"
+
+#include <queue>
+
+#include <commctrl.h>
+
+using namespace std;
+
+#define WM_RESIZE_KEY 0
+#define WM_COMMAND_KEY 1
+#define WM_CONTEXTMENU_KEY 2
+#define WM_ITEMCHANGED_KEY 3
+#define WM_ITEMACTIVATE_KEY 4
+#define WM_CLOSE_KEY 5
+
+UINT _resizeMsgId;
+UINT _commandMsgId;
+UINT _contextMenuMsgId;
+UINT _itemChangedMsgId;
+UINT _itemActivateMsgId;
+UINT _closeMsgId;
+
+queue<Message> msgQueue;
+
+
+UINT WINAPI GetRegisteredMessageId(UINT key)
+{
+ switch (key) {
+ case WM_RESIZE_KEY:
+ if (0 == _resizeMsgId) {
+ _resizeMsgId = RegisterWindowMessageW(L"resize_0b0f95e6-7ef7-4767-b484-940e7a3cf4f1");
+ }
+ return _resizeMsgId;
+
+ case WM_COMMAND_KEY:
+ if (0 == _commandMsgId) {
+ _commandMsgId = RegisterWindowMessageW(L"command_442946bf-f806-434b-baa3-98439930eecd");
+ }
+ return _commandMsgId;
+
+ case WM_CONTEXTMENU_KEY:
+ if (0 == _contextMenuMsgId) {
+ _contextMenuMsgId = RegisterWindowMessageW(L"contextmenu_50fe6189-a94b-4826-8dc3-48e179f89ffc");
+ }
+ return _contextMenuMsgId;
+
+ case WM_ITEMCHANGED_KEY:
+ if (0 == _itemChangedMsgId) {
+ _itemChangedMsgId = RegisterWindowMessageW(L"itemchanged_b453604c-2195-4df2-8d9d-e6486d9bf73a");
+ }
+ return _itemChangedMsgId;
+
+ case WM_ITEMACTIVATE_KEY:
+ if (0 == _itemActivateMsgId) {
+ _itemActivateMsgId = RegisterWindowMessageW(L"itemactivate_1ff744a7-ff21-464c-b0a7-786843b75976");
+ }
+ return _itemActivateMsgId;
+
+ case WM_CLOSE_KEY:
+ if (0 == _closeMsgId) {
+ _closeMsgId = RegisterWindowMessageW(L"close_cd8f8d08-cdb1-42c0-9d8c-bcf63f4114d7");
+ }
+ return _closeMsgId;
+ }
+
+ return 0;
+}
+
+int WINAPI GetCustomMessage(Message* msg)
+{
+ if (0 == msg) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ if (msgQueue.empty()) {
+ return 0;
+ }
+
+ (*msg) = msgQueue.back();
+ msgQueue.pop();
+
+ return msgQueue.size() + 1;
+}
+
+bool EnqueueMessage(const Message &msg)
+{
+ if (msgQueue.size() >= 1000) {
+ return false;
+ }
+
+ msgQueue.push(msg);
+
+ return true;
+}
+
+LRESULT CALLBACK ContainerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_CLOSE:
+ PostMessageW(hwnd, _closeMsgId, wParam, lParam);
+ break;
+
+ case WM_CONTEXTMENU:
+ PostMessageW(hwnd, _contextMenuMsgId, wParam, lParam);
+
+ case WM_COMMAND:
+ PostMessageW(hwnd, _commandMsgId, wParam, lParam);
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR*)(lParam))->code) {
+ case LVN_ITEMCHANGED: {
+ NMLISTVIEW* nmlv = (NMLISTVIEW*)lParam;
+
+ Message m;
+ m.hwnd = nmlv->hdr.hwndFrom;
+ m.msg = _itemChangedMsgId;
+ m.wParam = 0;
+ m.lParam = nmlv->iItem;
+
+ EnqueueMessage(m);
+ break;
+ }
+
+ case LVN_ITEMACTIVATE: {
+ NMITEMACTIVATE* nmia = (NMITEMACTIVATE*)lParam;
+
+ Message m;
+ m.hwnd = nmia->hdr.hwndFrom;
+ m.msg = _itemActivateMsgId;
+ m.wParam = 0;
+ m.lParam = nmia->iItem;
+
+ EnqueueMessage(m);
+ break;
+ }
+ }
+
+ break;
+
+ case WM_SIZE:
+ case WM_SIZING: {
+ LRESULT ret = DefDlgProcW(hwnd, msg, wParam, lParam);
+ PostMessageW(hwnd, _resizeMsgId, 0, 0);
+ return ret;
+ }
+
+ default:
+ return DefDlgProcW(hwnd, msg, wParam, lParam);
+ }
+ return 0;
+}
+
+ATOM WINAPI RegisterWindowClass(HINSTANCE hInst)
+{
+ WNDCLASSEX wcButton;
+ WNDCLASSEX wc;
+
+ wc.hCursor = LoadCursor(hInst, IDC_ARROW);
+
+ if (GetClassInfoExW(hInst, L"BUTTON", &wcButton) != FALSE) {
+ wc.hCursor = wcButton.hCursor;
+ }
+
+ wc.cbSize = sizeof(WNDCLASSEX);
+ wc.style = 0;
+ wc.lpfnWndProc = ContainerWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = DLGWINDOWEXTRA;
+ wc.hInstance = hInst;
+ wc.hIcon = LoadIconW(hInst, IDI_APPLICATION);
+// wc.hCursor = LoadCursor(hInst, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = L"Container_WindowClass";
+ wc.hIconSm = LoadIcon(hInst, IDI_APPLICATION);
+
+ return RegisterClassExW(&wc);
+}
4 crutches/crutches.def
@@ -0,0 +1,4 @@
+EXPORTS
+ GetCustomMessage
+ GetRegisteredMessageId
+ RegisterWindowClass
123 crutches/crutches.go
@@ -0,0 +1,123 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package crutches
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+import (
+ . "walk/winapi"
+ . "walk/winapi/kernel32"
+ . "walk/winapi/user32"
+)
+
+// Registered message keys
+const (
+ _WM_RESIZE_KEY = 0
+ _WM_COMMAND_KEY = 1
+ _WM_CONTEXTMENU_KEY = 2
+ _WM_ITEMCHANGED_KEY = 3
+ _WM_ITEMACTIVATE_KEY = 4
+ _WM_CLOSE_KEY = 5
+)
+
+// Library
+var (
+ crutches uint32
+)
+
+// Functions
+var (
+ _getCustomMessage uint32
+ _getRegisteredMessageId uint32
+ _registerWindowClass uint32
+)
+
+// Registered message ids
+var (
+ closeMsgId uint
+ commandMsgId uint
+ contextMenuMsgId uint
+ itemActivateMsgId uint
+ itemChangedMsgId uint
+ resizeMsgId uint
+)
+
+func CloseMsgId() uint {
+ return closeMsgId
+}
+
+func CommandMsgId() uint {
+ return commandMsgId
+}
+
+func ContextMenuMsgId() uint {
+ return contextMenuMsgId
+}
+
+func ItemActivateMsgId() uint {
+ return itemActivateMsgId
+}
+
+func ItemChangedMsgId() uint {
+ return itemChangedMsgId
+}
+
+func ResizeMsgId() uint {
+ return resizeMsgId
+}
+
+type Message struct {
+ Hwnd HWND
+ Msg uint
+ WParam uintptr
+ LParam uintptr
+}
+
+func init() {
+ // Library
+ crutches = MustLoadLibrary("crutches.dll")
+
+ // Functions
+ _getCustomMessage = MustGetProcAddress(crutches, "GetCustomMessage@4")
+ _getRegisteredMessageId = MustGetProcAddress(crutches, "GetRegisteredMessageId@4")
+ _registerWindowClass = MustGetProcAddress(crutches, "RegisterWindowClass@4")
+
+ resizeMsgId = getRegisteredMessageId(_WM_RESIZE_KEY)
+ commandMsgId = getRegisteredMessageId(_WM_COMMAND_KEY)
+ contextMenuMsgId = getRegisteredMessageId(_WM_CONTEXTMENU_KEY)
+ itemChangedMsgId = getRegisteredMessageId(_WM_ITEMCHANGED_KEY)
+ itemActivateMsgId = getRegisteredMessageId(_WM_ITEMACTIVATE_KEY)
+ closeMsgId = getRegisteredMessageId(_WM_CLOSE_KEY)
+}
+
+func GetCustomMessage(msg *Message) int {
+ ret, _, _ := syscall.Syscall(uintptr(_getCustomMessage),
+ uintptr(unsafe.Pointer(msg)),
+ 0,
+ 0)
+
+ return int(ret)
+}
+
+func getRegisteredMessageId(key uint) uint {
+ ret, _, _ := syscall.Syscall(uintptr(_getRegisteredMessageId),
+ uintptr(key),
+ 0,
+ 0)
+
+ return uint(ret)
+}
+
+func RegisterWindowClass(hInstance HINSTANCE) ATOM {
+ ret, _, _ := syscall.Syscall(uintptr(_registerWindowClass),
+ uintptr(hInstance),
+ 0,
+ 0)
+
+ return ATOM(ret)
+}
22 crutches/crutches.h
@@ -0,0 +1,22 @@
+#ifndef CRUTCHES_H
+#define CRUTCHES_H
+
+#include <windows.h>
+
+struct Message {
+ HWND hwnd;
+ UINT msg;
+ WPARAM wParam;
+ LPARAM lParam;
+};
+
+extern "C"
+{
+ ATOM WINAPI RegisterWindowClass(HINSTANCE hInst);
+
+ UINT WINAPI GetRegisteredMessageId(UINT key);
+
+ int WINAPI GetCustomMessage(Message* msg);
+}
+
+#endif // CRUTCHES_H
11 crutches/crutches.pro
@@ -0,0 +1,11 @@
+# -------------------------------------------------
+# Project created by QtCreator 2010-06-24T15:19:09
+# -------------------------------------------------
+QT -= core \
+ gui
+TARGET = crutches
+TEMPLATE = lib
+DEFINES += CRUTCHES_LIBRARY
+SOURCES += crutches.cpp
+HEADERS += crutches.h
+OTHER_FILES += crutches.def
144 drawing/brush.go
@@ -0,0 +1,144 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+import (
+ "os"
+)
+
+import (
+ . "walk/winapi/gdi32"
+)
+
+type HatchStyle int
+
+const (
+ HatchHorizontal HatchStyle = HS_HORIZONTAL
+ HatchVertical HatchStyle = HS_VERTICAL
+ HatchForwardDiagonal HatchStyle = HS_FDIAGONAL
+ HatchBackwardDiagonal HatchStyle = HS_BDIAGONAL
+ HatchCross HatchStyle = HS_CROSS
+ HatchDiagonalCross HatchStyle = HS_DIAGCROSS
+)
+
+type Brush interface {
+ Dispose()
+ handle() HBRUSH
+ logbrush() *LOGBRUSH
+}
+
+type nullBrush struct {
+ hBrush HBRUSH
+}
+
+func newNullBrush() *nullBrush {
+ lb := &LOGBRUSH{LbStyle: BS_NULL}
+
+ hBrush := CreateBrushIndirect(lb)
+ if hBrush == 0 {
+ panic("failed to create null brush")
+ }
+
+ return &nullBrush{hBrush: hBrush}
+}
+
+func (b *nullBrush) Dispose() {
+ if b.hBrush != 0 {
+ DeleteObject(HGDIOBJ(b.hBrush))
+
+ b.hBrush = 0
+ }
+}
+
+func (b *nullBrush) handle() HBRUSH {
+ return b.hBrush
+}
+
+func (b *nullBrush) logbrush() *LOGBRUSH {
+ return &LOGBRUSH{LbStyle: BS_NULL}
+}
+
+var nullBrushSingleton Brush = newNullBrush()
+
+func NullBrush() Brush {
+ return nullBrushSingleton
+}
+
+type SolidColorBrush struct {
+ hBrush HBRUSH
+ color Color
+}
+
+func NewSolidColorBrush(color Color) (*SolidColorBrush, os.Error) {
+ lb := &LOGBRUSH{LbStyle: BS_SOLID, LbColor: COLORREF(color)}
+
+ hBrush := CreateBrushIndirect(lb)
+ if hBrush == 0 {
+ return nil, newError("CreateBrushIndirect failed")
+ }
+
+ return &SolidColorBrush{hBrush: hBrush, color: color}, nil
+}
+
+func (b *SolidColorBrush) Color() Color {
+ return b.color
+}
+
+func (b *SolidColorBrush) Dispose() {
+ if b.hBrush != 0 {
+ DeleteObject(HGDIOBJ(b.hBrush))
+
+ b.hBrush = 0
+ }
+}
+
+func (b *SolidColorBrush) handle() HBRUSH {
+ return b.hBrush
+}
+
+func (b *SolidColorBrush) logbrush() *LOGBRUSH {
+ return &LOGBRUSH{LbStyle: BS_SOLID, LbColor: COLORREF(b.color)}
+}
+
+type HatchBrush struct {
+ hBrush HBRUSH
+ color Color
+ style HatchStyle
+}
+
+func NewHatchBrush(color Color, style HatchStyle) (*HatchBrush, os.Error) {
+ lb := &LOGBRUSH{LbStyle: BS_HATCHED, LbColor: COLORREF(color), LbHatch: uintptr(style)}
+
+ hBrush := CreateBrushIndirect(lb)
+ if hBrush == 0 {
+ return nil, newError("CreateBrushIndirect failed")
+ }
+
+ return &HatchBrush{hBrush: hBrush, color: color, style: style}, nil
+}
+
+func (b *HatchBrush) Color() Color {
+ return b.color
+}
+
+func (b *HatchBrush) Dispose() {
+ if b.hBrush != 0 {
+ DeleteObject(HGDIOBJ(b.hBrush))
+
+ b.hBrush = 0
+ }
+}
+
+func (b *HatchBrush) handle() HBRUSH {
+ return b.hBrush
+}
+
+func (b *HatchBrush) logbrush() *LOGBRUSH {
+ return &LOGBRUSH{LbStyle: BS_HATCHED, LbColor: COLORREF(b.color), LbHatch: uintptr(b.style)}
+}
+
+func (b *HatchBrush) Style() HatchStyle {
+ return b.style
+}
23 drawing/color.go
@@ -0,0 +1,23 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+type Color uint32
+
+func RGB(r, g, b byte) Color {
+ return Color(uint32(r) | uint32(g)<<8 | uint32(b)<<16)
+}
+
+func (c Color) R() byte {
+ return byte(c & 0xff)
+}
+
+func (c Color) G() byte {
+ return byte((c >> 8) & 0xff)
+}
+
+func (c Color) B() byte {
+ return byte((c >> 16) & 0xff)
+}
297 drawing/font.go
@@ -0,0 +1,297 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+import (
+ "container/vector"
+ "os"
+ "syscall"
+)
+
+import (
+ . "walk/winapi/gdi32"
+ . "walk/winapi/user32"
+)
+
+// Font flags
+const (
+ font_bold = 0x01
+ font_italic = 0x02
+ font_underline = 0x04
+ font_strikeOut = 0x08
+ font_suspendUpdate = 0x40
+ font_dirty = 0x80
+)
+
+var (
+ screenDPIX int
+ screenDPIY int
+)
+
+func init() {
+ // Retrieve screen DPI
+ hDC := GetDC(0)
+ screenDPIX = GetDeviceCaps(hDC, LOGPIXELSX)
+ screenDPIY = GetDeviceCaps(hDC, LOGPIXELSY)
+ ReleaseDC(0, hDC)
+}
+
+type FontChangeHandler func(font *Font)
+
+type Font struct {
+ hFont HFONT
+ family string
+ pointSize float
+ flags byte
+ changingHandlers vector.Vector
+ changedHandlers vector.Vector
+}
+
+func NewFont() *Font {
+ return &Font{}
+}
+
+func (f *Font) create() HFONT {
+ var lf LOGFONT
+
+ lf.LfHeight = int(f.pointSize * float(screenDPIY) / float(72))
+ if f.Bold() {
+ lf.LfWeight = FW_BOLD
+ } else {
+ lf.LfWeight = FW_NORMAL
+ }
+ if f.Italic() {
+ lf.LfItalic = 1
+ }
+ if f.Underline() {
+ lf.LfUnderline = 1
+ }
+ if f.StrikeOut() {
+ lf.LfStrikeOut = 1
+ }
+ lf.LfCharSet = DEFAULT_CHARSET
+ lf.LfOutPrecision = OUT_TT_PRECIS
+ lf.LfClipPrecision = CLIP_DEFAULT_PRECIS
+ lf.LfQuality = CLEARTYPE_QUALITY
+ lf.LfPitchAndFamily = VARIABLE_PITCH | FF_SWISS
+
+ src := syscall.StringToUTF16(f.family)
+ dest := lf.LfFaceName[0:]
+ copy(dest, src)
+
+ return CreateFontIndirect(&lf)
+}
+
+func (f *Font) update() (err os.Error) {
+ f.raiseChanging()
+
+ hFont := f.create()
+ if hFont == 0 {
+ err = newError("failed to create font")
+ return
+ }
+
+ f.Dispose()
+
+ f.hFont = hFont
+
+ f.setFlag(font_dirty, false)
+
+ f.raiseChanged()
+
+ return
+}
+
+func (f *Font) Handle() HFONT {
+ return f.hFont
+}
+
+func (f *Font) flag(flag byte) bool {
+ return (f.flags & flag) != 0
+}
+
+func (f *Font) setFlag(flag byte, value bool) {
+ if value {
+ f.flags |= flag
+ } else {
+ f.flags &^= flag
+ }
+}
+
+func (f *Font) setFlagValue(flag byte, value bool) (err os.Error) {
+ old := f.flag(flag)
+ if value != old {
+ wasDirty := f.isDirty()
+
+ f.setFlag(flag, value)
+
+ err = f.setDirty()
+ if err != nil {
+ f.setFlag(flag, old)
+ if wasDirty {
+ f.setDirty()
+ }
+ }
+ }
+
+ return
+}
+
+func (f *Font) isDirty() bool {
+ return f.flag(font_dirty)
+}
+
+func (f *Font) setDirty() (err os.Error) {
+ f.setFlag(font_dirty, true)
+
+ if !f.flag(font_suspendUpdate) {
+ err = f.update()
+ }
+
+ return
+}
+
+func (f *Font) Dispose() {
+ if f.hFont != 0 {
+ DeleteObject(HGDIOBJ(f.hFont))
+ f.hFont = 0
+ }
+}
+
+func (f *Font) IsDisposed() bool {
+ return f.hFont == 0
+}
+
+func (f *Font) BeginEdit() {
+ f.setFlag(font_suspendUpdate, true)
+}
+
+func (f *Font) EndEdit() (err os.Error) {
+ f.setFlag(font_suspendUpdate, false)
+
+ if f.isDirty() {
+ err = f.update()
+ }
+
+ return
+}
+
+func (f *Font) Family() string {
+ return f.family
+}
+
+func (f *Font) SetFamily(value string) (err os.Error) {
+ old := f.family
+ if value != old {
+ wasDirty := f.isDirty()
+
+ f.family = value
+
+ err = f.setDirty()
+ if err != nil {
+ f.family = old
+ if wasDirty {
+ f.setDirty()
+ }
+ }
+ }
+
+ return
+}
+
+func (f *Font) Bold() bool {
+ return f.flag(font_bold)
+}
+
+func (f *Font) SetBold(value bool) os.Error {
+ return f.setFlagValue(font_bold, value)
+}
+
+func (f *Font) Italic() bool {
+ return f.flag(font_italic)
+}
+
+func (f *Font) SetItalic(value bool) os.Error {
+ return f.setFlagValue(font_italic, value)
+}
+
+func (f *Font) StrikeOut() bool {
+ return f.flag(font_strikeOut)
+}
+
+func (f *Font) SetStrikeOut(value bool) os.Error {
+ return f.setFlagValue(font_strikeOut, value)
+}
+
+func (f *Font) Underline() bool {
+ return f.flag(font_underline)
+}
+
+func (f *Font) SetUnderline(value bool) os.Error {
+ return f.setFlagValue(font_underline, value)
+}
+
+func (f *Font) PointSize() float {
+ return f.pointSize
+}
+
+func (f *Font) SetPointSize(value float) (err os.Error) {
+ old := f.pointSize
+ if value != old {
+ wasDirty := f.isDirty()
+
+ f.pointSize = value
+
+ err = f.setDirty()
+ if err != nil {
+ f.pointSize = old
+ if wasDirty {
+ f.setDirty()
+ }
+ }
+ }
+
+ return
+}
+
+func (f *Font) AddChangingHandler(handler FontChangeHandler) {
+ f.changingHandlers.Push(handler)
+}
+
+func (f *Font) RemoveChangingHandler(handler FontChangeHandler) {
+ for i, h := range f.changingHandlers {
+ if h.(FontChangeHandler) == handler {
+ f.changingHandlers.Delete(i)
+ break
+ }
+ }
+}
+
+func (f *Font) AddChangedHandler(handler FontChangeHandler) {
+ f.changedHandlers.Push(handler)
+}
+
+func (f *Font) RemoveChangedHandler(handler FontChangeHandler) {
+ for i, h := range f.changedHandlers {
+ if h.(FontChangeHandler) == handler {
+ f.changedHandlers.Delete(i)
+ break
+ }
+ }
+}
+
+func (f *Font) raiseChanging() {
+ for _, handlerIface := range f.changingHandlers {
+ handler := handlerIface.(FontChangeHandler)
+ handler(f)
+ }
+}
+
+func (f *Font) raiseChanged() {
+ for _, handlerIface := range f.changedHandlers {
+ handler := handlerIface.(FontChangeHandler)
+ handler(f)
+ }
+}
179 drawing/pen.go
@@ -0,0 +1,179 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+import (
+ "os"
+)
+
+import (
+ . "walk/winapi/gdi32"
+)
+
+type PenStyle int
+
+// Pen styles
+const (
+ PenSolid PenStyle = PS_SOLID
+ PenDash PenStyle = PS_DASH
+ PenDot PenStyle = PS_DOT
+ PenDashDot PenStyle = PS_DASHDOT
+ PenDashDotDot PenStyle = PS_DASHDOTDOT
+ PenNull PenStyle = PS_NULL
+ PenInsideFrame PenStyle = PS_INSIDEFRAME
+ PenUserStyle PenStyle = PS_USERSTYLE
+ PenAlternate PenStyle = PS_ALTERNATE
+)
+
+// Pen cap styles (geometric pens only)
+const (
+ PenCapRound PenStyle = PS_ENDCAP_ROUND
+ PenCapSquare PenStyle = PS_ENDCAP_SQUARE
+ PenCapFlat PenStyle = PS_ENDCAP_FLAT
+)
+
+// Pen join styles (geometric pens only)
+const (
+ PenJoinBevel PenStyle = PS_JOIN_BEVEL
+ PenJoinMiter PenStyle = PS_JOIN_MITER
+ PenJoinRound PenStyle = PS_JOIN_ROUND
+)
+
+type Pen interface {
+ handle() HPEN
+ Dispose()
+ Style() PenStyle
+ Width() int
+}
+
+type nullPen struct {
+ hPen HPEN
+}
+
+func newNullPen() *nullPen {
+ lb := &LOGBRUSH{LbStyle: BS_NULL}
+
+ hPen := ExtCreatePen(PS_COSMETIC|PS_NULL, 1, lb, 0, nil)
+ if hPen == 0 {
+ panic("failed to create null brush")
+ }
+
+ return &nullPen{hPen: hPen}
+}
+
+func (p *nullPen) Dispose() {
+ if p.hPen != 0 {
+ DeleteObject(HGDIOBJ(p.hPen))
+
+ p.hPen = 0
+ }
+}
+
+func (p *nullPen) handle() HPEN {
+ return p.hPen
+}
+
+func (p *nullPen) Style() PenStyle {
+ return PenNull
+}
+
+func (p *nullPen) Width() int {
+ return 0
+}
+
+var nullPenSingleton Pen = newNullPen()
+
+func NullPen() Pen {
+ return nullPenSingleton
+}
+
+type CosmeticPen struct {
+ hPen HPEN
+ style PenStyle
+ color Color
+}
+
+func NewCosmeticPen(style PenStyle, color Color) (*CosmeticPen, os.Error) {
+ lb := &LOGBRUSH{LbStyle: BS_SOLID, LbColor: COLORREF(color)}
+
+ style |= PS_COSMETIC
+
+ hPen := ExtCreatePen(uint(style), 1, lb, 0, nil)
+ if hPen == 0 {
+ return nil, newError("ExtCreatePen failed")
+ }
+
+ return &CosmeticPen{hPen: hPen, style: style, color: color}, nil
+}
+
+func (p *CosmeticPen) Dispose() {
+ if p.hPen != 0 {
+ DeleteObject(HGDIOBJ(p.hPen))
+
+ p.hPen = 0
+ }
+}
+
+func (p *CosmeticPen) handle() HPEN {
+ return p.hPen
+}
+
+func (p *CosmeticPen) Style() PenStyle {
+ return p.style
+}
+
+func (p *CosmeticPen) Color() Color {
+ return p.color
+}
+
+func (p *CosmeticPen) Width() int {
+ return 1
+}
+
+type GeometricPen struct {
+ hPen HPEN
+ style PenStyle
+ brush Brush
+ width int
+}
+
+func NewGeometricPen(style PenStyle, width int, brush Brush) (*GeometricPen, os.Error) {
+ if brush == nil {
+ return nil, newError("brush cannot be nil")
+ }
+
+ style |= PS_GEOMETRIC
+
+ hPen := ExtCreatePen(uint(style), uint(width), brush.logbrush(), 0, nil)
+ if hPen == 0 {
+ return nil, newError("ExtCreatePen failed")
+ }
+
+ return &GeometricPen{hPen: hPen, style: style, width: width, brush: brush}, nil
+}
+
+func (p *GeometricPen) Dispose() {
+ if p.hPen != 0 {
+ DeleteObject(HGDIOBJ(p.hPen))
+
+ p.hPen = 0
+ }
+}
+
+func (p *GeometricPen) handle() HPEN {
+ return p.hPen
+}
+
+func (p *GeometricPen) Style() PenStyle {
+ return p.style
+}
+
+func (p *GeometricPen) Width() int {
+ return p.width
+}
+
+func (p *GeometricPen) Brush() Brush {
+ return p.brush
+}
9 drawing/point.go
@@ -0,0 +1,9 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+type Point struct {
+ X, Y int
+}
77 drawing/rectangle.go
@@ -0,0 +1,77 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+import (
+ "walk/winapi/gdi32"
+)
+
+type Rectangle struct {
+ X, Y, Width, Height int
+}
+
+func (r *Rectangle) Left() int {
+ return r.X
+}
+
+func (r *Rectangle) Top() int {
+ return r.Y
+}
+
+func (r *Rectangle) Right() int {
+ return r.X + r.Width - 1
+}
+
+func (r *Rectangle) Bottom() int {
+ return r.Y + r.Height - 1
+}
+
+func (r *Rectangle) HCenter() int {
+ return r.X + r.Width/2
+}
+
+func (r *Rectangle) VCenter() int {
+ return r.Y + r.Height/2
+}
+
+func (r *Rectangle) Indent(value int) *Rectangle {
+ valueTwice := value * 2
+
+ r.X += value
+ r.Y += value
+ r.Width -= valueTwice
+ r.Height -= valueTwice
+
+ return r
+}
+
+func (r *Rectangle) HIndent(value int) *Rectangle {
+ r.X += value
+ r.Width -= value * 2
+
+ return r
+}
+
+func (r *Rectangle) VIndent(value int) *Rectangle {
+ r.Y += value
+ r.Height -= value * 2
+
+ return r
+}
+
+func (r *Rectangle) Size() Size {
+ return Size{r.Width, r.Height}
+}
+
+func (r *Rectangle) SetSize(s Size) *Rectangle {
+ r.Width = s.Width
+ r.Height = s.Height
+
+ return r
+}
+
+func (r *Rectangle) toRECT() *gdi32.RECT {
+ return &gdi32.RECT{r.X, r.Y, r.X + r.Width, r.Y + r.Height}
+}
9 drawing/size.go
@@ -0,0 +1,9 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+type Size struct {
+ Width, Height int
+}
191 drawing/surface.go
@@ -0,0 +1,191 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+import (
+ "os"
+ "syscall"
+)
+
+import (
+ . "walk/winapi/gdi32"
+ . "walk/winapi/user32"
+)
+
+// DrawText format flags
+type DrawTextFormat uint
+
+const (
+ TextTop DrawTextFormat = DT_TOP
+ TextLeft DrawTextFormat = DT_LEFT
+ TextCenter DrawTextFormat = DT_CENTER
+ TextRight DrawTextFormat = DT_RIGHT
+ TextVCenter DrawTextFormat = DT_VCENTER
+ TextBottom DrawTextFormat = DT_BOTTOM
+ TextWordbreak DrawTextFormat = DT_WORDBREAK
+ TextSingleLine DrawTextFormat = DT_SINGLELINE
+ TextExpandTabs DrawTextFormat = DT_EXPANDTABS
+ TextTabstop DrawTextFormat = DT_TABSTOP
+ TextNoClip DrawTextFormat = DT_NOCLIP
+ TextExternalLeading DrawTextFormat = DT_EXTERNALLEADING
+ TextCalcRect DrawTextFormat = DT_CALCRECT
+ TextNoPrefix DrawTextFormat = DT_NOPREFIX
+ TextInternal DrawTextFormat = DT_INTERNAL
+ TextEditControl DrawTextFormat = DT_EDITCONTROL
+ TextPathEllipsis DrawTextFormat = DT_PATH_ELLIPSIS
+ TextEndEllipsis DrawTextFormat = DT_END_ELLIPSIS
+ TextModifyString DrawTextFormat = DT_MODIFYSTRING
+ TextRTLReading DrawTextFormat = DT_RTLREADING
+ TextWordEllipsis DrawTextFormat = DT_WORD_ELLIPSIS
+ TextNoFullWidthCharBreak DrawTextFormat = DT_NOFULLWIDTHCHARBREAK
+ TextHidePrefix DrawTextFormat = DT_HIDEPREFIX
+ TextPrefixOnly DrawTextFormat = DT_PREFIXONLY
+)
+
+type Surface struct {
+ hdc HDC
+ hwnd HWND
+}
+
+func NewCompatibleBitmapSurface(size Size) (*Surface, os.Error) {
+ return nil, newError("not implemented")
+}
+
+func NewDeviceSurface(driver, device string, devMode *DEVMODE) (*Surface, os.Error) {
+ hdc := CreateDC(syscall.StringToUTF16Ptr(driver), syscall.StringToUTF16Ptr(device), nil, devMode)
+ if hdc == 0 {
+ return nil, newError("CreateDC failed")
+ }
+
+ if SetBkMode(hdc, TRANSPARENT) == 0 {
+ return nil, newError("SetBkMode failed")
+ }
+
+ return &Surface{hdc: hdc}, nil
+}
+
+func NewWidgetSurface(hwnd HWND) (*Surface, os.Error) {
+ hdc := GetDC(hwnd)
+ if hdc == 0 {
+ return nil, newError("GetDC failed")
+ }
+
+ if SetBkMode(hdc, TRANSPARENT) == 0 {
+ return nil, newError("SetBkMode failed")
+ }
+
+ return &Surface{hdc: hdc, hwnd: hwnd}, nil
+}
+
+func (s *Surface) Dispose() {
+ if s.hdc != 0 {
+ if s.hwnd == 0 {
+ DeleteDC(s.hdc)
+ } else {
+ ReleaseDC(s.hwnd, s.hdc)
+ }
+
+ s.hdc = 0
+ }
+}
+
+func (s *Surface) withGdiObj(handle HGDIOBJ, f func() os.Error) os.Error {
+ oldHandle := SelectObject(s.hdc, handle)
+ if oldHandle == 0 {
+ return newError("SelectObject failed")
+ }
+ defer SelectObject(s.hdc, oldHandle)
+
+ return f()
+}
+
+func (s *Surface) withBrush(brush Brush, f func() os.Error) os.Error {
+ return s.withGdiObj(HGDIOBJ(brush.handle()), f)
+}
+
+func (s *Surface) withFontAndTextColor(font *Font, color Color, f func() os.Error) os.Error {
+ return s.withGdiObj(HGDIOBJ(font.hFont), func() os.Error {
+ oldColor := SetTextColor(s.hdc, COLORREF(color))
+ if oldColor == CLR_INVALID {
+ return newError("SetTextColor failed")
+ }
+ defer func() {
+ SetTextColor(s.hdc, oldColor)
+ }()
+
+ return f()
+ })
+}
+
+func (s *Surface) withPen(pen Pen, f func() os.Error) os.Error {
+ return s.withGdiObj(HGDIOBJ(pen.handle()), f)
+}
+
+func (s *Surface) withBrushAndPen(brush Brush, pen Pen, f func() os.Error) os.Error {
+ return s.withBrush(brush, func() os.Error {
+ return s.withPen(pen, f)
+ })
+}
+
+func (s *Surface) ellipse(brush Brush, pen Pen, bounds *Rectangle) os.Error {
+ return s.withBrushAndPen(brush, pen, func() os.Error {
+ if !Ellipse(s.hdc, bounds.X, bounds.Y, bounds.X+bounds.Width, bounds.Y+bounds.Height) {
+ return newError("Ellipse failed")
+ }
+
+ return nil
+ })
+}
+
+func (s *Surface) DrawEllipse(pen Pen, bounds *Rectangle) os.Error {
+ return s.ellipse(nullBrushSingleton, pen, bounds)
+}
+
+func (s *Surface) FillEllipse(brush Brush, bounds *Rectangle) os.Error {
+ return s.ellipse(brush, nullPenSingleton, bounds)
+}
+
+func (s *Surface) DrawLine(pen Pen, from, to Point) os.Error {
+ if !MoveToEx(s.hdc, from.X, from.Y, nil) {
+ return newError("MoveToEx failed")
+ }
+
+ return s.withPen(pen, func() os.Error {
+ if !LineTo(s.hdc, to.X, to.Y) {
+ return newError("LineTo failed")
+ }
+
+ return nil
+ })
+}
+
+func (s *Surface) rectangle(brush Brush, pen Pen, bounds *Rectangle) os.Error {
+ return s.withBrushAndPen(brush, pen, func() os.Error {
+ if !Rectangle_(s.hdc, bounds.X, bounds.Y, bounds.X+bounds.Width, bounds.Y+bounds.Height) {
+ return newError("Rectangle_ failed")
+ }
+
+ return nil
+ })
+}
+
+func (s *Surface) DrawRectangle(pen Pen, bounds *Rectangle) os.Error {
+ return s.rectangle(nullBrushSingleton, pen, bounds)
+}
+
+func (s *Surface) FillRectangle(brush Brush, bounds *Rectangle) os.Error {
+ return s.rectangle(brush, nullPenSingleton, bounds)
+}
+
+func (s *Surface) DrawText(text string, font *Font, color Color, bounds *Rectangle, format DrawTextFormat) os.Error {
+ return s.withFontAndTextColor(font, color, func() os.Error {
+ ret := DrawTextEx(s.hdc, syscall.StringToUTF16Ptr(text), -1, bounds.toRECT(), uint(format), nil)
+ if ret == 0 {
+ return newError("DrawTextEx failed")
+ }
+
+ return nil
+ })
+}
80 drawing/util.go
@@ -0,0 +1,80 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package drawing
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "runtime"
+ "syscall"
+)
+
+import (
+ . "walk/winapi"
+ . "walk/winapi/kernel32"
+)
+
+func callStack() string {
+ buf := bytes.NewBuffer(nil)
+
+ buf.WriteString("=======================================================\n")
+
+ i := 0
+ for {
+ pc, file, line, ok := runtime.Caller(i + 1)
+ if !ok {
+ break
+ }
+ if i > 0 {
+ buf.WriteString("-------------------------------------------------------\n")
+ }
+
+ fun := runtime.FuncForPC(pc)
+ name := fun.Name()
+
+ buf.WriteString(fmt.Sprintf("%s (%s, Line %d)\n", name, file, line))
+
+ i++
+ }
+
+ buf.WriteString("=======================================================\n")
+
+ return buf.String()
+}
+
+func printCallStack() {
+ fmt.Print(callStack())
+}
+
+func panicIfErr(err os.Error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+func toError(x interface{}) os.Error {
+ switch x := x.(type) {
+ case os.Error:
+ return x
+
+ case string:
+ return newError(x)
+ }
+
+ return newError(fmt.Sprintf("Error: %v", x))
+}
+
+func newError(message string) os.Error {
+ return os.NewError(fmt.Sprintf("%s\nCall Stack:\n", message, callStack()))
+}
+
+func lastError(win32FuncName string) os.Error {
+ if errno := GetLastError(); errno != ERROR_SUCCESS {
+ return newError(fmt.Sprintf("%s: %s", win32FuncName, syscall.Errstr(int(errno))))
+ }
+
+ return nil
+}
9 examples/drawing.exe.manifest
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="SomeFunkyNameHere" type="win32"/>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*"/>
+ </dependentAssembly>
+ </dependency>
+</assembly>
106 examples/drawing.go
@@ -0,0 +1,106 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+)
+
+import (
+ "walk/drawing"
+ "walk/gui"
+)
+
+type MainWindow struct {
+ *gui.MainWindow
+ treeView *gui.TreeView
+}
+
+func panicIfErr(err os.Error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+func (mw *MainWindow) drawStuff() {
+ bounds, err := mw.treeView.ClientBounds()
+ panicIfErr(err)
+
+ surface, err := mw.treeView.GetDrawingSurface()
+ panicIfErr(err)
+ defer surface.Dispose()
+
+ rectPen, err := drawing.NewCosmeticPen(drawing.PenSolid, drawing.RGB(255, 0, 0))
+ panicIfErr(err)
+ defer rectPen.Dispose()
+
+ panicIfErr(surface.DrawRectangle(rectPen, bounds))
+
+ font := drawing.NewFont()
+ font.BeginEdit()
+ font.SetFamily("Tahoma")
+ font.SetPointSize(36)
+ font.SetBold(true)
+ panicIfErr(font.EndEdit())
+ defer font.Dispose()
+
+ text := strings.Repeat("Hello! ", 10)
+ panicIfErr(surface.DrawText(text, font, drawing.RGB(255, 192, 128), bounds, drawing.TextWordbreak))
+
+ ellipseBrush, err := drawing.NewHatchBrush(drawing.RGB(0, 255, 0), drawing.HatchCross)
+ panicIfErr(err)
+ defer ellipseBrush.Dispose()
+
+ panicIfErr(surface.FillEllipse(ellipseBrush, bounds))
+
+ linesBrush, err := drawing.NewSolidColorBrush(drawing.RGB(0, 0, 255))
+ panicIfErr(err)
+ defer linesBrush.Dispose()
+
+ linesPen, err := drawing.NewGeometricPen(drawing.PenDash, 8, linesBrush)
+ panicIfErr(err)
+ defer linesPen.Dispose()
+
+ panicIfErr(surface.DrawLine(linesPen, drawing.Point{bounds.X, bounds.Y}, drawing.Point{bounds.Width, bounds.Height}))
+ panicIfErr(surface.DrawLine(linesPen, drawing.Point{bounds.X, bounds.Height}, drawing.Point{bounds.Width, bounds.Y}))
+}
+
+func runMainWindow() {
+ mainWnd, err := gui.NewMainWindow()
+ panicIfErr(err)
+ defer mainWnd.Dispose()
+
+ mw := &MainWindow{MainWindow: mainWnd}
+
+ mw.ClientArea().SetLayout(gui.NewVBoxLayout())
+
+ drawButton, err := gui.NewPushButton(mw.ClientArea())
+ panicIfErr(err)
+ panicIfErr(drawButton.SetText("Draw Stuff!"))
+ drawButton.AddClickedHandler(func(args gui.EventArgs) { mw.drawStuff() })
+
+ mw.treeView, err = gui.NewTreeView(mw.ClientArea())
+ panicIfErr(err)
+
+ panicIfErr(mw.SetSize(drawing.Size{800, 600}))
+ mw.Show()
+
+ panicIfErr(mw.RunMessageLoop())
+}
+
+func main() {
+ runtime.LockOSThread()
+
+ defer func() {
+ if x := recover(); x != nil {
+ fmt.Println("Error:", x)
+ }
+ }()
+
+ runMainWindow()
+}
189 gui/action.go
@@ -0,0 +1,189 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+ "container/vector"
+ "os"
+)
+
+type actionChangedHandler interface {
+ onActionChanged(action *Action) (err os.Error)
+}
+
+var (
+ // ISSUE: When pressing enter resp. escape,
+ // WM_COMMAND with wParam=1 resp. 2 is sent.
+ // Maybe there is more to consider.
+ nextActionId uint16 = 3
+ actionsById map[uint16]*Action = make(map[uint16]*Action)
+)
+
+type Action struct {
+ menu *Menu
+ triggeredHandlers vector.Vector
+ changedHandlers vector.Vector
+ text string
+ toolTip string
+ imageIndex int
+ enabled bool
+ visible bool
+ id uint16
+}
+
+func NewAction() *Action {
+ a := &Action{id: nextActionId}
+
+ actionsById[a.id] = a
+
+ nextActionId++
+
+ return a
+}
+
+func (a *Action) Enabled() bool {
+ return a.enabled
+}
+
+func (a *Action) SetEnabled(value bool) (err os.Error) {
+ if value != a.enabled {
+ old := a.enabled
+
+ a.enabled = value
+
+ err = a.raiseChanged()
+ if err != nil {
+ a.enabled = old
+ a.raiseChanged()
+ }
+ }
+
+ return
+}
+
+func (a *Action) ImageIndex() int {
+ return a.imageIndex
+}
+
+func (a *Action) SetImageIndex(value int) (err os.Error) {
+ if value != a.imageIndex {
+ old := a.imageIndex
+
+ a.imageIndex = value
+
+ err = a.raiseChanged()
+ if err != nil {
+ a.imageIndex = old
+ a.raiseChanged()
+ }
+ }
+
+ return
+}
+
+func (a *Action) Text() string {
+ return a.text
+}
+
+func (a *Action) SetText(value string) (err os.Error) {
+ if value != a.text {
+ old := a.text
+
+ a.text = value
+
+ err = a.raiseChanged()
+ if err != nil {
+ a.text = old
+ a.raiseChanged()
+ }
+ }
+
+ return
+}
+
+func (a *Action) ToolTip() string {
+ return a.toolTip
+}
+
+func (a *Action) SetToolTip(value string) (err os.Error) {
+ if value != a.toolTip {
+ old := a.toolTip
+
+ a.toolTip = value
+
+ err = a.raiseChanged()
+ if err != nil {
+ a.toolTip = old
+ a.raiseChanged()
+ }
+ }
+
+ return
+}
+
+func (a *Action) Visible() bool {
+ return a.visible
+}
+
+func (a *Action) SetVisible(value bool) (err os.Error) {
+ if value != a.visible {
+ old := a.visible
+
+ a.visible = value
+
+ err = a.raiseChanged()
+ if err != nil {
+ a.visible = old
+ a.raiseChanged()
+ }
+ }
+
+ return
+}
+
+func (a *Action) AddTriggeredHandler(handler EventHandler) {
+ a.triggeredHandlers.Push(handler)
+}
+
+func (a *Action) RemoveTriggeredHandler(handler EventHandler) {
+ for i, h := range a.triggeredHandlers {
+ if h.(EventHandler) == handler {
+ a.triggeredHandlers.Delete(i)
+ break
+ }
+ }
+}
+
+func (a *Action) raiseTriggered() {
+ for _, handlerIface := range a.triggeredHandlers {
+ handler := handlerIface.(EventHandler)
+ handler(&eventArgs{a})
+ }
+}
+
+func (a *Action) addChangedHandler(handler actionChangedHandler) {
+ a.changedHandlers.Push(handler)
+}
+
+func (a *Action) removeChangedHandler(handler actionChangedHandler) {
+ for i, h := range a.changedHandlers {
+ if h.(actionChangedHandler) == handler {
+ a.changedHandlers.Delete(i)
+ break
+ }
+ }
+}
+
+func (a *Action) raiseChanged() (err os.Error) {
+ for _, handlerIface := range a.changedHandlers {
+ handler := handlerIface.(actionChangedHandler)
+ err = handler.onActionChanged(a)
+ if err != nil {
+ return
+ }
+ }
+
+ return
+}
132 gui/actionlist.go
@@ -0,0 +1,132 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+ "container/vector"
+ "os"
+)
+
+type actionListObserver interface {
+ onInsertingAction(index int, action *Action) (err os.Error)
+ onRemovingAction(index int, action *Action) (err os.Error)
+ onClearingActions() (err os.Error)
+}
+
+type ActionList struct {
+ actions vector.Vector
+ observer actionListObserver
+}
+
+func newActionList(observer actionListObserver) *ActionList {
+ return &ActionList{observer: observer}
+}
+
+func (l *ActionList) Add(action *Action) (index int, err os.Error) {
+ index = l.actions.Len()
+ err = l.Insert(index, action)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+func (l *ActionList) AddMenu(menu *Menu) (index int, action *Action, err os.Error) {
+ index = l.actions.Len()
+ action, err = l.InsertMenu(index, menu)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+func (l *ActionList) At(index int) *Action {
+ return l.actions[index].(*Action)
+}
+
+func (l *ActionList) Clear() (err os.Error) {
+ observer := l.observer
+ if observer != nil {
+ err = observer.onClearingActions()
+ if err != nil {
+ return
+ }
+ }
+
+ l.actions.Resize(0, 8)
+
+ return
+}
+
+func (l *ActionList) IndexOf(action *Action) int {
+ for i, a := range l.actions {
+ if a.(*Action) == action {
+ return i
+ }
+ }
+
+ return -1
+}
+
+func (l *ActionList) Insert(index int, action *Action) (err os.Error) {
+ observer := l.observer
+ if observer != nil {
+ err = observer.onInsertingAction(index, action)
+ if err != nil {
+ return
+ }
+ }
+
+ l.actions.Insert(index, action)
+
+ return
+}
+
+func (l *ActionList) InsertMenu(index int, menu *Menu) (action *Action, err os.Error) {
+ action = NewAction()
+ action.menu = menu
+
+ observer := l.observer
+ if observer != nil {
+ err = observer.onInsertingAction(index, action)
+ if err != nil {
+ return
+ }
+ }
+
+ l.actions.Insert(index, action)
+
+ return
+}
+
+func (l *ActionList) Len() int {
+ return l.actions.Len()
+}
+
+func (l *ActionList) Remove(action *Action) (err os.Error) {
+ index := l.IndexOf(action)
+ if index == -1 {
+ return
+ }
+
+ return l.RemoveAt(index)
+}
+
+func (l *ActionList) RemoveAt(index int) (err os.Error) {
+ observer := l.observer
+ if observer != nil {
+ action := l.actions[index].(*Action)
+ err = observer.onRemovingAction(index, action)
+ if err != nil {
+ return
+ }
+ }
+
+ l.actions.Delete(index)
+
+ return
+}
13 gui/application.go
@@ -0,0 +1,13 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+ . "walk/winapi/user32"
+)
+
+func Exit(exitCode int) {
+ PostQuitMessage(exitCode)
+}
274 gui/boxlayout.go
@@ -0,0 +1,274 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+ // "log"
+ "os"
+)
+
+import (
+ "walk/drawing"
+)
+
+type BoxLayout struct {
+ container IContainer
+ margins *Margins
+ spacing int
+ vertical bool
+}
+
+func NewHBoxLayout() *BoxLayout {
+ return &BoxLayout{margins: &Margins{}}
+}
+
+func NewVBoxLayout() *BoxLayout {
+ return &BoxLayout{margins: &Margins{}, vertical: true}
+}
+
+func (l *BoxLayout) Container() IContainer {
+ return l.container
+}
+
+func (l *BoxLayout) SetContainer(value IContainer) {
+ if value != l.container {
+ if l.container != nil {
+ l.container.SetLayout(nil)
+ }
+
+ l.container = value
+
+ if value != nil && value.Layout() != Layout(l) {
+ value.SetLayout(l)
+
+ l.Update(true)
+ }
+ }
+}
+
+func (l *BoxLayout) Margins() *Margins {
+ return l.margins
+}
+
+func (l *BoxLayout) SetMargins(value *Margins) os.Error {
+ if value == nil {
+ return newError("margins cannot be nil")
+ }
+
+ l.margins = value
+
+ return nil
+}
+
+func (l *BoxLayout) Spacing() int {
+ return l.spacing
+}
+
+func (l *BoxLayout) SetSpacing(value int) os.Error {
+ if value != l.spacing {
+ if value < 0 {
+ return newError("spacing cannot be negative")
+ }
+
+ l.spacing = value
+
+ l.Update(false)
+ }
+
+ return nil
+}
+
+func (l *BoxLayout) Update(reset bool) (err os.Error) {
+ if l.container == nil {
+ return
+ }
+
+ // log.Stdout("*BoxLayout.Update")
+
+ widgets := make([]IWidget, 0, l.container.Children().Len())
+
+ children := l.container.Children()
+ j := 0
+ for i := 0; i < cap(widgets); i++ {
+ widget := children.At(i)
+
+ ps := widget.PreferredSize()
+ if ps.Width == 0 && ps.Height == 0 && widget.LayoutFlags() == 0 {
+ continue
+ }
+
+ widgets = widgets[0 : j+1]
+ widgets[j] = widget
+ j++
+ }
+
+ widgetCount := len(widgets)
+
+ if widgetCount == 0 {
+ return
+ }
+
+ // We will start by collecting some valuable information.
+ flags := make([]LayoutFlags, widgetCount)
+ prefSizes := make([]drawing.Size, widgetCount)
+ var prefSizeSum drawing.Size
+ var shrinkHorzCount, growHorzCount, shrinkVertCount, growVertCount int
+
+ for i := 0; i < widgetCount; i++ {
+ widget := widgets[i]
+
+ ps := widget.PreferredSize()
+
+ maxSize, err := widget.MaxSize()
+ if err != nil {
+ return err
+ }
+
+ lf := widget.LayoutFlags()
+ if maxSize.Width > 0 {
+ lf &^= GrowHorz
+ ps.Width = maxSize.Width
+ }
+ if maxSize.Height > 0 {
+ lf &^= GrowVert
+ ps.Height = maxSize.Height
+ }
+
+ if lf&ShrinkHorz > 0 {
+ shrinkHorzCount++
+ }
+ if lf&GrowHorz > 0 {
+ growHorzCount++
+ }
+ if lf&ShrinkVert > 0 {
+ shrinkVertCount++
+ }
+ if lf&GrowVert > 0 {
+ growVertCount++
+ }
+ flags[i] = lf
+
+ prefSizeSum.Width += ps.Width
+ prefSizeSum.Height += ps.Height
+ prefSizes[i] = ps
+ }
+
+ cb, err := l.container.ClientBounds()
+ if err != nil {
+ return
+ }
+
+ spacingSum := (widgetCount - 1) * l.spacing
+
+ // Now do the actual layout thing.
+ if l.vertical {
+ diff := cb.Height - l.margins.Top - prefSizeSum.Height - spacingSum - l.margins.Bottom
+
+ reqW := 0
+
+ for i, s := range prefSizes {
+ if s.Width > reqW && (flags[i]&ShrinkHorz == 0) {
+ reqW = s.Width
+ }
+ }
+ // if reqW == 0 {
+ reqW = cb.Width - l.margins.Left - l.margins.Right
+ // }
+
+ var change int
+ if diff < 0 {
+ if shrinkVertCount > 0 {
+ change = diff / shrinkVertCount
+ }
+ } else {
+ if growVertCount > 0 {
+ change = diff / growVertCount
+ }
+ }
+
+ // log.Stdoutf("*BoxLayout.Update: widgetCount: %d, cb: %+v, prefSizeSum: %+v, diff: %d, change: %d, reqW: %d", widgetCount, cb, prefSizeSum, diff, change, reqW)
+
+ y := cb.Y + l.margins.Top
+ for i := 0; i < widgetCount; i++ {
+ widget := widgets[i]
+
+ h := prefSizes[i].Height
+
+ switch {
+ case change < 0:
+ if flags[i]&ShrinkVert > 0 {
+ h += change
+ }
+
+ case change > 0:
+ if flags[i]&GrowVert > 0 {
+ h += change
+ }
+ }
+
+ bounds := &drawing.Rectangle{cb.X + l.margins.Left, y, reqW, h}
+
+ // log.Stdoutf("*BoxLayout.Update: bounds: %+v", bounds)
+
+ widget.SetBounds(bounds)
+
+ y += h + l.spacing
+ }
+ } else {
+ diff := cb.Width - l.margins.Left - prefSizeSum.Width - spacingSum - l.margins.Right
+ reqH := 0
+
+ for i, s := range prefSizes {
+ if s.Height > reqH && (flags[i]&ShrinkVert == 0) {
+ reqH = s.Height
+ }
+ }
+ // if reqH == 0 {
+ reqH = cb.Height - l.margins.Top - l.margins.Bottom
+ // }
+
+ var change int
+ if diff < 0 {
+ if shrinkHorzCount > 0 {
+ change = diff / shrinkHorzCount
+ }
+ } else {
+ if growHorzCount > 0 {
+ change = diff / growHorzCount
+ }
+ }
+
+ // log.Stdoutf("*BoxLayout.Update: widgetCount: %d, cb: %+v, prefSizeSum: %+v, diff: %d, change: %d, reqH: %d", widgetCount, cb, prefSizeSum, diff, change, reqH)
+
+ x := cb.X + l.margins.Left
+ for i := 0; i < widgetCount; i++ {
+ widget := widgets[i]
+
+ w := prefSizes[i].Width
+
+ switch {
+ case change < 0:
+ if flags[i]&ShrinkHorz > 0 {
+ w += change
+ }
+
+ case change > 0:
+ if flags[i]&GrowHorz > 0 {
+ w += change
+ }
+ }
+
+ bounds := &drawing.Rectangle{x, cb.Y + l.margins.Top, w, reqH}
+
+ // log.Stdoutf("*BoxLayout.Update: bounds: %+v", bounds)
+
+ widget.SetBounds(bounds)
+
+ x += w + l.spacing
+ }
+ }
+
+ return
+}
86 gui/button.go
@@ -0,0 +1,86 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+ "container/vector"
+ "os"
+)
+
+import (
+ . "walk/winapi/user32"
+)
+
+type Button struct {
+ Widget
+ clickedHandlers vector.Vector
+}
+
+func (b *Button) Checked() bool {
+ return SendMessage(b.hWnd, BM_GETCHECK, 0, 0) == BST_CHECKED
+}
+
+func (b *Button) SetChecked(value bool) {
+ var chk uintptr
+
+ if value {
+ chk = BST_CHECKED
+ } else {
+ chk = BST_UNCHECKED
+ }
+
+ SendMessage(b.hWnd, BM_SETCHECK, chk, 0)
+}
+
+func (b *Button) AddClickedHandler(handler EventHandler) {
+ b.clickedHandlers.Push(handler)
+}
+
+func (b *Button) RemoveClickedHandler(handler EventHandler) {
+ for i, h := range b.clickedHandlers {
+ if h.(EventHandler) == handler {
+ b.clickedHandlers.Delete(i)
+ break
+ }
+ }
+}
+
+func (b *Button) raiseClicked() {
+ for _, handlerIface := range b.clickedHandlers {
+ handler := handlerIface.(EventHandler)
+ handler(&eventArgs{widgetsByHWnd[b.hWnd]})
+ }
+}
+
+func (b *Button) raiseEvent(msg *MSG) os.Error {
+ b.Widget.raiseEvent(msg)
+
+ switch msg.Message {
+ case WM_KEYUP:
+ if msg.WParam != VK_SPACE {
+ break
+ }
+
+ b.raiseClicked()
+
+ case WM_LBUTTONUP:
+ if trackedMouseDownHWnd != b.Widget.hWnd {
+ break
+ }
+ bounds, err := b.ClientBounds()
+ if err != nil {
+ return err
+ }
+
+ x, y := GET_X_LPARAM(msg.LParam), GET_Y_LPARAM(msg.LParam)
+ if x < 0 || x >= bounds.Width || y < 0 || y >= bounds.Height {
+ break
+ }
+
+ b.raiseClicked()
+ }
+
+ return nil
+}
54 gui/checkbox.go
@@ -0,0 +1,54 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+ "os"
+ "syscall"
+)
+
+import (
+ "walk/drawing"
+ . "walk/winapi/user32"
+)
+
+type CheckBox struct {
+ Button
+}
+
+func NewCheckBox(parent IContainer) (*CheckBox, os.Error) {
+ if parent == nil {
+ return nil, newError("parent cannot be nil")
+ }
+
+ hWnd := CreateWindowEx(
+ 0, syscall.StringToUTF16Ptr("BUTTON"), nil,
+ BS_AUTOCHECKBOX /*|BS_NOTIFY*/ |WS_CHILD|WS_TABSTOP|WS_VISIBLE,
+ 0, 0, 120, 24, parent.Handle(), 0, 0, nil)
+ if hWnd == 0 {
+ return nil, lastError("CreateWindowEx")
+ }
+
+ cb := &CheckBox{Button: Button{Widget: Widget{hWnd: hWnd, parent: parent}}}
+ cb.SetFont(defaultFont)
+
+ widgetsByHWnd[hWnd] = cb
+
+ parent.Children().Add(cb)
+
+ return cb, nil
+}
+
+func (*CheckBox) LayoutFlags() LayoutFlags {
+ return ShrinkHorz | GrowHorz
+}
+
+func (cb *CheckBox) PreferredSize() drawing.Size {
+ return cb.dialogBaseUnitsToPixels(drawing.Size{50, 10})
+}
+
+func (cb *CheckBox) raiseEvent(msg *MSG) os.Error {
+ return cb.Button.raiseEvent(msg)
+}
81 gui/combobox.go
@@ -0,0 +1,81 @@
+// Copyright 2010 The Walk Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+import (
+ "walk/drawing"
+ . "walk/winapi/user32"
+)
+
+type ComboBox struct {
+ Widget
+ items *ComboBoxItemList
+}
+
+func NewComboBox(parent IContainer) (*ComboBox, os.Error) {
+ if parent == nil {