├── 2048 ├── MainWindow.c ├── MainWindow.h ├── Tile.c ├── Tile.h └── main.c ├── LICENSE.md ├── README.md ├── demo.c └── tinyGUI ├── tinyGUI.c └── tinyGUI.h /2048/MainWindow.c: -------------------------------------------------------------------------------- 1 | #include "../tinyGUI/tinyGUI.h" 2 | #include "MainWindow.h" 3 | 4 | /* Make the constructors */ 5 | #define FIELD(type, name, val) INIT_FIELD(type, name, val) 6 | #define DEF_FIELD(type, name) DEFINE_FIELD(type, name, ) 7 | #define VIRTUAL_METHOD(classType, type, name, args) INIT_VIRTUAL_METHOD(classType, type, name, args) 8 | 9 | PRIVATE void swapTiles(Tile tiles[][4], int i, int j, int i1, int j1){ 10 | Tile temp = tiles[i][j]; 11 | tiles[i][j] = tiles[i1][j1]; 12 | tiles[i1][j1] = temp; 13 | } 14 | 15 | PRIVATE struct coords findFarthest(MAKE_THIS(MainWindow), Tile tile, int xDirection, int yDirection){ 16 | int nextX = tile->gridXPos, nextY = tile->gridYPos, prevX, prevY; 17 | struct coords nextPos; 18 | 19 | do { 20 | prevX = nextX; prevY = nextY; 21 | nextX = prevX + xDirection; nextY = prevY + yDirection; 22 | } while (nextX < 4 && nextY < 4 && nextX >= 0 && nextY >= 0 && 23 | (!this->tiles[nextY][nextX] || (this->tiles[nextY][nextX] && this->tiles[nextY][nextX]->number == tile->number 24 | && !this->tiles[nextY][nextX]->merged))); 25 | 26 | nextPos.x = prevX; nextPos.y = prevY; 27 | return nextPos; 28 | } 29 | 30 | PRIVATE void addRandomTile(MAKE_THIS(MainWindow)){ SELFREF_INIT; 31 | int x, y, value = rand() % 10 < 9 ? 2 : 4; /* Same condition as it original 2048 */ 32 | while (x = rand() % 4, y = rand() % 4, this->tiles[y][x]); 33 | this->tiles[y][x] = newTile(this->panel->moduleInstance, value, x, y); 34 | 35 | $(this->panel)_addChild((GUIObject)(this->tiles[y][x])); 36 | displayControl((Control)(this->tiles[y][x])); 37 | 38 | SendMessageA(this->tiles[y][x]->handle, WM_PAINT, (WPARAM)NULL, (LPARAM)NULL); /* Force repaint after the object has been created */ 39 | } 40 | 41 | PRIVATE void slideTiles(MAKE_THIS(MainWindow), int xDirection, int yDirection){ SELFREF_INIT; 42 | int traversX[4], traversY[4], i, y, j, x; 43 | struct coords farthestCoords; 44 | BOOL moved = FALSE; 45 | 46 | for (i = 0; i < 4; i++){ 47 | traversX[i] = xDirection < 0 ? i : 3 - i; 48 | traversY[i] = yDirection < 0 ? i : 3 - i; 49 | } 50 | 51 | for (i = 0, y = traversY[i]; i < 4; i++, y = traversY[i]){ 52 | for (j = 0, x = traversX[j]; j < 4; j++, x = traversX[j]){ 53 | if (this->tiles[y][x]){ 54 | farthestCoords = findFarthest(this, this->tiles[y][x], xDirection, yDirection); 55 | 56 | if (farthestCoords.x != x || farthestCoords.y != y){ 57 | if (this->tiles[farthestCoords.y][farthestCoords.x]){ /* We need a merger! */ 58 | deleteTile(this->tiles[farthestCoords.y][farthestCoords.x]); 59 | this->tiles[farthestCoords.y][farthestCoords.x] = NULL; 60 | this->tiles[y][x]->merged = TRUE; 61 | $(this->tiles[y][x])_incNumber(); 62 | } 63 | 64 | $(this->tiles[y][x])_moveTile(farthestCoords.y, farthestCoords.x); 65 | swapTiles(this->tiles, y, x, farthestCoords.y, farthestCoords.x); 66 | moved = TRUE; 67 | } 68 | } 69 | } 70 | } 71 | 72 | if (moved){ 73 | for (i = 0; i < 4; i++) 74 | for (j = 0; j < 4; j++) 75 | if (this->tiles[i][j]) 76 | this->tiles[i][j]->merged = FALSE; 77 | addRandomTile(this); 78 | } 79 | 80 | flushMessageQueue(); 81 | } 82 | 83 | PRIVATE void onKeyDown(GUIObject sender, void *context, EventArgs e){ 84 | MainWindow window = (MainWindow)sender; 85 | 86 | switch (e->wParam){ 87 | case VK_LEFT: 88 | slideTiles(window, -1, 0); 89 | break; 90 | case VK_RIGHT: 91 | slideTiles(window, 1, 0); 92 | break; 93 | case VK_UP: 94 | slideTiles(window, 0, -1); 95 | break; 96 | case VK_DOWN: 97 | slideTiles(window, 0, 1); 98 | break; 99 | } 100 | } 101 | 102 | /* The constructor */ 103 | MainWindow newMainWindow(HINSTANCE hInstance){ SELFREF_INIT; 104 | ALLOC_THIS(MainWindow); 105 | int i, j; 106 | 107 | this->moduleInstance = hInstance; 108 | CLASS_MainWindow; 109 | initWindow((Window)this, hInstance, (char*)windowTitle, windowWidth, windowHeight); 110 | 111 | $(this)_addChild((GUIObject)(this->panel)); 112 | 113 | //$(this->panel)_addChild((GUIObject)this->btn); 114 | 115 | 116 | for (i = 0; i < 4; i++){ 117 | for (j = 0; j < 4; j++) 118 | (this->tiles)[i][j] = NULL; 119 | } 120 | 121 | addRandomTile(this); 122 | addRandomTile(this); 123 | 124 | 125 | 126 | this->panel->styles = WS_CHILD | WS_BORDER | WS_THICKFRAME; 127 | $(this->panel)_setResizable(FALSE); 128 | $(this)_setEvent(WM_KEYDOWN, onKeyDown, NULL, SYNC); 129 | 130 | this->panel->maxWidth = this->panel->width; 131 | this->panel->maxHeight = this->panel->height; 132 | 133 | this->panel->x = 10; 134 | this->panel->y = 10; 135 | 136 | return this; 137 | } 138 | 139 | void deleteMainWindow(MAKE_THIS(MainWindow)){ 140 | this->className = NULL; 141 | freeGUIObjectFields((GUIObject)this); 142 | free(this); 143 | } 144 | 145 | #undef FIELD 146 | #undef DEF_FIELD 147 | #undef VIRTUAL_METHOD -------------------------------------------------------------------------------- /2048/MainWindow.h: -------------------------------------------------------------------------------- 1 | #include "tile.h" 2 | 3 | 4 | /* Coordinates on the board */ 5 | struct coords { 6 | int x, y; 7 | }; 8 | 9 | 10 | MAKE_TYPEDEF(MainWindow); 11 | 12 | 13 | 14 | 15 | 16 | #define CLASS_MainWindow /* : */ CLASS_Window \ 17 | DEF_FIELD(int, nCmdShow); \ 18 | FIELD(Window, panel, newWindow(this->moduleInstance, NULL, BOARD_SIZE, BOARD_SIZE)); \ 19 | FIELD(Button, btn, newButton(this->moduleInstance, "test button", 10, 10, 100, 20)); \ 20 | DEF_FIELD(Tile, tiles[4][4]); 21 | 22 | static const char *windowTitle = "Memory Game"; 23 | static const int windowWidth = 700; 24 | static const int windowHeight = 500; 25 | 26 | 27 | 28 | /* =============================================================================================== */ 29 | 30 | 31 | 32 | /* Make the class */ 33 | #define FIELD(type, name, val) MAKE_FIELD(type, name, val) 34 | #define DEF_FIELD(type, name) MAKE_FIELD(type, name, ) 35 | #define VIRTUAL_METHOD(classType, type, name, args) MAKE_VIRTUAL_METHOD(classType, type, name, args) 36 | 37 | MAKE_CLASS(MainWindow); 38 | 39 | #undef FIELD 40 | #undef DEF_FIELD 41 | #undef VIRTUAL_METHOD 42 | 43 | 44 | /* =============================================================================================== */ 45 | 46 | /* The constructor and destructor */ 47 | MainWindow newMainWindow(HINSTANCE hInstance); 48 | MAKE_DEFAULT_DESTRUCTOR_PROTOTYPE(MainWindow); 49 | -------------------------------------------------------------------------------- /2048/Tile.c: -------------------------------------------------------------------------------- 1 | #include "../tinyGUI/tinyGUI.h" 2 | #include "Tile.h" 3 | 4 | 5 | /* Make the constructors */ 6 | #define FIELD(type, name, val) INIT_FIELD(type, name, val) 7 | #define DEF_FIELD(type, name) DEFINE_FIELD(type, name, ) 8 | #define VIRTUAL_METHOD(classType, type, name, args) INIT_VIRTUAL_METHOD(classType, type, name, args) 9 | 10 | METHOD(Tile, void, moveTile, (MAKE_THIS(Tile), int row, int col)){ SELFREF_INIT; 11 | Sleep(1); 12 | 13 | $(this)_setPos(col * BOARD_SIZE / 4, row * BOARD_SIZE / 4); 14 | this->gridXPos = col; 15 | this->gridYPos = row; 16 | } 17 | 18 | METHOD(Tile, void, incNumber, (MAKE_THIS(Tile))){ SELFREF_INIT; 19 | char title[7]; 20 | this->number *= 2; 21 | sprintf(title, "%d", this->number); 22 | 23 | $(this)_setText(title); 24 | } 25 | 26 | void onPaint(GUIObject tile, void *context, EventArgs e){ SELFREF_INIT; 27 | COLORREF color; 28 | RECT textRect; 29 | HGDIOBJ prevFont; 30 | 31 | textRect.top = 0; 32 | textRect.left = 0; 33 | textRect.right = tile->width - 1; 34 | textRect.bottom = tile->height - 1; 35 | 36 | switch(((Tile)tile)->number){ 37 | case 2: 38 | case 4: 39 | color = RGB(0xEE, 0xE4, 0xDA); 40 | break; 41 | 42 | case 8: 43 | color = RGB(0xF2, 0xB1, 0x79); 44 | break; 45 | 46 | case 16: 47 | color = RGB(0xF5, 0x95, 0x63); 48 | break; 49 | 50 | case 32: 51 | color = RGB(0xF6, 0x7C, 0x5F); 52 | break; 53 | 54 | case 64: 55 | color = RGB(0xF6, 0x5E, 0x3B); 56 | break; 57 | 58 | case 128: 59 | color = RGB(0xED, 0xCF, 0x72); 60 | break; 61 | 62 | case 256: 63 | color = RGB(0xED, 0xCC, 0x61); 64 | break; 65 | 66 | case 512: 67 | color = RGB(0xED, 0xC8, 0x50); 68 | break; 69 | 70 | case 1024: 71 | color = RGB(0xED, 0xC5, 0x3F); 72 | break; 73 | 74 | case 2048: 75 | color = RGB(0xED, 0xC2, 0x2E); 76 | break; 77 | 78 | default: 79 | color = RGB(0x3C, 0x3A, 0x32); 80 | break; 81 | } 82 | 83 | $(((Tile)tile)->bgBrush)_setBrushColor(color); 84 | 85 | $(tile)_drawRect(NULL, ((Tile)tile)->bgBrush, 0, 0, tile->width, tile->height); 86 | 87 | prevFont = SelectObject(tile->paintContext, GetStockObject(DEFAULT_GUI_FONT)); 88 | SetBkMode(tile->paintContext, TRANSPARENT); 89 | DrawTextA(tile->paintContext, tile->text, -1, &textRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); 90 | } 91 | 92 | 93 | /* The constructor */ 94 | Tile newTile(HINSTANCE hInstance, int number, int gridXPos, int gridYPos){ SELFREF_INIT; 95 | ALLOC_THIS(Tile); 96 | char title[7]; 97 | if (gridXPos >= 4 || gridYPos >= 4){ 98 | free(this); 99 | return NULL; 100 | } 101 | 102 | sprintf(title, "%d", number); 103 | 104 | CLASS_Tile; 105 | initLabel((Label)this, hInstance, title, gridXPos * BOARD_SIZE / 4, gridYPos * BOARD_SIZE / 4, BOARD_SIZE / 4, BOARD_SIZE / 4); 106 | this->number = number; 107 | 108 | this->gridXPos = gridXPos; 109 | this->gridYPos = gridYPos; 110 | 111 | this->styles |= WS_BORDER | SS_CENTER | SS_CENTERIMAGE; 112 | 113 | $(this)_setEvent(WM_PAINT, onPaint, NULL, SYNC); 114 | //$(this)_setEvent(WM_CTLCOLORSTATIC, onCtlColorStatic, NULL, SYNC); 115 | 116 | return this; 117 | } 118 | 119 | void deleteTile(MAKE_THIS(Tile)){ 120 | this->className = NULL; 121 | deleteBrush(this->bgBrush); 122 | freeGUIObjectFields((GUIObject)this); 123 | free(this); 124 | } 125 | 126 | #undef FIELD 127 | #undef DEF_FIELD 128 | #undef VIRTUAL_METHOD -------------------------------------------------------------------------------- /2048/Tile.h: -------------------------------------------------------------------------------- 1 | MAKE_TYPEDEF(Tile); 2 | 3 | 4 | 5 | #define BOARD_SIZE 400 6 | 7 | 8 | 9 | #define CLASS_Tile /* : */ CLASS_Label \ 10 | FIELD(int, number, 0); \ 11 | FIELD(int, gridXPos, 0); \ 12 | FIELD(int, gridYPos, 0); \ 13 | FIELD(BOOL, merged, FALSE); \ 14 | FIELD(Brush, bgBrush, newBrush(BS_SOLID, RGB(0xEE, 0xE4, 0xDA), (ULONG_PTR)NULL)); 15 | 16 | METHOD(Tile, void, moveTile, (MAKE_THIS(Tile), int row, int col)); 17 | METHOD(Tile, void, incNumber, (MAKE_THIS(Tile))); 18 | 19 | #define _moveTile(row, col) MAKE_METHOD_ALIAS(Tile, moveTile(CURR_THIS(Tile), row, col)) 20 | #define _incNumber() MAKE_METHOD_ALIAS(Tile, incNumber(CURR_THIS(Tile))) 21 | 22 | 23 | 24 | /* =============================================================================================== */ 25 | 26 | 27 | 28 | /* Make the class */ 29 | #define FIELD(type, name, val) MAKE_FIELD(type, name, val) 30 | #define DEF_FIELD(type, name) MAKE_FIELD(type, name, ) 31 | #define VIRTUAL_METHOD(classType, type, name, args) MAKE_VIRTUAL_METHOD(classType, type, name, args) 32 | 33 | MAKE_CLASS(Tile); 34 | 35 | #undef FIELD 36 | #undef DEF_FIELD 37 | #undef VIRTUAL_METHOD 38 | 39 | 40 | /* =============================================================================================== */ 41 | 42 | /* The constructor and destructor */ 43 | Tile newTile(HINSTANCE hInstance, int number, int gridXPos, int gridYPos); 44 | MAKE_DEFAULT_DESTRUCTOR_PROTOTYPE(Tile); 45 | -------------------------------------------------------------------------------- /2048/main.c: -------------------------------------------------------------------------------- 1 | #include "../tinyGUI/tinyGUI.h" 2 | #include "MainWindow.h" 3 | 4 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR nCmdLine, int nCmdShow){ 5 | srand(GetTickCount()); 6 | displayWindow((Window)newMainWindow(hInstance), nCmdShow); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Maxim Mints 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tinyGUI 2 | 3 | A small and easy-to-use object-oriented Windows GUI library for C 4 | 5 | Currently at version 0.3 (Alpha) 6 | 7 | tinyGUI is a (tiny) object-oriented Windows GUI library for C. It was designed with ease of use being the primary concern. 8 | tinyGUI sports a minimalist design, which contributes to its small size. However, all problems caused by abscence of certain functionality 9 | from this or other versions is mitigated by easy extendibility. 10 | 11 | To use tinyGUI, just add tinyGUI.h and tinyGUI.c to your project, 12 | `#include "tinyGUI/tinyGUI.h"` 13 | and start coding! 14 | 15 | tinyGUI is currently a work-in-progress, it is still missing many widgets and functionality that should make work easier. 16 | A demonstration of some of its current capabilities can be found in demo.c (a small educational application for children) and in the 2048 folder (a minimalist implementation of the infamous 2048 game). Stay tuned! 17 | 18 | P.S. This is the first project I've ever hosted at GitHub, sorry if I've messed up somehow! 19 | 20 | # Object - oriented system 21 | 22 | tinyGUI uses a custom object-oriented system called tinyObject, designed to create objects that are as easy to use 23 | as to extend. 24 | 25 | Check out [tinyObject's GitHub page](https://github.com/Mints97/tinyObject) for its syntaxis. 26 | 27 | 28 | 29 | 30 | 31 | # Library structure 32 | 33 | tinyGUI has, so far, the following class inheritance hierarchy: 34 | 35 | - Object 36 | - EventArgs 37 | - MouseEventArgs 38 | - Pen 39 | - Brush 40 | - GUIObject 41 | - Window 42 | - Control 43 | - Button 44 | - TextBox 45 | - Label 46 | 47 | A description of every class follows. 48 | 49 | 50 | ## Class Object 51 | 52 | Inheritance: base class 53 | 54 | The main base class in tinyGUI, all the other classes directly or indirectly inherit from it. 55 | 56 | ### Fields 57 | 58 | ```C 59 | enum _objectType type; /* Identifies the object type */ 60 | CRITICAL_SECTION criticalSection; /* The critical section for synchronising access to the object */ 61 | ``` 62 | 63 | *Note: the second parameter should be initialized as a critical section (by calling InitializeCriticalSection(object->criticalSection)), which is done automatically in its constructor and in the constructors of all its derived classes. 64 | It can then be used anywhere in the code to synchronize access to an object. You can use the following utility macros for this:* 65 | ```C 66 | startSync(object) 67 | endSync(object) 68 | ``` 69 | 70 | ### Constructors 71 | 72 | These are all default. 73 | 74 | 75 | ## Class EventArgs 76 | 77 | Inheritance: base class, inherits from Object 78 | 79 | Instances of this type are passed to event handler callbacks. They contain information about the Windows message 80 | that triggered the event, along with the additional Windows parameters. 81 | 82 | ### Fields 83 | 84 | ```C 85 | UINT message; /* The Windows message */ 86 | WPARAM wParam; /* The first Windows message additional parameter */ 87 | LPARAM lParam; /* The second Windows message additional parameter */ 88 | ``` 89 | 90 | ### Methods 91 | 92 | ```C 93 | /* Virtual method. Updates the field values of the object corresponding to the parameters. Actually points to the class initializer 94 | of it type */ 95 | void updateValue(UINT message, WPARAM wParam, LPARAM lParam); 96 | ``` 97 | 98 | ### Constructors 99 | 100 | ```C 101 | /* Both constructors set the field values of the object corresponding to their parameters */ 102 | void initEventArgs(EventArgs thisObject, UINT message, WPARAM wParam, LPARAM lParam); 103 | EventArgs newEventArgs(UINT message, WPARAM wParam, LPARAM lParam); 104 | ``` 105 | 106 | 107 | ## Class MouseEventArgs 108 | 109 | Inheritance: inherits from EventArgs 110 | 111 | Instances of this type are passed to event handler callbacks when a mouse message is recieved. 112 | 113 | ### Fields 114 | 115 | ```C 116 | int cursorX; /* The X position of the cursor at the time the message was sent */ 117 | int cursorY; /* The Y position of the cursor at the time the message was sent */ 118 | ``` 119 | 120 | ### Constructors 121 | 122 | ```C 123 | /* Both constructors set the field values of the object corresponding to their parameters */ 124 | void initMouseEventArgs(MouseEventArgs thisObject, UINT message, WPARAM wParam, LPARAM lParam); 125 | MouseEventArgs newMouseEventArgs(UINT message, WPARAM wParam, LPARAM lParam); 126 | ``` 127 | 128 | 129 | ## Class GUIObject 130 | 131 | Inheritance: base class, inherits from Object 132 | 133 | This is the main widget type; all the widgets directly or indirectly inherit from it. 134 | 135 | ### Fields 136 | 137 | ```C 138 | LONG_PTR origProcPtr; /* The pointer to the original window procedure */ 139 | 140 | HWND handle; /* The handle to the window/control; initialized with a call to CreateWindowEx */ 141 | HINSTANCE moduleInstance; /* The current module instance */ 142 | PAINTSTRUCT paintData; /* A structure with data about the GUIObject's painting */ 143 | HDC paintContext; /* A handle to the window's current paint context. Initialized internally on receiving a WM_PAINT message */ 144 | HDC offscreenPaintContext; /* A handle to the window's offscreen paint context. Used in double-buffering drawing optimizations */ 145 | HBITMAP offscreenBitmap; /* A handle to the window's offscreen bitmap instance. Used in double-buffering drawing optimizations */ 146 | BOOL customEraseBG; /* If this is TRUE, default processing for the WM_ERASEBKGND doesn't occur so custom processing in an event handler 147 | can be used. Useful for preventing flickering. */ 148 | 149 | char *className; /* The name of the window/control's WinAPI "class" */ 150 | HMENU ID; /* The child-window/control identifier */ 151 | DWORD styles; /* The window/control styles (WinAPI predefined macro values) */ 152 | DWORD exStyles; /* The window/control extended styles (WinAPI predefined macro values) */ 153 | 154 | struct _event *events; /* An array of the GUIObject's events */ 155 | unsigned int numEvents; /* The number of events registered for the GUIObject */ 156 | 157 | char *text; /* The window/control text */ 158 | 159 | int width; /* The window/control width, pixels */ 160 | int height; /* The window/control height, pixels */ 161 | 162 | int minWidth; /* The window/control minimum width, pixels */ 163 | int minHeight; /* The window/control minimum height, pixels */ 164 | 165 | int maxWidth; /* The window/control maximum width, pixels */ 166 | int maxHeight; /* The window/control maximum height, pixels */ 167 | 168 | int realWidth; /* The window/control width used in anchor calculations. Not affected by min and max settings */ 169 | int realHeight; /* The window/control height used in anchor calculations. Not affected by min and max settings */ 170 | 171 | int x; /* The window/control x position (from left), pixels */ 172 | int y; /* The window/control y position (from top), pixels */ 173 | 174 | int realX; /* used in anchor calculations. Not affected by min and max settings */ 175 | int realY; /* used in anchor calculations. Not affected by min and max settings */ 176 | 177 | BOOL enabled; /* The GUIObject's enabled state */ 178 | ``` 179 | 180 | ### Methods 181 | 182 | *Note: From here on, all BOOL methods return TRUE on success and FALSE on failure. I plan to replace this in the 183 | future with an error-handling mechanism.* 184 | 185 | ```C 186 | /* Adds a child object to a GUIObject. Child objects are displayed along with their parent */ 187 | BOOL addChild(GUIObject child); 188 | 189 | /* Removes a child object from a GUIObject */ 190 | BOOL removeChild(GUIObject child); 191 | 192 | /* Sets an event for a GUIObject by a Windows message. This method returns a unique event identifier for the object 193 | which can be later used in other event functions. 194 | The message parameter is a Windows message recieved through the window proc. It can also be a WM_COMMAND type message, like BN_CLICKED. 195 | Standard window messages apply for all controls, as they are subclassed by default. 196 | The callback parameter is a pointer to the callback event handler function. It should have the following declaration: 197 | void callback(GUIObject sender, void *context, EventArgs e) 198 | The context parameter is a pointer that is passed to the callback on every invocation. This is done to handle events 199 | in a thread-safe way. 200 | The mode parameter is the synchronization mode, it should be SYNC for synchronous events and ASYNC for asynchronous events. */ 201 | int setEvent(DWORD message, void(*callback)(GUIObject, void*, EventArgs), 202 | void *context, enum _syncMode mode); 203 | /* Note: if the sender of an event is a type derived from GUIObject, or the event's arguments are of a type derived from EventArgs, 204 | it is preferred to cast them inside the function, along with the context parameter. However, you can also declare the callback with 205 | the actual expected types, like this, for example: 206 | void onClick(Button sender, Window mainWindow, MouseEventArgs e) 207 | and cast its pointer to the appropriate callback type (you can use the Callback typedef): 208 | $(button)->setOnClick((Callback)onClick, (void*)mainWindow, SYNC); 209 | Beware! This may be easier for you to use, but this technically causes undefined behaviour, according to the C standard, as this means 210 | that tinyGUI will call a function pointer typecasted from an incompatible function pointer type. However, in practice, in most modern 211 | compilers on most modern systems, this is nearly always safe. */ 212 | 213 | /* Sets a condition for an event of a GUIObject. The condition parameter is a pointer to a BOOL variable, the value of which should 214 | serve as a condition for the event handling referred to by eventID. I.e. if the value of this variable is TRUE, the event handling 215 | occurs, if it is FALSE, the handling doesn't occur */ 216 | BOOL setEventCondition(int eventID, BOOL *condition); 217 | 218 | /* Sets an interrupt state for an event of a GUIObject. If the interrupt parameter is TRUE, the default processing for the message 219 | referred to by eventID doesn't occur. Otherwise, it occurs normally. */ 220 | BOOL setEventInterrupt(int eventID, BOOL interrupt); 221 | 222 | /* Changes an event's enabled state for a GUIObject. If enabled is set to FALSE, the event handling referred to by eventID doesn't happen. 223 | It happens normally otherwise. */ 224 | BOOL setEventEnabled(int eventID, BOOL enabled); 225 | 226 | /* Sets a WM_LBUTTONUP event for an (enabled) GUIObject */ 227 | int setOnClick(void(*callback)(GUIObject, void*, EventArgs), void *context, enum _syncMode mode); 228 | 229 | /* Virtual method. Moves a GUIObject to a new location specified by x and y */ 230 | BOOL setPos(int x, int y); 231 | 232 | /* Resizes a GUIObject to a new size specified by width and height */ 233 | BOOL setSize(int width, int height); 234 | 235 | /* Sets a GUIObject's minimum size to a new value specified by minWidth and minHeight */ 236 | BOOL setMinSize(int minWidth, int minHeight); 237 | 238 | /* Sets a GUIObject's maximum size to a new value specified by maxWidth and maxHeight */ 239 | BOOL setMaxSize(int maxWidth, int maxHeight); 240 | 241 | /* Sets a new text for a GUIObject */ 242 | BOOL setText(char *text); 243 | 244 | /* Sets a GUIObject's enabled state */ 245 | BOOL setEnabled(BOOL enabled); 246 | 247 | /* Draws a line in a GUIObject from the point specified by x1, y1 to the point specified by x2, y2 with a pen specified by the 248 | parameter pen. If pen is NULL, a null pen is used. Drawing is double-buffered */ 249 | BOOL drawLine(Pen pen, int x1, int y1, int x2, int y2); 250 | 251 | /* Draws an arc in a GUIObject inside the bounds of a rectangle specified by boundX1, boundY1, boundX2, and boundY2 252 | (the upper left and the bottom right corners of the rectangle, respectively), from a point specified by x1 and y1 to 253 | the point specified by x2, y2 with a pen specified by the parameter pen. If pen is NULL, a null pen is used. 254 | Drawing is double-buffered */ 255 | BOOL drawArc(Pen pen, int boundX1, int boundY1, int boundX2, int boundY2, int x1, int y1, int x2, int y2); 256 | 257 | /* Draws a rectangle in a GUIObject specified by boundX1, boundY1, boundX2, and boundY2 (the upper left and the bottom right corners of the 258 | rectangle, respectively) with a pen specified by the parameter pen and fills it with the brush specified by the parameter brush. If pen is NULL, a null pen is used. If brush is NULL, a hollow brush is used. Drawing is double-buffered */ 259 | BOOL drawRect(Pen pen, Brush brush, int boundX1, int boundY1, int boundX2, int boundY2); 260 | 261 | /* Draws a rounded rectangle in a GUIObject specified by boundX1, boundY1, boundX2, and boundY2 (the upper left and the bottom right 262 | corners of the rectangle, respectively) with an ellipse of width ellipseWidth and height ellipseHeight being used to draw the 263 | rounded edges, with a pen specified by the parameter pen, and fills it with the brush specified by the parameter brush. If pen is NULL, a null pen is used. If brush is NULL, a hollow brush is used. Drawing is double-buffered */ 264 | BOOL drawRoundedRect(Pen pen, Brush brush, int boundX1, int boundY1, 265 | int boundX2, int boundY2, int ellipseWidth, int ellipseHeight); 266 | 267 | /* Draws an ellipse in a GUIObject inside the bounds of a rectangle specified by boundX1, boundY1, boundX2, and boundY2 268 | (the upper left and the bottom right corners of the rectangle, respectively) with a pen specified by the parameter pen 269 | and fills it with the brush specified by the parameter brush. If pen is NULL, a null pen is used. If brush is NULL, 270 | a hollow brush is used. Drawing is double-buffered */ 271 | BOOL drawEllipse(Pen pen, Brush brush, int boundX1, int boundY1, int boundX2, int boundY2); 272 | 273 | /* Draws a polygon in a GUIObject between a set of points (number given by numPoints) specified by the coords array, which should 274 | contain the points in the format {x1, y1, x2, y2, x3, y3, ... xn, yn}, with a pen specified by the parameter pen 275 | and fills it with the brush specified by the parameter brush. If pen is NULL, a null pen is used. If brush is NULL, 276 | a hollow brush is used. Drawing is double-buffered */ 277 | BOOL drawPolygon(Pen pen, Brush brush, int numPoints, LONG *coords); 278 | ``` 279 | 280 | ### Constructors 281 | 282 | ```C 283 | /* The parameter instance is the module instance of your executable, it can be obtained from the 284 | hInstance parameter of the WinMain function. Caption specifies the GUIObject's text, width and height specify its initial size */ 285 | void initGUIObject(GUIObject thisObject, HINSTANCE instance, char *text, int width, int height); 286 | GUIObject newGUIObject(HINSTANCE instance, char *text, int width, int height); 287 | ``` 288 | 289 | 290 | 291 | ## Class Window 292 | 293 | Inheritance: inherits from GUIObject 294 | 295 | This class represents a window. 296 | 297 | ### Fields 298 | 299 | ```C 300 | int clientWidth; /* The client area's width, in pixels */ 301 | int clientHeight; /* The client area's height, in pixels */ 302 | 303 | BOOL resizable; /* The window's resizable state */ 304 | BOOL maximizeEnabled; /* The window's maximize box state */ 305 | ``` 306 | 307 | ### Methods 308 | 309 | ```C 310 | /* Changes a window's resizable style */ 311 | BOOL setResizable(BOOL resizable); 312 | 313 | /* Enables or disables a window's maximize box */ 314 | BOOL enableMaximize(BOOL maximize); 315 | ``` 316 | 317 | ### Constructors 318 | 319 | ```C 320 | /* The parameter instance is the module instance of your executable, it can be obtained from the 321 | hInstance parameter of the WinMain function. Caption specifies the window's text, width and height specify its initial size */ 322 | void initWindow(Window thisObject, HINSTANCE instance, char *text, int width, int height); 323 | Window newWindow(HINSTANCE instance, char *text, int width, int height); 324 | ``` 325 | 326 | 327 | ## Class Control 328 | 329 | Inheritance: base class, inherits from GUIObject 330 | 331 | This is the base class for all controls. 332 | 333 | ### Fields 334 | 335 | ```C 336 | int minX; /* The minimum x position of the control (distance from parent's left edge), pixels */ 337 | int minY; /* The minimum y position of the control (distance from parent's top edge), pixels */ 338 | 339 | int maxX; /* The maximum x position of the control (distance from parent's left edge), pixels */ 340 | int maxY; /* The maximum y position of the control (distance from parent's top edge), pixels */ 341 | 342 | short anchor; /* The anchor settings for the control. It can be a bitwise addition (OR) of the following values: 343 | ANCHOR_LEFT (0xF000), ANCHOR_RIGHT (0x000F), ANCHOR_TOP (0x0F00), ANCHOR_BOTTOM (0x00F0). 344 | If an anchor is used, the control is moved or resized so that its borders remain at a constant 345 | distance from the respective edges of its parent (limited by minWidth, minHeight, maxWidth, maxHeight, 346 | minX, minY, maxX and maxY settings for the control). If neither of the two anchors for an orientation 347 | (horizontal or vertical) are specified, the control is anchored to the center of its parent in this orientation. */ 348 | ``` 349 | 350 | ### Methods 351 | 352 | ```C 353 | /* Sets a control's minimum position to a new value specified by minX and minY */ 354 | BOOL setMinPos(int minX, int minY); 355 | 356 | /* Sets a control's maximum position to a new value specified by maxX and maxY */ 357 | BOOL setMaxPos(int maxX, int maxY); 358 | ``` 359 | 360 | ### Constructors 361 | 362 | ```C 363 | /* The parameter instance is the module instance of your executable, it can be obtained from the 364 | hInstance parameter of the WinMain function. Caption specifies the control's text, width and height specify its initial size, 365 | x and y specify its initial position */ 366 | void initControl(Control thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height); 367 | Control newControl(HINSTANCE instance, char *text, int x, int y, int width, int height); 368 | ``` 369 | 370 | 371 | 372 | ## Class Button 373 | 374 | Inheritance: inherits from Control 375 | 376 | This class represents a button. 377 | 378 | ### Constructors 379 | 380 | ```C 381 | /* The parameter instance is the module instance of your executable, it can be obtained from the 382 | hInstance parameter of the WinMain function. Caption specifies the button's text, width and height specify its initial size, 383 | x and y specify its initial position */ 384 | void initButton(Button thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height); 385 | Button newButton(HINSTANCE instance, char *text, int x, int y, int width, int height); 386 | ``` 387 | 388 | ## Class TextBox 389 | 390 | Inheritance: inherits from Control 391 | 392 | This class represents a textbox (text input field). 393 | 394 | ### Fields 395 | 396 | ```C 397 | BOOL multiline; /* The textbox's multiline style */ 398 | BOOL numOnly; /* The textbox's number only style - if it accepts only numbers or not */ 399 | ``` 400 | 401 | ### Methods 402 | 403 | ```C 404 | /* Sets the text input mode for a textbox to number-only or to not number-only */ 405 | BOOL setNumOnly(BOOL numOnly); 406 | ``` 407 | 408 | ### Constructors 409 | 410 | ```C 411 | /* The parameter instance is the module instance of your executable, it can be obtained from the 412 | hInstance parameter of the WinMain function. Caption specifies the textbox's text, width and height specify its initial size, 413 | x and y specify its initial position. The parameter multiline specifies the textbox's multiline style and can have the values 414 | MULTILINE and SINGLELINE */ 415 | void initTextBox(TextBox thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height, enum _textboxtype multiline); 416 | TextBox newTextBox(HINSTANCE instance, char *text, int x, int y, int width, int height, enum _textboxtype multiline); 417 | ``` 418 | 419 | ## Class Label 420 | 421 | Inheritance: inherits from Control 422 | 423 | This class represents a label (static text field). 424 | 425 | ### Constructors 426 | 427 | ```C 428 | /* The parameter instance is the module instance of your executable, it can be obtained from the 429 | hInstance parameter of the WinMain function. Caption specifies the label's text, width and height specify its initial size, 430 | x and y specify its initial position */ 431 | void initLabel(Label thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height); 432 | Label newLabel(HINSTANCE instance, char *text, int x, int y, int width, int height); 433 | ``` 434 | 435 | 436 | ## Class Pen 437 | 438 | Inheritance: inherits from Object 439 | 440 | This class represents a logical pen that can be used to draw lines. 441 | 442 | ### Fields 443 | 444 | ```C 445 | HPEN handle; /* The pen's handle */ 446 | int penStyle; /* The pen's style (WinAPI predefined macro values) */ 447 | int width; /* The pen's width/thickness */ 448 | COLORREF *color; /* The pen's color in RGB (can be defined with the RGB(r, g, b) WinAPI macro) */ 449 | ``` 450 | 451 | ### Constructors 452 | 453 | ```C 454 | /* Sets the field values to the values of the respective parameters */ 455 | void initPen(Pen thisObject, int penStyle, int width, COLORREF color); 456 | Pen newPen(int penStyle, int width, COLORREF color); 457 | /* Note: these constructors are deprecated as they basically evaluate to a call to WinAPI function CreatePen with the same parameters. They are to be replaced with more useful methods in the next versions. */ 458 | ``` 459 | 460 | 461 | ## Class Brush 462 | 463 | Inheritance: inherits from Object 464 | 465 | This class represents a logical brush that can be used to fill areas. 466 | 467 | ### Fields 468 | 469 | ```C 470 | HBRUSH *handle; /* The brush's handle */ 471 | UINT *brushStyle; /* The brush's style (WinAPI predefined macro values) */ 472 | COLORREF *color; /* The brush's color in RGB (can be defined with the RGB(r, g, b) WinAPI macro) */ 473 | ULONG_PTR *hatch; /* Either a predefined macro value of a WinAPI hatch style, or a handle to a bitmap with a pattern */ 474 | ``` 475 | 476 | ### Constructors 477 | 478 | ```C 479 | /* Sets the field values to the values of the respective parameters */ 480 | void initBrush(Brush thisObject, UINT brushStyle, COLORREF color, ULONG_PTR hatch); 481 | Brush newBrush(UINT brushStyle, COLORREF color, ULONG_PTR hatch); 482 | /* Note: these constructors are deprecated as they basically evaluate to a call to WinAPI function CreateBrushIndirect with the same parameters. They are to be replaced with more useful methods in the next versions. */ 483 | ``` 484 | 485 | 486 | 487 | ## Static functions 488 | 489 | ```C 490 | /* Free the fields of a GUIObject */ 491 | void freeGUIObjectFields(GUIObject object); 492 | 493 | /* Flush the current thread's message queue */ 494 | void flushMessageQueue(); 495 | 496 | /* Display a window with the application's command line settings */ 497 | BOOL displayWindow(Window mainWindow, int nCmdShow); 498 | 499 | /* Display a control on its parent window */ 500 | BOOL displayControl(Control control); 501 | ``` 502 | 503 | ## Other 504 | 505 | ```C 506 | /* An event object */ 507 | struct _event { 508 | void (*eventFunction)(GUIObject, void*, EventArgs); /* The callback */ 509 | enum _syncMode mode; /* The sync mode */ 510 | UINT message; /* The message ID */ 511 | GUIObject sender; /* The sender object */ 512 | void *context; /* A pointer to data that gets sent on every event */ 513 | EventArgs args; /* The event args */ 514 | BOOL *condition; /* A pointer to a variable that determines if the event should be handled */ 515 | BOOL interrupt; /* If this is set to TRUE, the default handling for the event doesn't occur */ 516 | BOOL enabled; /* The event's enabled status */ 517 | }; 518 | 519 | /* An event handler callback type */ 520 | typedef void(*Callback)(GUIObject, void*, EventArgs); 521 | 522 | /* Macros that expand to initPen (first 3) or initBrush (last 3) on a Pen or Brush object respectively with all parameters 523 | except for one being same as properties of the object */ 524 | _setPenStyle(penStyle) 525 | _setPenWidth(width) 526 | _setPenColor(color) 527 | _setBrushStyle(brushStyle) 528 | _setBrushColor(color) 529 | _setBrushHatch(hatch) 530 | 531 | ``` 532 | 533 | 534 | That is all so far! 535 | -------------------------------------------------------------------------------- /demo.c: -------------------------------------------------------------------------------- 1 | #include "tinyGUI/tinyGUI.h" /* Add tinyGUI to the project */ 2 | 3 | #define NUMBUTTONS 10 /* The number of the number buttons */ 4 | 5 | /* The number buttons' onClick event handler */ 6 | void numBtnOnClick(Button sender, void *context, MouseEventArgs e){ SELFREF_INIT; 7 | TextBox textbox = (TextBox)context; 8 | int newTextLen; 9 | char *newText; 10 | 11 | newTextLen = strlen(textbox->text) + 1; 12 | newText = (char*)malloc(newTextLen + 1); /* Allocate memory for the new text */ 13 | if (!newText) 14 | MessageBoxA(NULL, "Out of memory!", "Error!", MB_OK); 15 | else { 16 | strcpy_s(newText, newTextLen + 1, textbox->text); 17 | strcat_s(newText, newTextLen + 1, sender->text); /* Concatenate the pressed button's text to the textbox's text */ 18 | newText[newTextLen] = '\0'; 19 | $(textbox)_setText(newText); /* Set the textbox's new text */ 20 | free(newText); /* Free the new text. This is perfectly safe, the textbox's text is now stored elsewhere in memory */ 21 | } 22 | } 23 | 24 | /* Sets a new random task in a label */ 25 | int setTask(Label task){ SELFREF_INIT; 26 | int numA, numB; 27 | char newText[27]; 28 | 29 | srand((unsigned int)GetTickCount()); /* Generate 2 random numbers from 0 to 12 */ 30 | numA = rand() % 13; 31 | numB = rand() % 13; 32 | 33 | sprintf_s(newText, 27, "%d MULTIPLY BY %d EQUALS", numA, numB); 34 | 35 | $(task)_setText(newText); /* Set the label's text to the new task */ 36 | return numA * numB; /* return the task's solution */ 37 | } 38 | 39 | /* The "check answer" button's onClick asynchronous event handler */ 40 | void checkAnsBtnOnClick(Button checkAnsButton, void **context, MouseEventArgs e){ SELFREF_INIT; 41 | /* Get the objects needed by the event handler from the context */ 42 | Label task = (Label)context[0], header = (Label)context[1]; 43 | TextBox textbox = (TextBox)context[2]; 44 | int *answer = (int*)context[3], answerInput = 0; 45 | 46 | sscanf_s(textbox->text, "%d", &answerInput, 1); /* Get the numeric value of the answer input */ 47 | 48 | startSync(header); /* Synchronize access to the header object */ 49 | $(header)_setText((answerInput == *answer) ? "GOOD JOB!" : "WRONG ANSWER!"); /* Display the result */ 50 | startSync(checkAnsButton); $(checkAnsButton)_setEnabled(FALSE); endSync(checkAnsButton); /* Disable the "check answer" button (synchronized) */ 51 | Sleep(2000); /* Pause for 2000 milliseconds */ 52 | $(header)_setText("Now can you calculate this?"); /* Pause is over, start new task */ 53 | endSync(header); 54 | 55 | startSync(checkAnsButton); $(checkAnsButton)_setEnabled(TRUE); endSync(checkAnsButton); /* Enable the "check answer" button (synchronized) */ 56 | startSync(textbox); $(textbox)_setText(""); endSync(textbox); /* Clear the textbox (synchronized) */ 57 | startSync(task); *answer = setTask(task); endSync(task); /* Set a new task and save its answer to the context (synchronized) */ 58 | } 59 | 60 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ SELFREF_INIT; 61 | /* Create the objects */ 62 | Window window = newWindow(hInstance, "Learn the Multiplication Table!", 440, 200); /* Create the window */ 63 | Label header = newLabel(hInstance, "Can you calculate this?", 10, 10, 400, 20), /* Create the header label */ 64 | task = newLabel(hInstance, "", 10, 35, 220, 20); /* Create the task label */ 65 | TextBox ansInput = newTextBox(hInstance, "", 240, 32, 102, 25, SINGLELINE); /* Create the answer textbox (a single-line textbox) */ 66 | Button numberButtons[NUMBUTTONS], /* The number buttons */ 67 | checkAnsButton = newButton(hInstance, "Check answer", 10, 110, 400, 25); /* Create the "check answer" button */ 68 | 69 | char *buttonTexts[NUMBUTTONS] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; 70 | int i, firstTaskAnswer; 71 | void *buttonContext[4] = {(void*)task, (void*)header, (void*)ansInput, NULL}; /* Create the context for the "check answer" button's onClick event */ 72 | 73 | firstTaskAnswer = setTask(task); /* Get the answer to the first random task */ 74 | buttonContext[3] = (void*)&firstTaskAnswer; /* And add it to the context for the "check answer" button onClick event */ 75 | 76 | $(window)_setResizable(FALSE); /* Disable resizing the window */ $(window)_enableMaximize(FALSE); /* Disable its maximize box */ 77 | $(ansInput)_setNumOnly(TRUE); /* Set the textbox to accept only numbers */ 78 | $(checkAnsButton)_setOnClick((Callback)&checkAnsBtnOnClick, (void*)buttonContext, ASYNC); /* Set an asynchronous onClick event 79 | for the "check answer" button */ 80 | for (i = 0; i < NUMBUTTONS; i++){ /* Add the 10 number buttons */ 81 | numberButtons[i] = newButton(hInstance, buttonTexts[i], 10 + i * 40, 60, 40, 40); /* Create a button */ 82 | $(numberButtons[i])_setOnClick((Callback)&numBtnOnClick, (void*)ansInput, SYNC); /* Set a synchronous onClick event for the button */ 83 | $(window)_addChild((GUIObject)numberButtons[i]); /* Add the button to the window */ 84 | } 85 | 86 | $(window)_addChild((GUIObject)header); /* Add the header label to the window */ 87 | _(window)_addChild((GUIObject)task); /* Add the task label to the window */ 88 | _(window)_addChild((GUIObject)ansInput); /* Add the answer input textbox to the window */ 89 | _(window)_addChild((GUIObject)checkAnsButton); /* Add the "check answer" button to the window */ 90 | 91 | displayWindow(window, nCmdShow); /* Display the created window */ 92 | 93 | for (i = 0; i < NUMBUTTONS; i++) /* The window has been closed, destroy the created objects */ 94 | deleteButton(numberButtons[i]); 95 | deleteButton(checkAnsButton); deleteTextBox(ansInput); deleteLabel(task); deleteLabel(header); deleteWindow(window); 96 | 97 | return 0; 98 | } -------------------------------------------------------------------------------- /tinyGUI/tinyGUI.c: -------------------------------------------------------------------------------- 1 | #include "tinyGUI.h" 2 | 3 | /* Get ownership of a mutex. Used in thread synchronization for accessing shared global context */ 4 | static HANDLE getMutexOwnership(char *mutexName){ 5 | HANDLE mutex = NULL; 6 | DWORD mutexWaitResult; 7 | mutex = CreateMutexA(NULL, FALSE, mutexName); /* Create named mutex or get a handle to it if it exists */ 8 | 9 | if (!mutex) 10 | return NULL; 11 | 12 | mutexWaitResult = WaitForSingleObject(mutex, 0); /* Check mutex status */ 13 | 14 | if (mutexWaitResult == WAIT_TIMEOUT){ /* Someone owns the mutex, wait till it's released */ 15 | mutexWaitResult = WaitForSingleObject(mutex, INFINITE); 16 | if (mutexWaitResult == WAIT_OBJECT_0 || mutexWaitResult == WAIT_ABANDONED) 17 | return mutex; 18 | else 19 | return NULL; 20 | } else if (mutexWaitResult == WAIT_OBJECT_0 || mutexWaitResult == WAIT_ABANDONED) /* Mutex was not owned, we acquired it */ 21 | return mutex; 22 | else 23 | return NULL; 24 | } 25 | 26 | 27 | /* The window proc prototype */ 28 | static LRESULT CALLBACK windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 29 | 30 | 31 | /* Register a window's WinAPI "class" */ 32 | static BOOL registerClass(HINSTANCE hInstance, char *className, WNDPROC procName){ 33 | WNDCLASSEXA wc; 34 | BOOL result; 35 | 36 | wc.cbSize = sizeof(WNDCLASSEXA); 37 | wc.style = 0; 38 | wc.lpfnWndProc = procName; 39 | wc.cbClsExtra = 0; 40 | wc.cbWndExtra = 0; 41 | wc.hInstance = hInstance; 42 | wc.hIcon = LoadIcon(GetModuleHandle(NULL), NULL); 43 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 44 | wc.hbrBackground = (HBRUSH)COLOR_WINDOW; 45 | wc.lpszMenuName = NULL; 46 | wc.lpszClassName = className; 47 | wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), NULL, IMAGE_ICON, 16, 16, 0); 48 | 49 | result = RegisterClassExA(&wc); 50 | 51 | return result; 52 | } 53 | 54 | 55 | 56 | 57 | /* Make the constructors */ 58 | #define FIELD(type, name, val) INIT_FIELD(type, name, val) 59 | #define DEF_FIELD(type, name) DEFINE_FIELD(type, name, ) 60 | #define VIRTUAL_METHOD(classType, type, name, args) INIT_VIRTUAL_METHOD(classType, type, name, args) 61 | 62 | 63 | /* Class Object */ 64 | /* The Constructor*/ 65 | void initObject(Object thisObject){ 66 | if (!thisObject) 67 | return; 68 | 69 | CLASS_Object; 70 | 71 | /* Try to delete critical section just in case */ 72 | if (thisObject->criticalSectionInitialized) 73 | DeleteCriticalSection(&(thisObject->criticalSection)); 74 | 75 | InitializeCriticalSection(&(thisObject->criticalSection)); 76 | thisObject->criticalSectionInitialized = TRUE; 77 | } 78 | 79 | Object newObject(){ 80 | Object thisObject = (Object)malloc(sizeof(val_Object)); 81 | 82 | if (!thisObject) 83 | return NULL; 84 | 85 | initObject(thisObject); 86 | 87 | return thisObject; 88 | } 89 | 90 | void deleteObject(Object thisObject){ 91 | DeleteCriticalSection(&(thisObject->criticalSection)); 92 | free(thisObject); 93 | } 94 | 95 | 96 | 97 | 98 | 99 | /* Class GUIObject */ 100 | 101 | BOOL GUIObject_addChild(GUIObject object, GUIObject child){ 102 | GUIObject *newChildrenPointer; 103 | unsigned int i; 104 | 105 | if (!object) 106 | return FALSE; 107 | 108 | child->parent = object; 109 | 110 | if (object->children != NULL) 111 | for (i = 0; i < object->numChildren; i++) 112 | if ((object->children)[i] == NULL){ 113 | (object->children)[i] = child; 114 | return TRUE; 115 | } 116 | 117 | 118 | (object->numChildren)++; 119 | newChildrenPointer = (GUIObject*)realloc(object->children, object->numChildren * sizeof(GUIObject)); 120 | 121 | if (!newChildrenPointer) 122 | return FALSE; 123 | 124 | object->children = newChildrenPointer; 125 | (object->children)[object->numChildren - 1] = child; 126 | 127 | return TRUE; 128 | } 129 | 130 | /* Removes a child object from a GUIObject */ 131 | BOOL GUIObject_removeChild(GUIObject object, GUIObject child){ 132 | unsigned int i; 133 | 134 | if (!object) 135 | return FALSE; 136 | 137 | child->parent = NULL; 138 | 139 | if (object->children != NULL) 140 | for (i = 0; i < object->numChildren; i++) 141 | if ((object->children)[i] == child){ 142 | (object->children)[i] = NULL; 143 | return TRUE; 144 | } 145 | 146 | return FALSE; 147 | } 148 | 149 | /* Sets an event for a GUIObject by a Windows message */ 150 | int GUIObject_setEvent(GUIObject object, DWORD message, void(*callback)(GUIObject, void*, EventArgs), 151 | void *context, enum _syncMode mode){ 152 | struct _event *tempReallocPointer; 153 | unsigned int i; 154 | 155 | if (!object) 156 | return -1; 157 | 158 | for (i = 0; i < object->numEvents; i++) 159 | if ((object->events)[i].message == message){ 160 | (object->events)[i].eventFunction = callback; (object->events)[i].mode = mode; 161 | (object->events)[i].sender = object; (object->events)[i].context = context; 162 | (object->events)[i].condition = NULL; (object->events)[i].interrupt = FALSE; 163 | (object->events)[i].enabled = TRUE; 164 | return (int)i; 165 | } 166 | 167 | (object->numEvents)++; 168 | tempReallocPointer = (struct _event*)realloc(object->events, object->numEvents * sizeof(struct _event)); 169 | if (!tempReallocPointer) 170 | return -1; 171 | 172 | object->events = tempReallocPointer; 173 | switch(message){ 174 | case WM_MOUSEMOVE: case WM_MOUSEHOVER: case WM_MOUSELEAVE: case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: 175 | case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDBLCLK: 176 | (object->events)[i].args = (EventArgs)newMouseEventArgs(message, 0, 0); 177 | break; 178 | default: 179 | (object->events)[i].args = newEventArgs(message, 0, 0); 180 | } 181 | 182 | (object->events)[object->numEvents - 1].eventFunction = callback; (object->events)[object->numEvents - 1].mode = mode; 183 | (object->events)[object->numEvents - 1].message = message; (object->events)[object->numEvents - 1].sender = object; 184 | (object->events)[object->numEvents - 1].context = context; (object->events)[object->numEvents - 1].condition = NULL; 185 | (object->events)[object->numEvents - 1].interrupt = FALSE; (object->events)[object->numEvents - 1].enabled = TRUE; 186 | 187 | return (int)(object->numEvents - 1); 188 | } 189 | 190 | /* Sets a condition for an event of a GUIObject */ 191 | BOOL GUIObject_setEventCondition(GUIObject object, int eventID, BOOL *condition){ 192 | if (!object || eventID < 0 || (UINT)eventID >= object->numEvents) 193 | return FALSE; 194 | 195 | (object->events)[eventID].condition = condition; 196 | return TRUE; 197 | } 198 | 199 | /* Sets an interrupt state for an event of a GUIObject */ 200 | BOOL GUIObject_setEventInterrupt(GUIObject object, int eventID, BOOL interrupt){ 201 | if (!object || eventID < 0 || (UINT)eventID >= object->numEvents) 202 | return FALSE; 203 | 204 | (object->events)[eventID].interrupt = interrupt; 205 | return TRUE; 206 | } 207 | 208 | /* Changes an event's enabled state for a GUIObject */ 209 | BOOL GUIObject_setEventEnabled(GUIObject object, int eventID, BOOL enabled){ 210 | if (!object || eventID < 0 || (UINT)eventID >= object->numEvents) 211 | return FALSE; 212 | 213 | (object->events)[eventID].enabled = enabled; 214 | return TRUE; 215 | } 216 | 217 | /* Sets a WM_LBUTTONUP event for an (enabled) object */ 218 | int GUIObject_setOnClick(GUIObject object, void(*callback)(GUIObject, void*, EventArgs), void *context, enum _syncMode mode){ 219 | int eventID = GUIObject_setEvent(object, WM_LBUTTONUP, callback, context, mode); 220 | return GUIObject_setEventCondition(object, eventID, &(object->enabled)) ? eventID : -1; 221 | } 222 | 223 | /* Moves a GUIObject to a new location specified by x and y */ 224 | BOOL GUIObject_setPos(GUIObject object, int x, int y){ 225 | if (!object) 226 | return FALSE; 227 | 228 | object->x = x; object->realX = x; 229 | object->y = y; object->realY = y; 230 | if (!SetWindowPos(object->handle, NULL, x, y, object->width, object->height, SWP_NOSIZE | SWP_ASYNCWINDOWPOS | 231 | SWP_DRAWFRAME)) 232 | return FALSE; 233 | 234 | if (!InvalidateRect(object->handle, NULL, FALSE)) 235 | return FALSE; 236 | 237 | return TRUE; 238 | } 239 | 240 | /* Resizes a GUIObject to a new size specified by width and height */ 241 | BOOL GUIObject_setSize(GUIObject object, int width, int height){ 242 | if (!object) 243 | return FALSE; 244 | 245 | object->realWidth = width; 246 | object->realHeight = height; 247 | 248 | if (width >= object->minWidth && width <= object->maxWidth) 249 | object->width = width; 250 | if (height >= object->minHeight && height <= object->maxHeight) 251 | object->height = height; 252 | 253 | if (!SetWindowPos(object->handle, NULL, object->x, object->y, object->width, object->height, SWP_NOMOVE | SWP_ASYNCWINDOWPOS | 254 | SWP_DRAWFRAME)) 255 | return FALSE; 256 | 257 | if (!InvalidateRect(object->handle, NULL, FALSE)) 258 | return FALSE; 259 | 260 | return TRUE; 261 | } 262 | 263 | /* Sets a GUIObject's minimum size to a new value specified by minWidth and minHeight */ 264 | BOOL GUIObject_setMinSize(GUIObject object, int minWidth, int minHeight){ 265 | if (!object) 266 | return FALSE; 267 | 268 | object->minWidth = minWidth; 269 | object->minHeight = minHeight; 270 | 271 | if (minWidth > object->width){ 272 | object->width = minWidth; 273 | object->realWidth = minWidth; 274 | } 275 | 276 | if (minHeight > object->height){ 277 | object->height = minHeight; 278 | object->realHeight = minHeight; 279 | } 280 | 281 | if (!SetWindowPos(object->handle, NULL, object->x, object->y, object->width, object->height, SWP_NOMOVE | SWP_ASYNCWINDOWPOS | 282 | SWP_DRAWFRAME)) 283 | return FALSE; 284 | 285 | if (!InvalidateRect(object->handle, NULL, FALSE)) 286 | return FALSE; 287 | 288 | return TRUE; 289 | } 290 | 291 | /* Sets a GUIObject's maximum size to a new value specified by maxWidth and maxHeight */ 292 | BOOL GUIObject_setMaxSize(GUIObject object, int maxWidth, int maxHeight){ 293 | if (!object) 294 | return FALSE; 295 | 296 | object->maxWidth = maxWidth; 297 | object->maxHeight = maxHeight; 298 | 299 | if (maxWidth < object->width){ 300 | object->width = maxWidth; 301 | object->realWidth = maxWidth; 302 | } 303 | 304 | if (maxHeight < object->height){ 305 | object->height = maxHeight; 306 | object->realHeight = maxHeight; 307 | } 308 | 309 | if (!SetWindowPos(object->handle, NULL, object->x, object->y, object->width, object->height, SWP_NOMOVE | SWP_ASYNCWINDOWPOS | 310 | SWP_DRAWFRAME)) 311 | return FALSE; 312 | 313 | if (!InvalidateRect(object->handle, NULL, FALSE)) 314 | return FALSE; 315 | 316 | return TRUE; 317 | } 318 | 319 | /* Sets a new text for a GUIObject */ 320 | BOOL GUIObject_setText(GUIObject object, char *text){ 321 | int textLength = strlen(text); 322 | char *tempReallocPointer; 323 | 324 | if (!object) 325 | return FALSE; 326 | 327 | tempReallocPointer = (char*)realloc(object->text, textLength + 1); 328 | 329 | if (!tempReallocPointer) 330 | return FALSE; 331 | strcpy_s(tempReallocPointer, textLength + 1, text); 332 | object->text = tempReallocPointer; 333 | if (object->handle) 334 | return SetWindowTextA(object->handle, text); 335 | else 336 | return TRUE; 337 | } 338 | 339 | /* Sets a GUIObject's enabled state */ 340 | BOOL GUIObject_setEnabled(GUIObject object, BOOL enabled){ 341 | if (!object) 342 | return FALSE; 343 | object->enabled = enabled; 344 | 345 | if (object->handle) 346 | EnableWindow(object->handle, enabled); 347 | return TRUE; 348 | } 349 | 350 | PRIVATE HBITMAP GUIObject_updateOffscreenPaintContext(GUIObject object, BOOL eraseBG, BOOL transparent){ 351 | HBITMAP prevBitmap; 352 | RECT clientRect; 353 | HBRUSH backgroundBrush; 354 | 355 | GetClientRect(object->handle, &clientRect); 356 | 357 | if (!object->offscreenBitmap){ 358 | if (object->offscreenPaintContext) 359 | DeleteObject(object->offscreenPaintContext); 360 | 361 | object->offscreenPaintContext = CreateCompatibleDC(object->paintContext); 362 | object->offscreenBitmap = CreateCompatibleBitmap(object->paintContext, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top); 363 | } 364 | 365 | prevBitmap = SelectBitmap(object->offscreenPaintContext, object->offscreenBitmap); 366 | 367 | if (eraseBG){ 368 | backgroundBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); 369 | FillRect(object->offscreenPaintContext, &clientRect, backgroundBrush); 370 | DeleteObject(backgroundBrush); 371 | } 372 | 373 | if (transparent) 374 | SetBkMode(object->offscreenPaintContext, TRANSPARENT); 375 | 376 | return prevBitmap; 377 | } 378 | 379 | PRIVATE void GUIObject_updatePaintContext(GUIObject object, HBITMAP prevBitmap){ 380 | RECT clientRect; 381 | 382 | GetClientRect(object->handle, &clientRect); 383 | 384 | BitBlt(object->paintContext, clientRect.left, clientRect.top, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, 385 | object->offscreenPaintContext, 0, 0, SRCCOPY); 386 | SelectObject(object->offscreenPaintContext, prevBitmap); 387 | } 388 | 389 | /* Draws a line in a GUIObject from the point specified by x1, y1 to the point specified by x2, y2 */ 390 | BOOL GUIObject_drawLine(GUIObject object, Pen pen, int x1, int y1, int x2, int y2){ 391 | HPEN prevPen = NULL, activePen; 392 | POINT prevPoint; 393 | BOOL acquiredContext = FALSE; 394 | HBITMAP prevBitmap; 395 | 396 | if (!object) 397 | return FALSE; 398 | 399 | if (!pen) 400 | activePen = GetStockPen(NULL_PEN); /* If no pen was passed, use a null pen */ 401 | else 402 | activePen = pen->handle; 403 | 404 | 405 | if (!object->paintContext){ 406 | object->paintContext = GetDC(object->handle); 407 | acquiredContext = TRUE; 408 | } 409 | 410 | prevBitmap = GUIObject_updateOffscreenPaintContext(object, TRUE, TRUE); 411 | 412 | prevPen = (HPEN)SelectObject(object->offscreenPaintContext, activePen); 413 | if (!prevPen || !MoveToEx(object->offscreenPaintContext, x1, y1, &prevPoint)) 414 | goto release_DC_on_error; 415 | if (!LineTo(object->offscreenPaintContext, x2, y2)) 416 | goto release_DC_on_error; 417 | /* Restore previous values */ 418 | if (!MoveToEx(object->offscreenPaintContext, prevPoint.x, prevPoint.y, &prevPoint) || !SelectObject(object->offscreenPaintContext, prevPen)) 419 | goto release_DC_on_error; 420 | 421 | GUIObject_updatePaintContext(object, prevBitmap); 422 | 423 | if (acquiredContext){ 424 | ReleaseDC(object->handle, object->paintContext); 425 | object->paintContext = NULL; 426 | } 427 | 428 | return TRUE; 429 | 430 | release_DC_on_error: 431 | if (acquiredContext){ 432 | ReleaseDC(object->handle, object->paintContext); 433 | object->paintContext = NULL; 434 | } 435 | return FALSE; 436 | } 437 | 438 | /* Draws an arc in a GUIObject */ 439 | BOOL GUIObject_drawArc(GUIObject object, Pen pen, int boundX1, int boundY1, int boundX2, int boundY2, int x1, int y1, int x2, int y2){ 440 | HPEN prevPen = NULL, activePen; 441 | BOOL acquiredContext = FALSE; 442 | HBITMAP prevBitmap; 443 | 444 | if (!object) 445 | return FALSE; 446 | 447 | if (!pen) 448 | activePen = GetStockPen(NULL_PEN); /* If no pen was passed, use a null pen */ 449 | else 450 | activePen = pen->handle; 451 | 452 | 453 | if (!object->paintContext){ 454 | object->paintContext = GetDC(object->handle); 455 | acquiredContext = TRUE; 456 | } 457 | 458 | prevBitmap = GUIObject_updateOffscreenPaintContext(object, TRUE, TRUE); 459 | 460 | prevPen = (HPEN)SelectObject(object->offscreenPaintContext, activePen); 461 | if (!prevPen) 462 | goto release_DC_on_error; 463 | if (!Arc(object->offscreenPaintContext, boundX1, boundY1, boundX2, boundY2, x1, y1, x2, y2)) 464 | goto release_DC_on_error; 465 | /* Restore previous values */ 466 | if (!SelectObject(object->offscreenPaintContext, prevPen)) 467 | goto release_DC_on_error; 468 | 469 | GUIObject_updatePaintContext(object, prevBitmap); 470 | 471 | if (acquiredContext){ 472 | ReleaseDC(object->handle, object->paintContext); 473 | object->paintContext = NULL; 474 | } 475 | 476 | return TRUE; 477 | 478 | release_DC_on_error: 479 | if (acquiredContext){ 480 | ReleaseDC(object->handle, object->paintContext); 481 | object->paintContext = NULL; 482 | } 483 | return FALSE; 484 | } 485 | 486 | /* Draws a rectangle in a GUIObject */ 487 | BOOL GUIObject_drawRect(GUIObject object, Pen pen, Brush brush, int boundX1, int boundY1, int boundX2, int boundY2){ 488 | HPEN prevPen = NULL, activePen; 489 | HBRUSH prevBrush = NULL, activeBrush; 490 | BOOL acquiredContext = FALSE; 491 | HBITMAP prevBitmap; 492 | 493 | if (!object) 494 | return FALSE; 495 | 496 | if (!pen) 497 | activePen = GetStockPen(NULL_PEN); /* If no pen was passed, use a null pen */ 498 | else 499 | activePen = pen->handle; 500 | 501 | if (!brush) 502 | activeBrush = GetStockBrush(HOLLOW_BRUSH); /* If no brush was passed, use a hollow brush */ 503 | else 504 | activeBrush = brush->handle; 505 | 506 | if (!object->paintContext){ 507 | object->paintContext = GetDC(object->handle); 508 | acquiredContext = TRUE; 509 | } 510 | 511 | prevBitmap = GUIObject_updateOffscreenPaintContext(object, TRUE, TRUE); 512 | 513 | prevPen = (HPEN)SelectObject(object->offscreenPaintContext, activePen); 514 | prevBrush = (HBRUSH)SelectObject(object->offscreenPaintContext, activeBrush); 515 | if (!prevPen || !prevBrush) 516 | goto release_DC_on_error; 517 | if (!Rectangle(object->offscreenPaintContext, boundX1, boundY1, boundX2, boundY2)) 518 | goto release_DC_on_error; 519 | /* Restore previous values */ 520 | if (!SelectObject(object->offscreenPaintContext, prevPen) || !SelectObject(object->offscreenPaintContext, prevBrush)) 521 | goto release_DC_on_error; 522 | 523 | GUIObject_updatePaintContext(object, prevBitmap); 524 | 525 | if (acquiredContext){ 526 | ReleaseDC(object->handle, object->paintContext); 527 | object->paintContext = NULL; 528 | } 529 | 530 | return TRUE; 531 | 532 | release_DC_on_error: 533 | if (acquiredContext){ 534 | ReleaseDC(object->handle, object->paintContext); 535 | object->paintContext = NULL; 536 | } 537 | return FALSE; 538 | } 539 | 540 | /* Draws a rounded rectangle in a GUIObject */ 541 | BOOL GUIObject_drawRoundedRect(GUIObject object, Pen pen, Brush brush, int boundX1, int boundY1, 542 | int boundX2, int boundY2, int ellipseWidth, int ellipseHeight){ 543 | HPEN prevPen = NULL, activePen; 544 | HBRUSH prevBrush = NULL; 545 | HBRUSH activeBrush; 546 | BOOL acquiredContext = FALSE; 547 | HBITMAP prevBitmap; 548 | 549 | if (!object) 550 | return FALSE; 551 | 552 | if (!pen) 553 | activePen = GetStockPen(NULL_PEN); /* If no pen was passed, use a null pen */ 554 | else 555 | activePen = pen->handle; 556 | 557 | if (!brush) 558 | activeBrush = GetStockBrush(HOLLOW_BRUSH); /* If no brush was passed, use a hollow brush */ 559 | else 560 | activeBrush = brush->handle; 561 | 562 | if (!object->paintContext){ 563 | object->paintContext = GetDC(object->handle); 564 | acquiredContext = TRUE; 565 | } 566 | 567 | prevBitmap = GUIObject_updateOffscreenPaintContext(object, TRUE, TRUE); 568 | 569 | prevPen = (HPEN)SelectObject(object->offscreenPaintContext, activePen); 570 | prevBrush = (HBRUSH)SelectObject(object->offscreenPaintContext, activeBrush); 571 | if (!prevPen || !prevBrush) 572 | goto release_DC_on_error; 573 | if (!RoundRect(object->offscreenPaintContext, boundX1, boundY1, boundX2, boundY2, ellipseWidth, ellipseHeight)) 574 | goto release_DC_on_error; 575 | /* Restore previous values */ 576 | if (!SelectObject(object->offscreenPaintContext, prevPen) || !SelectObject(object->offscreenPaintContext, prevBrush)) 577 | goto release_DC_on_error; 578 | 579 | GUIObject_updatePaintContext(object, prevBitmap); 580 | 581 | if (acquiredContext){ 582 | ReleaseDC(object->handle, object->paintContext); 583 | object->paintContext = NULL; 584 | } 585 | 586 | return TRUE; 587 | 588 | release_DC_on_error: 589 | if (acquiredContext){ 590 | ReleaseDC(object->handle, object->paintContext); 591 | object->paintContext = NULL; 592 | } 593 | return FALSE; 594 | } 595 | 596 | /* Draws an ellipse in a GUIObject */ 597 | BOOL GUIObject_drawEllipse(GUIObject object, Pen pen, Brush brush, int boundX1, int boundY1, int boundX2, int boundY2){ 598 | HPEN prevPen = NULL, activePen; 599 | HBRUSH prevBrush = NULL; 600 | HBRUSH activeBrush; 601 | BOOL acquiredContext = FALSE; 602 | HBITMAP prevBitmap; 603 | 604 | if (!object) 605 | return FALSE; 606 | 607 | if (!pen) 608 | activePen = GetStockPen(NULL_PEN); /* If no pen was passed, use a null pen */ 609 | else 610 | activePen = pen->handle; 611 | 612 | if (!brush) 613 | activeBrush = GetStockBrush(HOLLOW_BRUSH); /* If no brush was passed, use a hollow brush */ 614 | else 615 | activeBrush = brush->handle; 616 | 617 | if (!object->paintContext){ 618 | object->paintContext = GetDC(object->handle); 619 | acquiredContext = TRUE; 620 | } 621 | 622 | prevBitmap = GUIObject_updateOffscreenPaintContext(object, TRUE, TRUE); 623 | 624 | prevPen = (HPEN)SelectObject(object->offscreenPaintContext, activePen); 625 | prevBrush = (HBRUSH)SelectObject(object->offscreenPaintContext, activeBrush); 626 | if (!prevPen || !prevBrush) 627 | goto release_DC_on_error; 628 | if (!Ellipse(object->offscreenPaintContext, boundX1, boundY1, boundX2, boundY2)) 629 | goto release_DC_on_error; 630 | /* Restore previous values */ 631 | if (!SelectObject(object->offscreenPaintContext, prevPen) || !SelectObject(object->offscreenPaintContext, prevBrush)) 632 | goto release_DC_on_error; 633 | 634 | GUIObject_updatePaintContext(object, prevBitmap); 635 | 636 | if (acquiredContext){ 637 | ReleaseDC(object->handle, object->paintContext); 638 | object->paintContext = NULL; 639 | } 640 | 641 | return TRUE; 642 | 643 | release_DC_on_error: 644 | if (acquiredContext){ 645 | ReleaseDC(object->handle, object->paintContext); 646 | object->paintContext = NULL; 647 | } 648 | return FALSE; 649 | } 650 | 651 | /* Draws a polygon in a GUIObject */ 652 | BOOL GUIObject_drawPolygon(GUIObject object, Pen pen, Brush brush, int numPoints, LONG *coords){ 653 | HPEN prevPen = NULL, activePen; 654 | HBRUSH prevBrush = NULL; 655 | HBRUSH activeBrush; 656 | POINT *points = (POINT*)malloc(numPoints * sizeof(POINT)); 657 | BOOL acquiredContext = FALSE; 658 | HBITMAP prevBitmap; 659 | 660 | int i; 661 | 662 | if (!object || numPoints < 2){ 663 | free(points); 664 | return FALSE; 665 | } 666 | 667 | if (!pen) 668 | activePen = GetStockPen(NULL_PEN); /* If no pen was passed, use a null pen */ 669 | else 670 | activePen = pen->handle; 671 | 672 | if (!brush) 673 | activeBrush = GetStockBrush(HOLLOW_BRUSH); /* If no brush was passed, use a hollow brush */ 674 | else 675 | activeBrush = brush->handle; 676 | 677 | if (!object->paintContext){ 678 | object->paintContext = GetDC(object->handle); 679 | acquiredContext = TRUE; 680 | } 681 | 682 | for (i = 0; i < numPoints * 2; i+=2){ 683 | points[i / 2].x = coords[i]; 684 | points[i / 2].y = coords[i + 1]; 685 | } 686 | 687 | prevBitmap = GUIObject_updateOffscreenPaintContext(object, TRUE, TRUE); 688 | 689 | prevPen = (HPEN)SelectObject(object->offscreenPaintContext, activePen); 690 | prevBrush = (HBRUSH)SelectObject(object->offscreenPaintContext, activeBrush); 691 | if (!prevPen || !prevBrush) 692 | goto release_DC_on_error; 693 | if (!Polygon(object->offscreenPaintContext, points, numPoints)) 694 | goto release_DC_on_error; 695 | /* Restore previous values */ 696 | if (!SelectObject(object->offscreenPaintContext, prevPen) || !SelectObject(object->offscreenPaintContext, prevBrush)) 697 | goto release_DC_on_error; 698 | 699 | GUIObject_updatePaintContext(object, prevBitmap); 700 | 701 | if (acquiredContext){ 702 | ReleaseDC(object->handle, object->paintContext); 703 | object->paintContext = NULL; 704 | } 705 | 706 | free(points); 707 | return TRUE; 708 | 709 | release_DC_on_error: 710 | if (acquiredContext){ 711 | ReleaseDC(object->handle, object->paintContext); 712 | object->paintContext = NULL; 713 | } 714 | 715 | free(points); 716 | return FALSE; 717 | } 718 | 719 | /* Get the number of symbols in the decimal representation of a number. */ 720 | PRIVATE unsigned int getNumLength(unsigned int num){ 721 | unsigned int numLength = 1; 722 | while ((num /= 10) > 0) 723 | numLength++; 724 | return numLength; 725 | } 726 | 727 | PRIVATE void setGUIObjectFields(GUIObject thisObject, HINSTANCE instance, char *text, int width, int height){ 728 | static HMENU ID = NULL; 729 | unsigned int IDLength, textLength; 730 | 731 | /* Set the fields */ 732 | 733 | thisObject->moduleInstance = instance; 734 | 735 | initObject((Object)thisObject); 736 | 737 | 738 | EnterCriticalSection(&(thisObject->criticalSection)); 739 | thisObject->ID = ID; 740 | ID++; 741 | LeaveCriticalSection(&(thisObject->criticalSection)); 742 | 743 | IDLength = getNumLength((unsigned int)(thisObject->ID)); 744 | thisObject->className = (char*)malloc(IDLength + 1); 745 | if (!thisObject->className) 746 | goto alloc_className_failed; 747 | sprintf_s(thisObject->className, IDLength + 1, "%d", thisObject->ID); 748 | 749 | thisObject->numEvents = 1; 750 | thisObject->events = (struct _event*)malloc(thisObject->numEvents * sizeof(struct _event)); //TODO: add more events 751 | if (!thisObject->events) 752 | goto alloc_events_failed; 753 | (thisObject->events)[0].eventFunction = NULL; (thisObject->events)[0].mode = SYNC; (thisObject->events)[0].message = WM_LBUTTONUP; 754 | (thisObject->events)[0].sender = thisObject; (thisObject->events)[0].context = NULL; 755 | (thisObject->events)[0].args = newEventArgs(WM_LBUTTONUP, 0, 0); (thisObject->events)[0].condition = NULL; 756 | (thisObject->events)[0].interrupt = FALSE; (thisObject->events)[0].enabled = FALSE; 757 | 758 | if (text){ 759 | textLength = strlen(text); 760 | thisObject->text = (char*)malloc(textLength + 1); 761 | if (!thisObject->text) 762 | goto alloc_text_failed; 763 | strcpy_s(thisObject->text, textLength + 1, text); /* Copy the object text to the heap */ 764 | } else 765 | thisObject->text = NULL; 766 | 767 | thisObject->width = width; thisObject->height = height; 768 | thisObject->realWidth = width; thisObject->realHeight = height; 769 | 770 | return; 771 | 772 | /* Cleanup in case of malloc failures. That's where goto's come in handy */ 773 | alloc_text_failed: free(thisObject->events); 774 | alloc_events_failed: free(thisObject->className); 775 | alloc_className_failed: DeleteCriticalSection(&(thisObject->criticalSection)); 776 | } 777 | 778 | /* The Constructors*/ 779 | void initGUIObject(GUIObject thisObject, HINSTANCE instance, char *text, int width, int height){ 780 | 781 | if (!thisObject) 782 | return; 783 | 784 | /* Initialize the fields to the default values */ 785 | CLASS_GUIObject; 786 | 787 | setGUIObjectFields(thisObject, instance, text, width, height); 788 | 789 | thisObject->type = GUIOBJECT; 790 | 791 | return; 792 | } 793 | 794 | GUIObject newGUIObject(HINSTANCE instance, char *text, int width, int height){ 795 | GUIObject thisObject = (GUIObject)malloc(sizeof(val_GUIObject)); 796 | if (!thisObject) 797 | return NULL; 798 | 799 | initGUIObject(thisObject, instance, text, width, height); 800 | return thisObject; 801 | } 802 | 803 | void freeGUIObjectFields(GUIObject object){ 804 | unsigned int i; 805 | EndPaint(object->handle, &(object->paintData)); 806 | if (!DestroyWindow(object->handle)) 807 | SendMessageA(object->handle, WM_CLOSE, (WPARAM)NULL, (WPARAM)NULL); 808 | 809 | DeleteCriticalSection(&(object->criticalSection)); 810 | 811 | if (object->parent) 812 | GUIObject_removeChild(object->parent, object); 813 | 814 | free(object->className); 815 | if (object->events){ 816 | for (i = 0; i < object->numEvents; i++) 817 | if ((object->events)[i].args) 818 | if ((object->events)[i].args->type == EVENTARGS) 819 | deleteEventArgs((object->events)[i].args); 820 | else if ((object->events)[i].args->type == MOUSEEVENTARGS) 821 | deleteMouseEventArgs((MouseEventArgs)(object->events)[i].args); 822 | free(object->events); 823 | } 824 | free(object->text); 825 | } 826 | 827 | /* The Destructor*/ 828 | void deleteGUIObject(GUIObject object){ 829 | freeGUIObjectFields(object); 830 | free(object); 831 | } 832 | 833 | 834 | 835 | 836 | 837 | /* Class Window */ 838 | /* The Methods*/ 839 | /* Changes a window's resizable style */ 840 | BOOL Window_setResizable(Window window, BOOL resizable){ 841 | if (!window) 842 | return FALSE; 843 | window->resizable = resizable; 844 | window->styles = resizable ? (window->styles) | WS_THICKFRAME : (window->styles) ^ WS_THICKFRAME; 845 | 846 | if (window->handle) 847 | return SetWindowLongPtrA(window->handle, GWL_STYLE, (LONG)(window->styles)) ? TRUE : FALSE; 848 | else 849 | return TRUE; 850 | } 851 | 852 | /* Enables or disables a window's maximize box */ 853 | BOOL Window_enableMaximize(Window window, BOOL maximize){ 854 | if (!window) 855 | return FALSE; 856 | window->maximizeEnabled = maximize; 857 | window->styles = maximize ? window->styles | WS_MAXIMIZEBOX : window->styles ^ WS_MAXIMIZEBOX; 858 | 859 | if (window->handle) 860 | return SetWindowLongPtrA(window->handle, GWL_STYLE, (LONG)(window->styles)) ? TRUE : FALSE; 861 | else 862 | return TRUE; 863 | } 864 | 865 | 866 | /* The Constructors*/ 867 | void initWindow(Window thisObject, HINSTANCE instance, char *text, int width, int height){ 868 | if (!thisObject) 869 | return; 870 | 871 | CLASS_Window; 872 | 873 | setGUIObjectFields((GUIObject)thisObject, instance, text, width, height); 874 | 875 | /* Register the window class */ 876 | if (!registerClass(thisObject->moduleInstance, thisObject->className, (WNDPROC)windowProc)) 877 | return; 878 | 879 | /* Set the fields */ 880 | thisObject->type = WINDOW; 881 | thisObject->styles = WS_OVERLAPPEDWINDOW; 882 | thisObject->exStyles = WS_EX_WINDOWEDGE; 883 | } 884 | 885 | Window newWindow(HINSTANCE instance, char *text, int width, int height){ 886 | Window thisObject = (Window)malloc(sizeof(val_Window)); 887 | 888 | if (!thisObject) 889 | return NULL; 890 | 891 | initWindow(thisObject, instance, text, width, height); 892 | 893 | return thisObject; 894 | } 895 | 896 | /* The Destructor*/ 897 | void deleteWindow(Window window){ 898 | freeGUIObjectFields((GUIObject)window); 899 | free(window); 900 | } 901 | 902 | 903 | 904 | 905 | 906 | /* Class Control */ 907 | /* The Methods*/ 908 | /* Moves a GUIObject to a new location specified by x and y */ 909 | /* Overrides setPosT in GUIObject */ 910 | BOOL Control_setPos(GUIObject object, int x, int y){ 911 | if (!object) 912 | return FALSE; 913 | 914 | object->realX = x; 915 | object->realY = y; 916 | 917 | if (x >= ((Control)object)->minX && x <= ((Control)object)->maxX) 918 | object->x = x; 919 | if (y >= ((Control)object)->minY && y <= ((Control)object)->maxY) 920 | object->y = y; 921 | 922 | if (!SetWindowPos(object->handle, NULL, object->x, object->y, object->width, object->height, SWP_NOSIZE | SWP_ASYNCWINDOWPOS | 923 | SWP_DRAWFRAME)) 924 | return FALSE; 925 | 926 | if (!InvalidateRect(object->handle, NULL, FALSE)) 927 | return FALSE; 928 | 929 | return TRUE; 930 | } 931 | 932 | /* Sets a control's minimum position to a new value specified by minX and minY */ 933 | BOOL Control_setMinPos(Control object, int minX, int minY){ 934 | if (!object) 935 | return FALSE; 936 | 937 | object->minX = minX; 938 | object->minY = minY; 939 | 940 | if (minX > object->x){ 941 | object->x = minX; 942 | object->realX = minX; 943 | } 944 | 945 | if (minY > object->y){ 946 | object->y = minY; 947 | object->realY = minY; 948 | } 949 | 950 | if (!SetWindowPos(object->handle, NULL, object->x, object->y, object->width, object->height, SWP_NOMOVE | SWP_ASYNCWINDOWPOS | 951 | SWP_DRAWFRAME)) 952 | return FALSE; 953 | 954 | if (!InvalidateRect(object->handle, NULL, FALSE)) 955 | return FALSE; 956 | 957 | return TRUE; 958 | } 959 | 960 | /* Sets a control's maximum position to a new value specified by maxX and maxY */ 961 | BOOL Control_setMaxPos(Control object, int maxX, int maxY){ 962 | if (!object) 963 | return FALSE; 964 | 965 | object->maxX = maxX; 966 | object->maxY = maxY; 967 | 968 | if (maxX < object->x){ 969 | object->x = maxX; 970 | object->realX = maxX; 971 | } 972 | 973 | if (maxY < object->y){ 974 | object->y = maxY; 975 | object->realY = maxY; 976 | } 977 | 978 | if (!SetWindowPos(object->handle, NULL, object->x, object->y, object->width, object->height, SWP_NOMOVE | SWP_ASYNCWINDOWPOS | 979 | SWP_DRAWFRAME)) 980 | return FALSE; 981 | 982 | if (!InvalidateRect(object->handle, NULL, FALSE)) 983 | return FALSE; 984 | 985 | return TRUE; 986 | } 987 | 988 | 989 | PRIVATE void setControlFields(Control thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height){ 990 | if (!thisObject) 991 | return; 992 | 993 | setGUIObjectFields((GUIObject)thisObject, instance, text, width, height); 994 | 995 | /* Set the fields */ 996 | thisObject->x = x; thisObject->realX = x; 997 | thisObject->y = y; thisObject->realY = y; 998 | thisObject->exStyles = WS_EX_WINDOWEDGE; 999 | 1000 | free(thisObject->className); 1001 | thisObject->className = NULL; 1002 | 1003 | /* Override virtual methods */ 1004 | thisObject->setPos = &Control_setPos; 1005 | } 1006 | 1007 | 1008 | /* The Constructors*/ 1009 | void initControl(Control thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height){ 1010 | if (!thisObject) 1011 | return; 1012 | 1013 | CLASS_Control; 1014 | 1015 | setControlFields(thisObject, instance, text, x, y, width, height); 1016 | 1017 | thisObject->type = CONTROL; 1018 | } 1019 | 1020 | Control newControl(HINSTANCE instance, char *text, int x, int y, int width, int height){ 1021 | Control thisObject = (Control)malloc(sizeof(val_Control)); 1022 | 1023 | if (!thisObject) 1024 | return NULL; 1025 | 1026 | initControl(thisObject, instance, text, width, x, y, height); 1027 | 1028 | return thisObject; 1029 | } 1030 | 1031 | /* The Destructor*/ 1032 | void deleteControl(Control control){ 1033 | freeGUIObjectFields((GUIObject)control); 1034 | free(control); 1035 | } 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | /* Class Button */ 1042 | /* The Constructors*/ 1043 | void initButton(Button thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height){ 1044 | if (!thisObject) 1045 | return; 1046 | 1047 | CLASS_Button; 1048 | 1049 | setControlFields((Control)thisObject, instance, text, x, y, width, height); 1050 | 1051 | /* Set the fields */ 1052 | thisObject->type = BUTTON; 1053 | thisObject->className = "Button"; 1054 | thisObject->styles = WS_CHILD | WS_VISIBLE | BS_TEXT | BS_PUSHBUTTON; 1055 | } 1056 | 1057 | Button newButton(HINSTANCE instance, char *text, int x, int y, int width, int height){ 1058 | Button thisObject = (Button)malloc(sizeof(val_Button)); 1059 | 1060 | if (!thisObject) 1061 | return NULL; 1062 | 1063 | initButton(thisObject, instance, text, x, y, width, height); 1064 | 1065 | return thisObject; 1066 | } 1067 | 1068 | /* The Destructor*/ 1069 | void deleteButton(Button button){ 1070 | button->className = NULL; 1071 | freeGUIObjectFields((GUIObject)button); 1072 | free(button); 1073 | } 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | /* Class TextBox */ 1080 | /* The Methods*/ 1081 | /* Sets the text input mode for a textbox to number-only or to not number-only */ 1082 | BOOL TextBox_setNumOnly(TextBox textbox, BOOL numOnly){ 1083 | textbox->numOnly = numOnly; 1084 | textbox->styles = numOnly ? textbox->styles | ES_NUMBER : textbox->styles ^ ES_NUMBER; 1085 | 1086 | if (textbox->handle) 1087 | return SetWindowLongPtrA(textbox->handle, GWL_STYLE, (LONG)textbox->styles) ? TRUE : FALSE; 1088 | else 1089 | return TRUE; 1090 | } 1091 | 1092 | 1093 | 1094 | /* The Constructors*/ 1095 | void initTextBox(TextBox thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height, enum _textboxtype multiline){ 1096 | if (!thisObject) 1097 | return; 1098 | 1099 | CLASS_TextBox; 1100 | 1101 | setControlFields((Control)thisObject, instance, text, x, y, width, height); 1102 | 1103 | /* Set the fields */ 1104 | thisObject->type = TEXTBOX; 1105 | thisObject->className = "Edit"; 1106 | thisObject->styles = WS_CHILD | WS_VISIBLE | WS_BORDER; 1107 | 1108 | thisObject->multiline = (BOOL)multiline; 1109 | if (thisObject->multiline) 1110 | thisObject->styles |= ES_MULTILINE | ES_WANTRETURN; 1111 | } 1112 | 1113 | TextBox newTextBox(HINSTANCE instance, char *text, int x, int y, int width, int height, enum _textboxtype multiline){ 1114 | TextBox thisObject = (TextBox)malloc(sizeof(val_TextBox)); 1115 | 1116 | if (!thisObject) 1117 | return NULL; 1118 | 1119 | initTextBox(thisObject, instance, text, x, y, width, height, multiline); 1120 | 1121 | return thisObject; 1122 | } 1123 | 1124 | /* The Destructor*/ 1125 | void deleteTextBox(TextBox textbox){ 1126 | textbox->className = NULL; 1127 | freeGUIObjectFields((GUIObject)textbox); 1128 | free(textbox); 1129 | } 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | /* Class Label */ 1136 | /* The Constructors*/ 1137 | void initLabel(Label thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height){ 1138 | if (!thisObject) 1139 | return; 1140 | 1141 | CLASS_Button; 1142 | 1143 | setControlFields((Control)thisObject, instance, text, x, y, width, height); 1144 | 1145 | /* Set the fields */ 1146 | thisObject->type = LABEL; 1147 | thisObject->className = "Static"; 1148 | thisObject->styles = WS_CHILD | WS_VISIBLE | SS_NOTIFY; 1149 | } 1150 | 1151 | Label newLabel(HINSTANCE instance, char *text, int x, int y, int width, int height){ 1152 | Label thisObject = (Label)malloc(sizeof(val_Label)); 1153 | 1154 | if (!thisObject) 1155 | return NULL; 1156 | 1157 | initLabel(thisObject, instance, text, x, y, width, height); 1158 | 1159 | return thisObject; 1160 | } 1161 | 1162 | /* The Destructor*/ 1163 | void deleteLabel(Label label){ 1164 | label->className = NULL; 1165 | freeGUIObjectFields((GUIObject)label); 1166 | free(label); 1167 | } 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | /* Class EventArgs */ 1174 | 1175 | /* The Constructors*/ 1176 | void initEventArgs(EventArgs thisObject, UINT message, WPARAM wParam, LPARAM lParam){ 1177 | if (!thisObject) 1178 | return; 1179 | 1180 | initObject((Object)thisObject); 1181 | 1182 | thisObject->type = EVENTARGS; 1183 | 1184 | thisObject->message = message; 1185 | thisObject->wParam = wParam; 1186 | thisObject->lParam = lParam; 1187 | 1188 | thisObject->updateValue = &initEventArgs; 1189 | } 1190 | 1191 | EventArgs newEventArgs(UINT message, WPARAM wParam, LPARAM lParam){ 1192 | EventArgs thisObject = (EventArgs)malloc(sizeof(val_EventArgs)); 1193 | 1194 | if (!thisObject) 1195 | return NULL; 1196 | 1197 | initEventArgs(thisObject, message, wParam, lParam); 1198 | 1199 | return thisObject; 1200 | } 1201 | 1202 | /* The Destructor*/ 1203 | void deleteEventArgs(EventArgs eventargs){ 1204 | DeleteCriticalSection(&(eventargs->criticalSection)); 1205 | free(eventargs); 1206 | } 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | /* Class MouseEventArgs */ 1213 | 1214 | void MouseEventArgs_updateValue(EventArgs thisObject, UINT message, WPARAM wParam, LPARAM lParam){ 1215 | initMouseEventArgs((MouseEventArgs)thisObject, message, wParam, lParam); 1216 | } 1217 | 1218 | /* The Constructors*/ 1219 | void initMouseEventArgs(MouseEventArgs thisObject, UINT message, WPARAM wParam, LPARAM lParam){ 1220 | if (!thisObject) 1221 | return; 1222 | 1223 | initEventArgs((EventArgs)thisObject, message, wParam, lParam); 1224 | 1225 | thisObject->type = MOUSEEVENTARGS; 1226 | 1227 | thisObject->cursorX = GET_X_LPARAM(lParam); 1228 | thisObject->cursorY = GET_Y_LPARAM(lParam); 1229 | 1230 | thisObject->updateValue = &MouseEventArgs_updateValue; 1231 | } 1232 | 1233 | MouseEventArgs newMouseEventArgs(UINT message, WPARAM wParam, LPARAM lParam){ 1234 | MouseEventArgs thisObject = (MouseEventArgs)malloc(sizeof(val_MouseEventArgs)); 1235 | if (!thisObject) 1236 | return NULL; 1237 | 1238 | initMouseEventArgs(thisObject, message, wParam, lParam); 1239 | 1240 | return thisObject; 1241 | } 1242 | 1243 | /* The Destructor*/ 1244 | void deleteMouseEventArgs(MouseEventArgs mouseeventargs){ 1245 | DeleteCriticalSection(&(mouseeventargs->criticalSection)); 1246 | free(mouseeventargs); 1247 | } 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | /* Class Pen */ 1254 | 1255 | /* The Constructors*/ 1256 | void initPen(Pen thisObject, int penStyle, int width, COLORREF color){ 1257 | if (!thisObject) 1258 | return; 1259 | 1260 | initObject((Object)thisObject); 1261 | 1262 | thisObject->type = PEN; 1263 | 1264 | thisObject->penStyle = penStyle; 1265 | thisObject->width = width; 1266 | thisObject->color = color; 1267 | 1268 | if (thisObject->handle) 1269 | DeleteObject(thisObject->handle); 1270 | thisObject->handle = CreatePen(penStyle, width, color); 1271 | } 1272 | 1273 | Pen newPen(int penStyle, int width, COLORREF color){ 1274 | Pen thisObject = (Pen)malloc(sizeof(val_Pen)); 1275 | 1276 | if (!thisObject) 1277 | return NULL; 1278 | 1279 | initPen(thisObject, penStyle, width, color); 1280 | 1281 | return thisObject; 1282 | } 1283 | 1284 | /* The Destructor*/ 1285 | void deletePen(Pen pen){ 1286 | DeleteObject(pen->handle); 1287 | DeleteCriticalSection(&(pen->criticalSection)); 1288 | free(pen); 1289 | } 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | /* Class Brush */ 1296 | 1297 | /* The Constructors*/ 1298 | void initBrush(Brush thisObject, UINT brushStyle, COLORREF color, ULONG_PTR hatch){ 1299 | LOGBRUSH brushInfo; 1300 | 1301 | if (!thisObject) 1302 | return; 1303 | 1304 | initObject((Object)thisObject); 1305 | 1306 | thisObject->type = BRUSH; 1307 | 1308 | thisObject->brushStyle = brushStyle; brushInfo.lbStyle = brushStyle; 1309 | thisObject->color = color; brushInfo.lbColor = color; 1310 | thisObject->hatch = hatch; brushInfo.lbHatch = hatch; 1311 | 1312 | if (thisObject->handle) 1313 | DeleteObject(thisObject->handle); 1314 | 1315 | thisObject->handle = CreateBrushIndirect(&brushInfo); 1316 | } 1317 | 1318 | Brush newBrush(UINT brushStyle, COLORREF color, ULONG_PTR hatch){ 1319 | Brush thisObject = (Brush)malloc(sizeof(val_Brush)); 1320 | 1321 | if (!thisObject) 1322 | return NULL; 1323 | 1324 | initBrush(thisObject, brushStyle, color, hatch); 1325 | 1326 | return thisObject; 1327 | } 1328 | 1329 | /* The Destructor*/ 1330 | void deleteBrush(Brush brush){ 1331 | DeleteObject(brush->handle); 1332 | DeleteCriticalSection(&(brush->criticalSection)); 1333 | free(brush); 1334 | } 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | /* WinAPI Call Functions */ 1341 | 1342 | /* Event handling */ 1343 | /* The common asynchronous event callback function */ 1344 | static DWORD WINAPI asyncEventProc(LPVOID event){ 1345 | struct _event *eventPointer = (struct _event*)event; 1346 | eventPointer->eventFunction(eventPointer->sender, eventPointer->context, eventPointer->args); 1347 | return TRUE; 1348 | } 1349 | 1350 | /* Find and fire off an event for a GUIObject */ 1351 | static int handleEvents(GUIObject currObject, UINT messageID, WPARAM wParam, LPARAM lParam){ 1352 | unsigned int i; 1353 | BOOL condition = TRUE; 1354 | 1355 | for (i = 0; i < currObject->numEvents; i++){ 1356 | if ((currObject->events)[i].message == messageID && (currObject->events)[i].eventFunction != NULL){ 1357 | if ((currObject->events)[i].condition) 1358 | condition = *((currObject->events)[i].condition); 1359 | 1360 | if ((currObject->events)[i].enabled && condition && (currObject->events)[i].eventFunction){ 1361 | EnterCriticalSection(&((currObject->events)[i].args->criticalSection)); 1362 | (currObject->events)[i].args->updateValue((currObject->events)[i].args, messageID, wParam, lParam); /* Update the event args */ 1363 | LeaveCriticalSection(&((currObject->events)[i].args->criticalSection)); 1364 | 1365 | if ((currObject->events)[i].mode == SYNC){ 1366 | ((currObject->events)[i].eventFunction)((currObject->events)[i].sender, 1367 | (currObject->events)[i].context, (currObject->events)[i].args); 1368 | } else 1369 | CreateThread(NULL, 0, asyncEventProc, (LPVOID)&(currObject->events)[i], 0, NULL); 1370 | } 1371 | 1372 | return (int)i; 1373 | } 1374 | } 1375 | 1376 | return -1; 1377 | } 1378 | 1379 | /* The event handling routine for WM_COMMAND type events for window controls */ 1380 | static int commandEventHandler(HWND hwnd, WPARAM wParam, LPARAM lParam){ 1381 | UINT itemID = LOWORD(wParam), messageID = HIWORD(wParam); 1382 | GUIObject currObject = (GUIObject)GetWindowLongPtrA((HWND)lParam, GWLP_USERDATA); 1383 | char *controlText = NULL; 1384 | int textLength = 0; 1385 | 1386 | if (currObject){ 1387 | if (messageID == EN_CHANGE){ /* Text in a textbox was changed by the user */ 1388 | textLength = GetWindowTextLengthA(currObject->handle); 1389 | if (textLength > 0 && (controlText = (char*)malloc(textLength + 1))){ 1390 | if (GetWindowTextA(currObject->handle, controlText, textLength + 1) > 0){ 1391 | free(currObject->text); 1392 | currObject->text = controlText; 1393 | } 1394 | } 1395 | } 1396 | 1397 | return handleEvents(currObject, messageID, wParam, lParam); 1398 | } else 1399 | return -1; 1400 | } 1401 | 1402 | /*FIELD VALUES CONSISTENCY SUPPORT ON REFRESH*/ 1403 | /* Resizes and/or moves the children of a window or a control according to their anchor settings */ 1404 | BOOL alignChildren(GUIObject object, int widthChange, int heightChange){ 1405 | unsigned int i; 1406 | Control currChild; 1407 | int controlWidthChange = 0, controlHeightChange = 0; 1408 | 1409 | if (!object) 1410 | return FALSE; 1411 | 1412 | for (i = 0; i < object->numChildren; i++){ 1413 | if ((object->children)[i] != NULL){ 1414 | currChild = (Control)(object->children)[i]; 1415 | InvalidateRect(currChild->handle, NULL, FALSE); 1416 | 1417 | if (((currChild->anchor & 0xF000) && (currChild->anchor & 0x000F)) || 1418 | ((currChild->anchor & 0x0F00) && (currChild->anchor & 0x00F0))){ /* Anchored top and bottom and/or left and right */ 1419 | if ((currChild->anchor & 0xF000) && (currChild->anchor & 0x000F)) /* Anchored left and right */ 1420 | controlWidthChange = widthChange; 1421 | 1422 | if ((currChild->anchor & 0x0F00) && (currChild->anchor & 0x00F0)) /* Anchored top and bottom */ 1423 | controlHeightChange = heightChange; 1424 | 1425 | EnterCriticalSection(&(currChild->criticalSection)); 1426 | GUIObject_setSize((object->children)[i], currChild->realWidth + controlWidthChange, currChild->realHeight + controlHeightChange); 1427 | LeaveCriticalSection(&(currChild->criticalSection)); 1428 | 1429 | if (currChild->numChildren != 0) 1430 | if (!alignChildren((object->children)[i], controlWidthChange, controlHeightChange)) 1431 | return FALSE; 1432 | } 1433 | 1434 | if ((!(currChild->anchor & 0xF000) && (currChild->anchor & 0x000F)) || 1435 | (!(currChild->anchor & 0x0F00) && (currChild->anchor & 0x00F0))){ /* Anchored right but not left and/or bottom but not top */ 1436 | EnterCriticalSection(&(currChild->criticalSection)); 1437 | Control_setPos((object->children)[i], currChild->realX + widthChange, currChild->realY + heightChange); 1438 | LeaveCriticalSection(&(currChild->criticalSection)); 1439 | } 1440 | 1441 | /* Not anchored left or right */ 1442 | if (!(currChild->anchor & 0xF000) && !(currChild->anchor & 0x000F)){ 1443 | EnterCriticalSection(&(currChild->criticalSection)); EnterCriticalSection(&((currChild->parent)->criticalSection)); 1444 | Control_setPos((object->children)[i], currChild->realX + (currChild->parent)->width / 2 - ((currChild->parent)->width - widthChange) / 2, 1445 | currChild->realY); 1446 | LeaveCriticalSection(&(currChild->criticalSection)); LeaveCriticalSection(&((currChild->parent)->criticalSection)); 1447 | } 1448 | 1449 | /* Not anchored top or bottom */ 1450 | if (!(currChild->anchor & 0x0F00) && !(currChild->anchor & 0x00F0)) { 1451 | EnterCriticalSection(&(currChild->criticalSection)); EnterCriticalSection(&((currChild->parent)->criticalSection)); 1452 | Control_setPos((object->children)[i], currChild->realX, 1453 | currChild->realY + (currChild->parent)->height / 2 - ((currChild->parent)->height - heightChange) / 2); 1454 | LeaveCriticalSection(&(currChild->criticalSection)); LeaveCriticalSection(&((currChild->parent)->criticalSection)); 1455 | } 1456 | } 1457 | } 1458 | 1459 | return TRUE; 1460 | } 1461 | 1462 | /* Sets the current window's width, height and clientWidth and clientHeight on user resize. Thread-safe */ 1463 | static void refreshWindowSize(Window window, LPARAM lParam){ 1464 | RECT clientSize; 1465 | WINDOWPOS *windowPos; 1466 | int widthChange = 0, heightChange = 0; 1467 | 1468 | EnterCriticalSection(&(window->criticalSection)); 1469 | 1470 | windowPos = (WINDOWPOS*)lParam; 1471 | window->width = windowPos->cx; 1472 | window->height = windowPos->cy; 1473 | window->x = windowPos->x; 1474 | window->y = windowPos->y; 1475 | 1476 | if (GetClientRect(window->handle, &clientSize)){ 1477 | widthChange = clientSize.right - clientSize.left - window->clientWidth; 1478 | heightChange = clientSize.bottom - clientSize.top - window->clientHeight; 1479 | 1480 | window->clientWidth = clientSize.right - clientSize.left; 1481 | window->clientHeight = clientSize.bottom - clientSize.top; 1482 | } 1483 | 1484 | InvalidateRect(window->handle, NULL, FALSE); 1485 | 1486 | LeaveCriticalSection(&(window->criticalSection)); 1487 | 1488 | alignChildren((GUIObject)window, widthChange, heightChange); 1489 | } 1490 | 1491 | /* Display a control on a window */ 1492 | BOOL displayControl(Control control){ 1493 | HFONT hFont; 1494 | LOGFONT lf; 1495 | 1496 | /* Add the control */ 1497 | control->handle = CreateWindowExA(control->exStyles, control->className, control->text, control->styles, control->x, control->y, 1498 | control->width, control->height, (control->parent) ? (control->parent)->handle : NULL, control->ID, control->moduleInstance, NULL); 1499 | 1500 | if (!control->handle) 1501 | return FALSE; 1502 | 1503 | /* Change its font */ 1504 | GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf); 1505 | hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);//CreateFontW(0, 0, 0, 0, 400, 0, 0, lf.lfStrikeOut, lf.lfCharSet, lf.lfOutPrecision, 1506 | //lf.lfClipPrecision, lf.lfQuality, lf.lfPitchAndFamily, lf.lfFaceName); 1507 | //TODO: make the font a property of the Object type! 1508 | SendMessageA(control->handle, WM_SETFONT, (WPARAM)hFont, TRUE); 1509 | 1510 | /* Set the control handle's additional data to a pointer to its object */ 1511 | SetWindowLongPtrA(control->handle, GWLP_USERDATA, (LONG)control); 1512 | 1513 | /* Subclass the control to make it send its messages through the main window proc */ 1514 | control->origProcPtr = SetWindowLongPtrA(control->handle, GWLP_WNDPROC, (LONG_PTR)windowProc); 1515 | if (!control->origProcPtr) 1516 | return FALSE; 1517 | 1518 | ShowWindow(control->handle, SW_SHOWDEFAULT); 1519 | 1520 | UpdateWindow(control->handle); 1521 | EnableWindow(control->handle, control->enabled); 1522 | if (control->parent) 1523 | UpdateWindow(control->parent->handle); 1524 | 1525 | return TRUE; 1526 | } 1527 | 1528 | /* Go through a GUIObject's list of children and add all of them to the window, then call itself on each of the children */ 1529 | static void displayChildren(GUIObject object){ 1530 | unsigned int i; 1531 | 1532 | if (object) 1533 | for (i = 0; i < object->numChildren; i++) 1534 | if ((object->children)[i] != NULL){ 1535 | displayControl((Control)(object->children)[i]); 1536 | displayChildren((object->children)[i]); 1537 | } 1538 | } 1539 | 1540 | 1541 | /* The Window Procedure callback function */ 1542 | static LRESULT CALLBACK windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){ 1543 | GUIObject currObject = NULL; 1544 | int eventID = -1; 1545 | BOOL interrupt = FALSE; 1546 | LRESULT defCallResult = (LRESULT)NULL; 1547 | 1548 | if (hwnd != NULL) 1549 | currObject = (GUIObject)GetWindowLongPtrA(hwnd, GWLP_USERDATA); /* Get the object that this handle belongs to */ 1550 | 1551 | /* Default event handling */ 1552 | switch(msg){ 1553 | case WM_CREATE: 1554 | if (currObject) 1555 | displayChildren(currObject); 1556 | break; 1557 | 1558 | case WM_PAINT: 1559 | if (currObject){ /* Begin or end painting the object */ 1560 | /* We need default paint processing for the control to occur BEFORE we begin painting */ 1561 | if (currObject->type == WINDOW) 1562 | defCallResult = DefWindowProcA(hwnd, msg, wParam, lParam); 1563 | else 1564 | defCallResult = CallWindowProcA((WNDPROC)(currObject->origProcPtr), hwnd, msg, wParam, lParam); 1565 | 1566 | if (!currObject->paintContext && currObject->handle) 1567 | currObject->paintContext = GetDC(currObject->handle); 1568 | if (!currObject->paintContext) 1569 | currObject->paintContext = BeginPaint(currObject->handle, &(currObject->paintData)); 1570 | } 1571 | break; 1572 | 1573 | case WM_ERASEBKGND: 1574 | if (currObject->customEraseBG) 1575 | return (LRESULT)TRUE; 1576 | 1577 | /*case WM_CTLCOLORSTATIC: 1578 | return (LONG)GetStockObject(NULL_BRUSH);*/ 1579 | 1580 | case WM_COMMAND: 1581 | eventID = commandEventHandler(hwnd, wParam, lParam); 1582 | break; 1583 | 1584 | case WM_NOTIFY: 1585 | //TODO: add "notify" event handling! 1586 | break; 1587 | 1588 | case WM_GETMINMAXINFO: /* The window size or position limits are queried */ 1589 | if (currObject){ 1590 | ((MINMAXINFO*)lParam)->ptMaxSize.x = currObject->maxWidth; 1591 | ((MINMAXINFO*)lParam)->ptMaxSize.y = currObject->maxHeight; 1592 | ((MINMAXINFO*)lParam)->ptMaxTrackSize.x = currObject->maxWidth; 1593 | ((MINMAXINFO*)lParam)->ptMaxTrackSize.y = currObject->maxHeight; 1594 | ((MINMAXINFO*)lParam)->ptMinTrackSize.x = currObject->minWidth; 1595 | ((MINMAXINFO*)lParam)->ptMinTrackSize.y = currObject->minHeight; 1596 | } 1597 | break; 1598 | 1599 | case WM_WINDOWPOSCHANGED: /* The window size or position have just been changed */ 1600 | if (currObject->type == WINDOW) 1601 | refreshWindowSize((Window)currObject, lParam); 1602 | break; 1603 | 1604 | case WM_CLOSE: 1605 | DestroyWindow(hwnd); 1606 | break; 1607 | 1608 | case WM_DESTROY: 1609 | if (currObject && currObject->type == WINDOW) 1610 | PostQuitMessage(0); 1611 | break; 1612 | } 1613 | 1614 | if (currObject){ 1615 | eventID = handleEvents(currObject, msg, wParam, lParam); 1616 | 1617 | if (msg == WM_PAINT && currObject->handle) { 1618 | if (!ReleaseDC(currObject->handle, currObject->paintContext)) 1619 | EndPaint(currObject->handle, &(currObject->paintData)); 1620 | currObject->paintContext = NULL; 1621 | 1622 | return defCallResult; 1623 | } 1624 | } 1625 | 1626 | /* Call the window's events */ 1627 | if (currObject){ 1628 | if (eventID >= 0 && (UINT)eventID < currObject->numEvents) 1629 | interrupt = (currObject->events)[eventID].interrupt; 1630 | 1631 | if (!interrupt){ 1632 | if (currObject->type == WINDOW) 1633 | return DefWindowProcA(hwnd, msg, wParam, lParam); 1634 | else /* Resend the messages to the subclassed object's default window proc */ 1635 | return CallWindowProcA((WNDPROC)(currObject->origProcPtr), hwnd, msg, wParam, lParam); 1636 | } 1637 | 1638 | return 0; 1639 | } else 1640 | return DefWindowProcA(hwnd, msg, wParam, lParam); 1641 | } 1642 | 1643 | /* Flushes the current thread's message queue */ 1644 | void flushMessageQueue(){ 1645 | MSG msg; 1646 | 1647 | while (PeekMessageA(&msg,NULL,0,0,PM_REMOVE)) 1648 | GetMessageA(&msg, NULL, 0, 0); 1649 | } 1650 | 1651 | /* Display a window with the application's command line settings */ 1652 | BOOL displayWindow(Window mainWindow, int nCmdShow){ 1653 | MSG msg; 1654 | RECT clientRect; 1655 | 1656 | mainWindow->handle = CreateWindowExA(mainWindow->exStyles, mainWindow->className, 1657 | mainWindow->text, 1658 | mainWindow->styles, 1659 | mainWindow->x, mainWindow->y, 1660 | mainWindow->width, 1661 | mainWindow->height, 1662 | NULL, NULL, 1663 | mainWindow->moduleInstance, NULL); 1664 | 1665 | if (!mainWindow->handle) 1666 | return FALSE; 1667 | 1668 | /* Set the window handle's additional data to a pointer to its object */ 1669 | SetWindowLongPtrA(mainWindow->handle, GWLP_USERDATA, (LONG)(mainWindow)); 1670 | 1671 | if (GetClientRect(mainWindow->handle, &clientRect)){ 1672 | mainWindow->clientWidth = clientRect.right - clientRect.left; 1673 | mainWindow->clientHeight = clientRect.bottom - clientRect.top; 1674 | } 1675 | 1676 | displayChildren((GUIObject)mainWindow); 1677 | 1678 | ShowWindow(mainWindow->handle, nCmdShow); 1679 | UpdateWindow(mainWindow->handle); 1680 | EnableWindow(mainWindow->handle, mainWindow->enabled); 1681 | 1682 | /* The message loop */ 1683 | while(GetMessageA(&msg, NULL, 0, 0) > 0){ 1684 | TranslateMessage(&msg); 1685 | DispatchMessageA(&msg); 1686 | } 1687 | 1688 | return msg.wParam; 1689 | } -------------------------------------------------------------------------------- /tinyGUI/tinyGUI.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Set the visual styles for code compiling in Visual Studio */ 7 | #ifdef _MSC_VER 8 | #pragma comment(linker,"\"/manifestdependency:type='win32' \ 9 | name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ 10 | processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 11 | #endif 12 | 13 | #define _CRT_SECURE_NO_WARNINGS 14 | #include 15 | #include 16 | #include 17 | 18 | /* Static assertion - produces error with a message at compile time */ 19 | #define STATIC_ASSERT(condition, message) extern char STATIC_ASSERTION__##message[1]; \ 20 | extern char STATIC_ASSERTION__##message[(condition)? 1 : 2] 21 | 22 | /* tinyObject utility macros */ 23 | #define MAKE_FIELD(type, name, val) type name 24 | #define DEFINE_FIELD(type, name, val) 25 | #define INIT_FIELD(type, name, val) thisObject->name = (type)val 26 | 27 | #define PRIVATE static 28 | 29 | #define METHOD(classType, type, name, args) type classType##_##name args 30 | 31 | #define MAKE_VIRTUAL_METHOD(classType, type, name, args) type (*name)args 32 | #define INIT_VIRTUAL_METHOD(classType, type, name, args) thisObject->name = &classType##_##name 33 | 34 | /* Determine if the compiler uses the regular struct member alignment logic, otherwise, inheritance won't work */ 35 | struct _ASSERTION_TEST_STRUCT { char a; int b; }; 36 | struct _ASSERTION_TEST_STRUCT2 { char a; int b; long c[100000]; long d[100000]; }; 37 | STATIC_ASSERT(offsetof(struct _ASSERTION_TEST_STRUCT, b) == offsetof(struct _ASSERTION_TEST_STRUCT2, b), 38 | This_compiler_follows_an_irregular_logic_in_struct_member_alignment_and_cannot_be_used_to_compile_tinyObject); 39 | 40 | #define MAKE_THIS(type) type thisObject 41 | #define ALLOC_THIS(type) struct type##_s *thisObject = (struct type##_s*)malloc(sizeof(struct type##_s)) 42 | #define this thisObject 43 | 44 | #define MAKE_TYPEDEF(type) \ 45 | typedef struct type##_s val_##type, *type; \ 46 | 47 | #define MAKE_CLASS(type) \ 48 | struct type##_s { CLASS_##type }; \ 49 | void delete##type (struct type##_s *thisObject) 50 | 51 | #define MAKE_CLASS_TYPEDEFED(type) \ 52 | MAKE_TYPEDEF(type); \ 53 | MAKE_CLASS(type); 54 | 55 | #define MAKE_DEFAULT_CONSTRUCTOR_PROTOTYPES(type) \ 56 | struct type##_s *new##type(); \ 57 | void init##type(struct type##_s *thisObject); 58 | 59 | #define MAKE_DEFAULT_INITIALIZER(type) void init##type(struct type##_s *thisObject){ CLASS_##type } 60 | 61 | #define MAKE_DEFAULT_CONSTRUCTOR(type) \ 62 | struct type##_s *new##type(){ \ 63 | struct type##_s *thisObject = (struct type##_s*)malloc(sizeof(struct type##_s)); \ 64 | if (!thisObject) return NULL; \ 65 | CLASS_##type \ 66 | return thisObject; \ 67 | } 68 | 69 | #define MAKE_DEFAULT_CONSTRUCTORS(type) \ 70 | MAKE_DEFAULT_INITIALIZER(type) \ 71 | MAKE_DEFAULT_CONSTRUCTOR(type) 72 | 73 | #define MAKE_DEFAULT_DESTRUCTOR(type) void delete##type(struct type##_s *thisObject){ free(thisObject); } 74 | 75 | #define MAKE_DEFAULT_DESTRUCTOR_PROTOTYPE(type) void delete##type(struct type##_s *thisObject); 76 | 77 | #define SELFREF_INIT void *currThis 78 | #define $(object) (((currThis = (void*)(object)), object) 79 | #define _(object) ((object) 80 | 81 | #define CURR_THIS(type) (struct type##_s*)currThis 82 | #define MAKE_METHOD_ALIAS(type, method) , type##_##method ) 83 | #define MAKE_VIRTUAL_METHOD_ALIAS(method) ->method ) 84 | 85 | 86 | 87 | 88 | /* tinyGUI utility macros */ 89 | 90 | /* Anchor macros */ 91 | #define ANCHOR_LEFT 0xF000 92 | #define ANCHOR_RIGHT 0x000F 93 | #define ANCHOR_TOP 0x0F00 94 | #define ANCHOR_BOTTOM 0x00F0 95 | 96 | /* Color macros */ 97 | #define COLOR_RED RGB(0xFF, 0, 0) 98 | #define COLOR_GREEN RGB(0, 0xFF, 0) 99 | #define COLOR_BLUE RGB(0, 0, 0xFF) 100 | 101 | /* Synchronize access to current object */ 102 | #define startSync(object) EnterCriticalSection(&(object->criticalSection)) 103 | #define endSync(object) LeaveCriticalSection(&(object->criticalSection)) 104 | 105 | 106 | 107 | /* Datatypes */ 108 | 109 | /* The object types */ 110 | enum _objectType { 111 | OBJECT, 112 | GUIOBJECT, 113 | /* Window types */ 114 | WINDOW, 115 | DIALOG, 116 | /* Control types */ 117 | CONTROL, 118 | BUTTON, 119 | LABEL, 120 | TEXTBOX, 121 | /* Event arg types */ 122 | EVENTARGS, 123 | MOUSEEVENTARGS, 124 | /* GDI types */ 125 | PEN, 126 | BRUSH 127 | }; 128 | 129 | /* An event sync mode */ 130 | enum _syncMode { 131 | SYNC, 132 | ASYNC 133 | }; 134 | 135 | /* TextBox multiline/singleline */ 136 | enum _textboxtype { 137 | MULTILINE = TRUE, 138 | SINGLELINE = FALSE 139 | }; 140 | 141 | /* An event */ 142 | typedef struct _event Event; 143 | 144 | 145 | 146 | MAKE_TYPEDEF(Object); 147 | MAKE_TYPEDEF(EventArgs); 148 | MAKE_TYPEDEF(MouseEventArgs); 149 | MAKE_TYPEDEF(GUIObject); 150 | MAKE_TYPEDEF(Window); 151 | MAKE_TYPEDEF(Control); 152 | MAKE_TYPEDEF(Button); 153 | MAKE_TYPEDEF(TextBox); 154 | MAKE_TYPEDEF(Label); 155 | MAKE_TYPEDEF(Pen); 156 | MAKE_TYPEDEF(Brush); 157 | 158 | 159 | 160 | /* Class Object */ 161 | #define CLASS_Object \ 162 | FIELD(enum _objectType, type, OBJECT); \ 163 | DEF_FIELD(CRITICAL_SECTION, criticalSection); \ 164 | FIELD(BOOL, criticalSectionInitialized, FALSE); 165 | 166 | 167 | 168 | /* Class EventArgs */ 169 | #define CLASS_EventArgs /* inherits from */ CLASS_Object \ 170 | DEF_FIELD(UINT, message); \ 171 | DEF_FIELD(WPARAM, wParam); \ 172 | DEF_FIELD(LPARAM, lParam); \ 173 | DEF_FIELD(void, (*updateValue)(MAKE_THIS(EventArgs), UINT message, WPARAM wParam, LPARAM lParam)); 174 | 175 | 176 | /* Class MouseEventArgs */ 177 | #define CLASS_MouseEventArgs /* inherits from */ CLASS_EventArgs \ 178 | FIELD(int, cursorX, NULL); \ 179 | FIELD(int, cursorY, NULL); 180 | 181 | 182 | /* Class GUIObject */ 183 | #define CLASS_GUIObject /* inherits from */ CLASS_Object \ 184 | FIELD(LONG_PTR, origProcPtr, NULL); /* The pointer to the original window procedure */ \ 185 | \ 186 | FIELD(HWND, handle, NULL); /* The handle to the window/control; initialized with a call to CreateWindowEx */ \ 187 | FIELD(HINSTANCE, moduleInstance, NULL); /* The current module instance */ \ 188 | DEF_FIELD(PAINTSTRUCT, paintData); \ 189 | FIELD(HDC, paintContext, NULL); \ 190 | FIELD(HDC, offscreenPaintContext, NULL); \ 191 | FIELD(HBITMAP, offscreenBitmap, NULL); \ 192 | FIELD(BOOL, customEraseBG, FALSE); \ 193 | \ 194 | FIELD(char*, className, NULL); /* The name of the window/control's WinAPI "class" */ \ 195 | FIELD(HMENU, ID, 0); /* The child-window/control identifier */ \ 196 | FIELD(DWORD, styles, 0); /* The window/control styles */ \ 197 | FIELD(DWORD, exStyles, 0); /* The window/control extended styles */ \ 198 | \ 199 | /* events */ \ 200 | FIELD(struct _event*, events, NULL); \ 201 | FIELD(unsigned int, numEvents, 0); \ 202 | \ 203 | FIELD(char*, text, NULL); /* The window/control text */ \ 204 | \ 205 | FIELD(int, width, 0); \ 206 | FIELD(int, height, 0); \ 207 | \ 208 | FIELD(int, minWidth, 0); \ 209 | FIELD(int, minHeight, 0); \ 210 | \ 211 | FIELD(int, maxWidth, INT_MAX); \ 212 | FIELD(int, maxHeight, INT_MAX); \ 213 | \ 214 | FIELD(int, realWidth, 0); /* Used in anchor calculations. Not affected by min and max settings */ \ 215 | FIELD(int, realHeight, 0); \ 216 | \ 217 | FIELD(int, x, CW_USEDEFAULT); \ 218 | FIELD(int, y, CW_USEDEFAULT); \ 219 | \ 220 | FIELD(int, realX, CW_USEDEFAULT); /* Used in anchor calculations. Not affected by min and max settings */ \ 221 | FIELD(int, realY, CW_USEDEFAULT); \ 222 | \ 223 | FIELD(BOOL, enabled, TRUE); \ 224 | \ 225 | /* links */ \ 226 | FIELD(GUIObject, parent, NULL); /* A pointer to the parent */ \ 227 | FIELD(GUIObject*, children, NULL); /* A pointer to the array of children */ \ 228 | FIELD(unsigned int, numChildren, 0); /* The number of children */ \ 229 | \ 230 | /* Moves a GUIObject to a new location specified by x and y */ \ 231 | VIRTUAL_METHOD(GUIObject, BOOL, setPos, (MAKE_THIS(GUIObject), int x, int y)); 232 | 233 | /* Adds a child object to a GUIObject */ 234 | METHOD(GUIObject, BOOL, addChild, (MAKE_THIS(GUIObject), struct GUIObject_s *child)); 235 | /* Removes a child object from a GUIObject */ 236 | METHOD(GUIObject, BOOL, removeChild, (MAKE_THIS(GUIObject), struct GUIObject_s *child)); 237 | /* Sets an event for a GUIObject by a Windows message */ 238 | METHOD(GUIObject, int, setEvent, (MAKE_THIS(GUIObject), DWORD message, void(*callback)(GUIObject, void*, struct EventArgs_s*), 239 | void *context, enum _syncMode mode)); 240 | /* Sets a condition for an event of a GUIObject */ 241 | METHOD(GUIObject, BOOL, setEventCondition, (MAKE_THIS(GUIObject), int eventID, BOOL *condition)); 242 | /* Sets an interrupt state for an event of a GUIObject */ 243 | METHOD(GUIObject, BOOL, setEventInterrupt, (MAKE_THIS(GUIObject), int eventID, BOOL interrupt)); 244 | /* Changes an event's enabled state for a GUIObject */ 245 | METHOD(GUIObject, BOOL, setEventEnabled, (MAKE_THIS(GUIObject), int eventID, BOOL enabled)); 246 | /* Sets a WM_LBUTTONUP event for an (enabled) object */ 247 | METHOD(GUIObject, int, setOnClick, (MAKE_THIS(GUIObject), void(*callback)(GUIObject, void*, struct EventArgs_s*), void *context, 248 | enum _syncMode mode)); 249 | /* Resizes a GUIObject to a new size specified by width and height */ 250 | METHOD(GUIObject, BOOL, setSize, (MAKE_THIS(GUIObject), int width, int height)); 251 | /* Sets a GUIObject's minimum size to a new value specified by minWidth and minHeight */ 252 | METHOD(GUIObject, BOOL, setMinSize, (MAKE_THIS(GUIObject), int minWidth, int minHeight)); 253 | /* Sets a GUIObject's maximum size to a new value specified by maxWidth and maxHeight */ 254 | METHOD(GUIObject, BOOL, setMaxSize, (MAKE_THIS(GUIObject), int maxWidth, int maxHeight)); 255 | /* Sets a new text for a GUIObject */ 256 | METHOD(GUIObject, BOOL, setText, (MAKE_THIS(GUIObject), char *text)); 257 | /* Sets a GUIObject's enabled state */ 258 | METHOD(GUIObject, BOOL, setEnabled, (MAKE_THIS(GUIObject), BOOL enabled)); 259 | /* Draws a line in a GUIObject from the point specified by x1, y1 to the point specified by x2, y2 */ 260 | METHOD(GUIObject, BOOL, drawLine, (MAKE_THIS(GUIObject), Pen pen, int x1, int y1, int x2, int y2)); 261 | /* Draws an arc in a GUIObject */ 262 | METHOD(GUIObject, BOOL, drawArc, (MAKE_THIS(GUIObject), Pen pen, int boundX1, int boundY1, int boundX2, int boundY2, 263 | int x1, int y1, int x2, int y2)); 264 | /* Draws a rectangle in a GUIObject */ 265 | METHOD(GUIObject, BOOL, drawRect, (MAKE_THIS(GUIObject), Pen pen, Brush brush, int boundX1, int boundY1, 266 | int boundX2, int boundY2)); 267 | /* Draws a rounded rectangle in a GUIObject */ 268 | METHOD(GUIObject, BOOL, drawRoundedRect, (MAKE_THIS(GUIObject), Pen pen, Brush brush, int boundX1, int boundY1, 269 | int boundX2, int boundY2, int ellipseWidth, int ellipseHeight)); 270 | /* Draws an ellipse in a GUIObject */ 271 | METHOD(GUIObject, BOOL, drawEllipse, (MAKE_THIS(GUIObject), Pen pen, Brush brush, int boundX1, int boundY1, 272 | int boundX2, int boundY2)); 273 | /* Draws a polygon in a GUIObject */ 274 | METHOD(GUIObject, BOOL, drawPolygon, (MAKE_THIS(GUIObject), Pen pen, Brush brush, int numPoints, LONG *coords)); 275 | 276 | /* Virtual method prototype */ 277 | BOOL GUIObject_setPos(GUIObject object, int x, int y); 278 | 279 | /* Self-reference mechanism for methods */ 280 | /* Moves a GUIObject to a new location specified by x and y */ 281 | #define _setPos(x, y) MAKE_VIRTUAL_METHOD_ALIAS(setPos(CURR_THIS(GUIObject), x, y)) 282 | 283 | #define _addChild(child) MAKE_METHOD_ALIAS(GUIObject, addChild(CURR_THIS(GUIObject), child)) 284 | /* Removes a child object from a GUIObject */ 285 | #define _removeChild(child) MAKE_METHOD_ALIAS(GUIObject, removeChild(CURR_THIS(GUIObject), child)) 286 | /* Sets an event for a GUIObject by a Windows message */ 287 | #define _setEvent(message, callback, context, mode) MAKE_METHOD_ALIAS(GUIObject, setEvent(CURR_THIS(GUIObject), message, callback, context, mode)) 288 | /* Sets a condition for an event of a GUIObject */ 289 | #define _setEventCondition(object, eventID, condition) MAKE_METHOD_ALIAS(GUIObject, setEventCondition(CURR_THIS(GUIObject), eventID, condition)) 290 | /* Sets an interrupt state for an event of a GUIObject */ 291 | #define _setEventInterrupt(eventID, interrupt) MAKE_METHOD_ALIAS(GUIObject, setEventInterrupt(CURR_THIS(GUIObject), eventID, interrupt)) 292 | /* Changes an event's enabled state for a GUIObject */ 293 | #define _setEventEnabled(eventID, enabled) MAKE_METHOD_ALIAS(GUIObject, setEventEnabled(CURR_THIS(GUIObject), eventID, enabled)) 294 | /* Sets a WM_LBUTTONUP event for an (enabled) object */ 295 | #define _setOnClick(callback, context, mode) MAKE_METHOD_ALIAS(GUIObject, setOnClick(CURR_THIS(GUIObject), callback, context, mode)) 296 | /* Resizes a GUIObject to a new size specified by width and height */ 297 | #define _setSize(width, height) MAKE_METHOD_ALIAS(GUIObject, setSize(CURR_THIS(GUIObject), width, height)) 298 | /* Sets a GUIObject's minimum size to a new value specified by minWidth and minHeight */ 299 | #define _setMinSize(minWidth, minHeight) MAKE_METHOD_ALIAS(GUIObject, setMinSize(CURR_THIS(GUIObject), minWidth, minHeight)) 300 | /* Sets a GUIObject's maximum size to a new value specified by maxWidth and maxHeight */ 301 | #define _setMaxSize(maxWidth, maxHeight) MAKE_METHOD_ALIAS(GUIObject, setMaxSize(CURR_THIS(GUIObject), maxWidth, maxHeight)) 302 | /* Sets a new text for a GUIObject */ 303 | #define _setText(text) MAKE_METHOD_ALIAS(GUIObject, setText(CURR_THIS(GUIObject), text)) 304 | /* Sets a GUIObject's enabled state */ 305 | #define _setEnabled(enabled) MAKE_METHOD_ALIAS(GUIObject, setEnabled(CURR_THIS(GUIObject), enabled)) 306 | /* Draws a line in a GUIObject from the pospecified by x1, y1 to the pospecified by x2, y2 */ 307 | #define _drawLine(pen, x1, y1, x2, y2) MAKE_METHOD_ALIAS(GUIObject, drawLine(CURR_THIS(GUIObject), pen, x1, y1, x2, y2)) 308 | /* Draws an arc in a GUIObject */ 309 | #define _drawArc(pen, boundX1, boundY1, boundX2, boundY2, x1, y1, x2, y2) MAKE_METHOD_ALIAS(GUIObject, \ 310 | drawArc(CURR_THIS(GUIObject), pen, boundX1, boundY1, boundX2, boundY2, x1, y1, x2, y2)) 311 | /* Draws a rectangle in a GUIObject */ 312 | #define _drawRect(pen, brush, boundX1, boundY1, boundX2, boundY2) MAKE_METHOD_ALIAS(GUIObject, \ 313 | drawRect(CURR_THIS(GUIObject), pen, brush, boundX1, boundY1, boundX2, boundY2)) 314 | /* Draws a rounded rectangle in a GUIObject */ 315 | #define _drawRoundedRect(pen, brush, boundX1, boundY1, boundX2, boundY2, ellipseWidth, ellipseHeight) MAKE_METHOD_ALIAS(GUIObject, \ 316 | drawRoundedRect(CURR_THIS(GUIObject), pen, brush, boundX1, boundY1, boundX2, boundY2, ellipseWidth, ellipseHeight) 317 | /* Draws an ellipse in a GUIObject */ 318 | #define _drawEllipse(pen, brush, boundX1, boundY1, boundX2, boundY2) MAKE_METHOD_ALIAS(GUIObject, \ 319 | drawEllipse(CURR_THIS(GUIObject), pen, brush, boundX1, boundY1, boundX2, boundY2)) 320 | /* Draws a polygon in a GUIObject */ 321 | #define _drawPolygon(pen, brush, numPoints, coords) MAKE_METHOD_ALIAS(GUIObject, \ 322 | drawPolygon(CURR_THIS(GUIObject), pen, brush, numPoints, coords)) 323 | 324 | 325 | 326 | /* Class Window */ 327 | #define CLASS_Window /* inherits from */ CLASS_GUIObject \ 328 | /* fields */ \ 329 | FIELD(int, clientWidth, 0); \ 330 | FIELD(int, clientHeight, 0); \ 331 | \ 332 | FIELD(BOOL, resizable, TRUE); \ 333 | FIELD(BOOL, maximizeEnabled, TRUE); 334 | 335 | /* methods */ 336 | METHOD(Window, BOOL, setResizable, (MAKE_THIS(Window), BOOL resizable)); 337 | METHOD(Window, BOOL, enableMaximize, (MAKE_THIS(Window), BOOL maximize)); 338 | 339 | /* Self-reference mechanism for methods */ 340 | #define _setResizable(resizable) MAKE_METHOD_ALIAS(Window, setResizable(CURR_THIS(Window), resizable)) 341 | #define _enableMaximize(maximize) MAKE_METHOD_ALIAS(Window, enableMaximize(CURR_THIS(Window), maximize)) 342 | 343 | 344 | 345 | /* Class Control */ 346 | #define CLASS_Control /* inherits from */ CLASS_GUIObject \ 347 | /* fields */ \ 348 | FIELD(int, minX, INT_MIN); \ 349 | FIELD(int, minY, INT_MIN); \ 350 | \ 351 | FIELD(int, maxX, INT_MAX); \ 352 | FIELD(int, maxY, INT_MAX); \ 353 | \ 354 | FIELD(short, anchor, ANCHOR_TOP | ANCHOR_LEFT); 355 | 356 | 357 | /* methods */ 358 | METHOD(Control, BOOL, setMinPos, (MAKE_THIS(Control), int minX, int minY)); 359 | METHOD(Control, BOOL, setMaxPos, (MAKE_THIS(Control), int maxX, int maxY)); 360 | 361 | /* Self-reference mechanism for methods */ 362 | #define _setMinPos(minX, minY) MAKE_METHOD_ALIAS(Control, setMinPos(CURR_THIS(Control), minX, minY)) 363 | #define _setMaxPos(maxX, maxY) MAKE_METHOD_ALIAS(Control, setMaxPos(CURR_THIS(Control), maxX, maxY)) 364 | 365 | 366 | 367 | /* Class Button */ 368 | #define CLASS_Button /* inherits from */ CLASS_Control 369 | 370 | 371 | 372 | /* Class TextBox */ 373 | #define CLASS_TextBox /* inherits from */ CLASS_Control \ 374 | FIELD(BOOL, multiline, FALSE); \ 375 | FIELD(BOOL, numOnly, FALSE); 376 | 377 | METHOD(TextBox, BOOL, setNumOnly, (MAKE_THIS(TextBox), BOOL numOnly)); 378 | 379 | #define _setNumOnly(numOnly) MAKE_METHOD_ALIAS(TextBox, setNumOnly(CURR_THIS(TextBox), numOnly)) 380 | 381 | 382 | 383 | /* Class Label */ 384 | #define CLASS_Label /* inherits from */ CLASS_Control 385 | 386 | 387 | 388 | /* Class Pen */ 389 | #define CLASS_Pen /* inherits from */ CLASS_Object \ 390 | FIELD(HPEN, handle, NULL); \ 391 | FIELD(int, penStyle, 0); \ 392 | FIELD(int, width, 0); \ 393 | FIELD(COLORREF, color, 0); 394 | 395 | #define _setPenStyle(penStyle) , initPen(CURR_THIS(Pen), penStyle, (CURR_THIS(Pen))->width, (CURR_THIS(Pen))->color)) 396 | #define _setPenWidth(width) , initPen(CURR_THIS(Pen), (CURR_THIS(Pen))->penStyle, width, (CURR_THIS(Pen))->color)) 397 | #define _setPenColor(color) , initPen(CURR_THIS(Pen), (CURR_THIS(Pen))->penStyle, (CURR_THIS(Pen))->width, color)) 398 | 399 | 400 | 401 | /* Class Brush */ 402 | #define CLASS_Brush /* inherits from */ CLASS_Object \ 403 | FIELD(HBRUSH, handle, NULL); \ 404 | FIELD(UINT, brushStyle, 0); \ 405 | FIELD(COLORREF, color, 0); \ 406 | FIELD(ULONG_PTR, hatch, NULL); 407 | 408 | #define _setBrushStyle(brushStyle) , initBrush(CURR_THIS(Brush), brushStyle, (CURR_THIS(Pen))->color, (CURR_THIS(Brush))->hatch)) 409 | #define _setBrushColor(color) , initBrush(CURR_THIS(Brush), (CURR_THIS(Brush))->brushStyle, color, (CURR_THIS(Brush))->hatch)) 410 | #define _setBrushHatch(hatch) , initBrush(CURR_THIS(Brush), (CURR_THIS(Brush))->brushStyle, (CURR_THIS(Brush))->color, hatch)) 411 | 412 | 413 | 414 | 415 | 416 | /* Make the classes */ 417 | #define FIELD(type, name, val) MAKE_FIELD(type, name, val) 418 | #define DEF_FIELD(type, name) MAKE_FIELD(type, name, ) 419 | #define VIRTUAL_METHOD(classType, type, name, args) MAKE_VIRTUAL_METHOD(classType, type, name, args) 420 | 421 | MAKE_CLASS(Object); 422 | MAKE_DEFAULT_CONSTRUCTOR_PROTOTYPES(Object); 423 | 424 | MAKE_CLASS(EventArgs); 425 | void initEventArgs(EventArgs thisObject, UINT message, WPARAM wParam, LPARAM lParam); 426 | EventArgs newEventArgs(UINT message, WPARAM wParam, LPARAM lParam); 427 | 428 | MAKE_CLASS(MouseEventArgs); 429 | void initMouseEventArgs(MouseEventArgs thisObject, UINT message, WPARAM wParam, LPARAM lParam); 430 | MouseEventArgs newMouseEventArgs(UINT message, WPARAM wParam, LPARAM lParam); 431 | 432 | MAKE_CLASS(Pen); 433 | void initPen(Pen thisObject, int penStyle, int width, COLORREF color); 434 | Pen newPen(int penStyle, int width, COLORREF color); 435 | 436 | MAKE_CLASS(Brush); 437 | void initBrush(Brush thisObject, UINT brushStyle, COLORREF color, ULONG_PTR hatch); 438 | Brush newBrush(UINT brushStyle, COLORREF color, ULONG_PTR hatch); 439 | 440 | MAKE_CLASS(GUIObject); 441 | void initGUIObject(GUIObject thisObject, HINSTANCE instance, char *text, int width, int height); 442 | GUIObject newGUIObject(HINSTANCE instance, char *text, int width, int height); 443 | 444 | MAKE_CLASS(Window); 445 | void initWindow(Window thisObject, HINSTANCE instance, char *text, int width, int height); 446 | Window newWindow(HINSTANCE instance, char *text, int width, int height); 447 | 448 | MAKE_CLASS(Control); 449 | void initControl(Control thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height); 450 | Control newControl(HINSTANCE instance, char *text, int x, int y, int width, int height); 451 | 452 | MAKE_CLASS(Button); 453 | void initButton(Button thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height); 454 | Button newButton(HINSTANCE instance, char *text, int x, int y, int width, int height); 455 | 456 | MAKE_CLASS(TextBox); 457 | void initTextBox(TextBox thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height, enum _textboxtype multiline); 458 | TextBox newTextBox(HINSTANCE instance, char *text, int x, int y, int width, int height, enum _textboxtype multiline); 459 | 460 | MAKE_CLASS(Label); 461 | void initLabel(Label thisObject, HINSTANCE instance, char *text, int x, int y, int width, int height); 462 | Label newLabel(HINSTANCE instance, char *text, int x, int y, int width, int height); 463 | 464 | 465 | #undef FIELD 466 | #undef DEF_FIELD 467 | #undef VIRTUAL_METHOD 468 | 469 | 470 | struct _event { 471 | void (*eventFunction)(GUIObject, void*, EventArgs); /* The callback */ 472 | enum _syncMode mode; /* The sync mode */ 473 | UINT message; /* The message ID */ 474 | GUIObject sender; /* The sender object */ 475 | void *context; /* A pointer to data that gets sent on every event */ 476 | EventArgs args; /* The event args */ 477 | BOOL *condition; /* A pointer to a variable that determines if the event should be handled */ 478 | BOOL interrupt; /* If this is set to TRUE, the default handling for the event doesn't occur */ 479 | BOOL enabled; /* The event's enabled status */ 480 | }; 481 | 482 | 483 | /* An event handler callback type */ 484 | typedef void(*Callback)(GUIObject, void*, EventArgs); 485 | 486 | 487 | 488 | /* "static" function prototypes */ 489 | static void *getCurrentThis(); 490 | void setCurrentThis(void *self); 491 | 492 | void freeGUIObjectFields(GUIObject object); 493 | 494 | void flushMessageQueue(); 495 | 496 | BOOL displayControl(Control control); 497 | BOOL displayWindow(Window mainWindow, int nCmdShow); --------------------------------------------------------------------------------