diff --git a/make/msvc/Make-vs-project.r3 b/make/msvc/Make-vs-project.r3 index 9691cefa87..fc35718cfb 100644 --- a/make/msvc/Make-vs-project.r3 +++ b/make/msvc/Make-vs-project.r3 @@ -20,7 +20,11 @@ VS: context [ SourcePath: "" - AdditionalDependencies: "wsock32.lib;comdlg32.lib;" + AdditionalDependencies: rejoin [ + "wsock32.lib;comdlg32.lib;" + "winmm.lib;" ;- for MIDI + "Gdi32.lib;Comctl32.lib;UxTheme.lib;" ;- for View + ] Sources: [] @@ -448,8 +452,20 @@ vs/Sources: compose/only [ ] vs/IncludePath-x86: vs/IncludePath-x64: "..\..\..\src\include;" -vs/PreprocessorDefinitions-x86: {TO_WIN32;REB_CORE;REB_EXE;ENDIAN_LITTLE;_FILE_OFFSET_BITS=64;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;USE_LZMA;} -vs/PreprocessorDefinitions-x64: {UNICODE;_UNICODE;ENDIAN_LITTLE;_CRT_SECURE_NO_WARNINGS;USE_MIDI_DEVICE;USE_PNG_CODEC;USE_JPG_CODEC;USE_LZMA;REB_EXE;TEST_EXTENSIONS;_DEBUG;NO_COMPOSITOR;TO_WIN32_X64;__LLP64__;_FILE_OFFSET_BITS=64;} + +common-definitions: {REB_EXE;ENDIAN_LITTLE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;_FILE_OFFSET_BITS=64;} +optional-components: {USE_MIDI_DEVICE;USE_PNG_CODEC;USE_JPG_CODEC;USE_LZMA;} ;TEST_EXTENSIONS; +vs/PreprocessorDefinitions-x86: rejoin [ + common-definitions + {TO_WIN32;} + optional-components +] +vs/PreprocessorDefinitions-x64: rejoin [ + common-definitions + {TO_WIN32_X64;__LLP64__;} + optional-components +] + vs/Prebuild-x86: { set REBOL=..\..\prebuild\r3-make-win.exe set T=../../../src/tools diff --git a/make/r3.manifest b/make/r3.manifest index d3bab1b4a7..61862e5054 100644 --- a/make/r3.manifest +++ b/make/r3.manifest @@ -12,4 +12,16 @@ True/PM + + + + + \ No newline at end of file diff --git a/make/r3.rc b/make/r3.rc index 4f67080d99..9ad703e172 100644 --- a/make/r3.rc +++ b/make/r3.rc @@ -28,5 +28,5 @@ BEGIN 101 "Rebol 3 (Oldes branch)" END -2 MANIFEST "r3.manifest" +1 MANIFEST "r3.manifest" diff --git a/src/boot/sysobj.r b/src/boot/sysobj.r index a120cefb3a..ffb0c79be0 100644 --- a/src/boot/sysobj.r +++ b/src/boot/sysobj.r @@ -394,6 +394,12 @@ view: context [ scroll-page drop-file + + click + change + focus + unfocus + scroll ] event-keys: [ ; Event types. Order dependent for C and REBOL. diff --git a/src/boot/window.r b/src/boot/window.r index 804e3733e4..21d7e111fe 100644 --- a/src/boot/window.r +++ b/src/boot/window.r @@ -19,7 +19,20 @@ REBOL [ ] words: [ - ;gui-metric + ;- widgets + button + check + radio + field + area + text + text-list + progress + slider + date-time + group-box + + ;- gui-metric border-fixed border-size screen-size diff --git a/src/core/a-lib.c b/src/core/a-lib.c index 8b288ba1c8..e1f2bf664c 100644 --- a/src/core/a-lib.c +++ b/src/core/a-lib.c @@ -545,6 +545,23 @@ RL_API void *RL_Make_Block(u32 size) return Make_Block(size); } +RL_API void RL_Expand_Series(REBSER *series, REBCNT index, REBCNT delta) +/* +** Expand a series at a particular index point by the number +** number of units specified by delta. +** +** Returns: +** +** Arguments: +** series - series to expand +** index - position where to expand +** delta - number of UNITS to expand from TAIL (keeping terminator) +*/ +{ + Expand_Series(series, index, delta); +} + + RL_API void *RL_Make_String(u32 size, int unicode) /* ** Allocate a new string or binary series. @@ -630,7 +647,7 @@ RL_API void RL_Protect_GC(REBSER *series, u32 flags) (flags == 1) ? SERIES_SET_FLAG(series, SER_KEEP) : SERIES_CLR_FLAG(series, SER_KEEP); } -RL_API int RL_Get_String(REBSER *series, u32 index, void **str) +RL_API int RL_Get_String(REBSER *series, u32 index, void **str, REBOOL needs_wide) /* ** Obtain a pointer into a string (bytes or unicode). ** @@ -641,6 +658,7 @@ RL_API int RL_Get_String(REBSER *series, u32 index, void **str) ** series - string series pointer ** index - index from beginning (zero-based) ** str - pointer to first character +** needs_wide - unicode string is required, converts if needed ** Notes: ** If the len is less than zero, then the string is optimized to ** codepoints (chars) 255 or less for ASCII and LATIN-1 charsets. @@ -651,13 +669,14 @@ RL_API int RL_Get_String(REBSER *series, u32 index, void **str) int len = (index >= series->tail) ? 0 : series->tail - index; if (BYTE_SIZE(series)) { - *str = BIN_SKIP(series, index); - len = -len; - } - else { - *str = UNI_SKIP(series, index); + if (needs_wide) { + Widen_String(series); + } else { + *str = BIN_SKIP(series, index); + return -len; + } } - + *str = UNI_SKIP(series, index); return len; } diff --git a/src/core/t-gob.c b/src/core/t-gob.c index d49cb46212..d2174f1e84 100644 --- a/src/core/t-gob.c +++ b/src/core/t-gob.c @@ -324,6 +324,9 @@ const REBCNT Gob_Flag_Words[] = { /* ***********************************************************************/ { + REBVAL *spec; + REBVAL *hndl; + switch (VAL_WORD_CANON(word)) { case SYM_OFFSET: return Set_Pair(&(gob->offset), val); @@ -343,6 +346,30 @@ const REBCNT Gob_Flag_Words[] = { else if (IS_NONE(val)) SET_GOB_TYPE(gob, GOBT_NONE); else return FALSE; break; +#ifdef HAS_WIDGET_GOB + case SYM_WIDGET: + //printf("WIDGET GOB\n"); + SET_GOB_TYPE(gob, GOBT_WIDGET); + SET_GOB_OPAQUE(gob); + + GOB_CONTENT(gob) = Make_Block(4); // [handle type spec data] + hndl = Append_Value(GOB_CONTENT(gob)); + Append_Value(GOB_CONTENT(gob)); // used to cache type on host's side + spec = Append_Value(GOB_CONTENT(gob)); + Append_Value(GOB_CONTENT(gob)); // used to cache result data + + SET_HANDLE(hndl, 0, SYM_WIDGET, 0); + + if (IS_WORD(val) || IS_LIT_WORD(val)) { + Set_Block(spec, Make_Block(1)); + Append_Val(VAL_SERIES(spec), val); + } + else if (IS_BLOCK(val)) { + Set_Block(spec, VAL_SERIES(val)); + } + else return FALSE; + break; +#endif // HAS_WIDGET_GOB case SYM_DRAW: CLR_GOB_OPAQUE(gob); @@ -383,7 +410,7 @@ const REBCNT Gob_Flag_Words[] = { if (IS_TUPLE(val)) { SET_GOB_TYPE(gob, GOBT_COLOR); Set_Pixel_Tuple((REBYTE*)&GOB_CONTENT(gob), val); - if (VAL_TUPLE_LEN(val) < 4 || VAL_TUPLE(val)[3] == 0) + if (VAL_TUPLE_LEN(val) < 4 || VAL_TUPLE(val)[3] == 255) SET_GOB_OPAQUE(gob); } else if (IS_NONE(val)) SET_GOB_TYPE(gob, GOBT_NONE); @@ -406,6 +433,11 @@ const REBCNT Gob_Flag_Words[] = { break; case SYM_DATA: +#ifdef HAS_WIDGET_GOB + if (GOB_TYPE(gob) == GOBT_WIDGET) { + OS_SET_WIDGET_DATA(gob, val); + } else { +#endif SET_GOB_DTYPE(gob, GOBD_NONE); if (IS_OBJECT(val)) { SET_GOB_DTYPE(gob, GOBD_OBJECT); @@ -430,6 +462,9 @@ const REBCNT Gob_Flag_Words[] = { else if (IS_NONE(val)) SET_GOB_TYPE(gob, GOBT_NONE); else return FALSE; +#ifdef HAS_WIDGET_GOB + } +#endif break; case SYM_FLAGS: @@ -462,6 +497,7 @@ const REBCNT Gob_Flag_Words[] = { /* ***********************************************************************/ { + REBSER *data; switch (VAL_WORD_CANON(word)) { case SYM_OFFSET: @@ -479,6 +515,14 @@ const REBCNT Gob_Flag_Words[] = { else goto is_none; break; +#ifdef HAS_WIDGET_GOB + case SYM_WIDGET: + data = VAL_SERIES(GOB_WIDGET_SPEC(gob)); + Init_Word(val, VAL_WORD_CANON(BLK_HEAD(data))); + VAL_SET(val, REB_LIT_WORD); + break; +#endif + case SYM_DRAW: if (GOB_TYPE(gob) == GOBT_DRAW) { Set_Block(val, GOB_CONTENT(gob)); // Note: compiler optimizes SET_BLOCKs below @@ -531,20 +575,27 @@ const REBCNT Gob_Flag_Words[] = { break; case SYM_DATA: +#ifdef HAS_WIDGET_GOB + if (GOB_TYPE(gob) == GOBT_WIDGET) { + return OS_GET_WIDGET_DATA(gob, val); + } +#endif + data = GOB_DATA(gob); + if (GOB_DTYPE(gob) == GOBD_OBJECT) { - SET_OBJECT(val, GOB_DATA(gob)); + SET_OBJECT(val, data); } else if (GOB_DTYPE(gob) == GOBD_BLOCK) { - Set_Block(val, GOB_DATA(gob)); + Set_Block(val, data); } else if (GOB_DTYPE(gob) == GOBD_STRING) { - Set_String(val, GOB_DATA(gob)); + Set_String(val, data); } else if (GOB_DTYPE(gob) == GOBD_BINARY) { - SET_BINARY(val, GOB_DATA(gob)); + SET_BINARY(val, data); } else if (GOB_DTYPE(gob) == GOBD_INTEGER) { - SET_INTEGER(val, (REBIPT)GOB_DATA(gob)); + SET_INTEGER(val, (REBIPT)data); } else goto is_none; break; @@ -591,23 +642,29 @@ const REBCNT Gob_Flag_Words[] = { { REBSER *ser = Make_Block(10); REBVAL *val; - REBINT words[6] = {SYM_OFFSET, SYM_SIZE, SYM_ALPHA, 0}; - REBVAL *vals[6]; - REBINT n = 0; REBVAL *val1; REBCNT sym; - for (n = 0; words[n]; n++) { + val = Append_Value(ser); + Init_Word(val, SYM_OFFSET); + VAL_SET(val, REB_SET_WORD); + val = Append_Value(ser); + SET_PAIR(val, GOB_X(gob), GOB_Y(gob)); + + val = Append_Value(ser); + Init_Word(val, SYM_SIZE); + VAL_SET(val, REB_SET_WORD); + val = Append_Value(ser); + SET_PAIR(val, GOB_W(gob), GOB_H(gob)); + + if (!GET_GOB_FLAG(gob, GOBF_OPAQUE) && GOB_ALPHA(gob) < 255) { val = Append_Value(ser); - Init_Word(val, words[n]); + Init_Word(val, SYM_ALPHA); VAL_SET(val, REB_SET_WORD); - vals[n] = Append_Value(ser); + val = Append_Value(ser); + SET_INTEGER(val, 255 - GOB_ALPHA(gob)); } - SET_PAIR(vals[0], GOB_X(gob), GOB_Y(gob)); - SET_PAIR(vals[1], GOB_W(gob), GOB_H(gob)); - SET_INTEGER(vals[2], GOB_ALPHA(gob)); - if (!GOB_TYPE(gob)) return ser; if (GOB_CONTENT(gob)) { @@ -620,6 +677,11 @@ const REBCNT Gob_Flag_Words[] = { case GOBT_IMAGE: sym = SYM_IMAGE; break; +#ifdef HAS_WIDGET_GOB + case GOBT_WIDGET: + sym = SYM_WIDGET; + break; +#endif case GOBT_STRING: case GOBT_TEXT: sym = SYM_TEXT; diff --git a/src/include/reb-config.h b/src/include/reb-config.h index face4f36dd..8bd160db4b 100644 --- a/src/include/reb-config.h +++ b/src/include/reb-config.h @@ -101,6 +101,7 @@ These are now obsolete (as of A107) and should be removed: #define OS_CRLF TRUE // uses CRLF as line terminator #define OS_DIR_SEP '\\' // file path separator (Thanks Bill.) #define HAS_ASYNC_DNS // supports it +#define HAS_WIDGET_GOB // supports it #define ATOI // supports it #define ATOI64 // supports it #define ITOA64 // supports it diff --git a/src/include/reb-gob.h b/src/include/reb-gob.h index 7386014111..be2432a58f 100644 --- a/src/include/reb-gob.h +++ b/src/include/reb-gob.h @@ -68,6 +68,7 @@ enum GOB_TYPES { // Types of content GOBT_NONE = 0, GOBT_COLOR, GOBT_IMAGE, + GOBT_WIDGET, // must be between IMAGE and STRING so GC can mark its content! GOBT_STRING, GOBT_DRAW, GOBT_TEXT, @@ -178,6 +179,11 @@ typedef struct gob_window { // Maps gob to window #define GOB_PARENT(g) ((g)->parent) #define GOB_CONTENT(g) ((g)->content) +#define GOB_WIDGET_HANDLE(g) (BLK_HEAD(GOB_CONTENT(g))) +#define GOB_WIDGET_TYPE(g) (BLK_SKIP(GOB_CONTENT(g), 1)) +#define GOB_WIDGET_SPEC(g) (BLK_SKIP(GOB_CONTENT(g), 2)) +#define GOB_WIDGET_DATA(g) (BLK_SKIP(GOB_CONTENT(g), 3)) + // Control dependencies on series structures: #ifdef REB_DEF #define GOB_STRING(g) SERIES_DATA(GOB_CONTENT(g)) diff --git a/src/include/sys-value.h b/src/include/sys-value.h index bdea7bc69f..08e4466bd1 100644 --- a/src/include/sys-value.h +++ b/src/include/sys-value.h @@ -536,6 +536,7 @@ typedef struct Reb_Series_Ref #define VAL_SERIES(v) ((v)->data.series.series) #define VAL_INDEX(v) ((v)->data.series.index) #define VAL_TAIL(v) (VAL_SERIES(v)->tail) +#define VAL_REST(v) (VAL_SERIES(v)->rest) #define VAL_LEN(v) (Val_Series_Len(v)) #define VAL_DATA(s) (VAL_BIN_HEAD(s) + (VAL_INDEX(s) * VAL_SERIES_WIDTH(s))) diff --git a/src/os/host-ext-test.c b/src/os/host-ext-test.c index d786c6d987..db34819e2f 100644 --- a/src/os/host-ext-test.c +++ b/src/os/host-ext-test.c @@ -201,7 +201,7 @@ RXIEXT int RX_Call(int cmd, RXIFRM *frm, void *ctx) { break; case 4: //command [{return word from string} str [string!]] - RL_GET_STRING(RXA_SERIES(frm, 1), 0, (void*)(&str)); // latin-1 only for test + RL_GET_STRING(RXA_SERIES(frm, 1), 0, (void*)(&str), FALSE); // latin-1 only for test RXA_WORD(frm, 1) = RL_MAP_WORD(str); RXA_TYPE(frm, 1) = RXT_WORD; break; diff --git a/src/os/host-main.c b/src/os/host-main.c index c64a7ca43e..2138e6769b 100644 --- a/src/os/host-main.c +++ b/src/os/host-main.c @@ -69,6 +69,7 @@ REBARGS Main_Args; #ifdef TO_WINDOWS #define MAX_TITLE_LENGTH 1024 HINSTANCE App_Instance = 0; +HWND Focused_Window = 0; WCHAR App_Title[MAX_TITLE_LENGTH]; //will be filled later from the resources file #endif diff --git a/src/os/win32/dev-event.c b/src/os/win32/dev-event.c index 796c1bf053..5fb9ee7c54 100644 --- a/src/os/win32/dev-event.c +++ b/src/os/win32/dev-event.c @@ -48,7 +48,7 @@ HWND Event_Handle = 0; // Used for async DNS static int Timer_Id = 0; // The timer we are using extern HINSTANCE App_Instance; // From Main module. - +extern HWND Focused_Window; /*********************************************************************** ** @@ -137,8 +137,10 @@ extern HINSTANCE App_Instance; // From Main module. if (msg.message == WM_DNS) Done_Device(msg.wParam, msg.lParam>>16); // error code else { - TranslateMessage(&msg); - DispatchMessage(&msg); + //if (IsDialogMessage(msg.hwnd, &msg) == 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + //} } } @@ -166,8 +168,10 @@ extern HINSTANCE App_Instance; // From Main module. if (msg.message == WM_DNS) Done_Device(msg.wParam, msg.lParam>>16); // error code else { - TranslateMessage(&msg); - DispatchMessage(&msg); + if(Focused_Window && !IsDialogMessage(Focused_Window, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } } } diff --git a/src/os/win32/host-compositor.c b/src/os/win32/host-compositor.c index 081fdef3e2..79481646d9 100644 --- a/src/os/win32/host-compositor.c +++ b/src/os/win32/host-compositor.c @@ -111,6 +111,7 @@ static REBXYF Zero_Pair = {0, 0}; ***********************************************************************/ { //do cleanup + puts("OS_Destroy_Compositor"); ReleaseDC(GOB_HWIN(ctx->wind_gob), ctx->wind_DC); DeleteDC(ctx->back_DC); DeleteObject(ctx->back_buffer); @@ -184,7 +185,7 @@ static REBXYF Zero_Pair = {0, 0}; DeleteObject((HBITMAP)SelectObject(new_DC, new_buffer)); //fill the background color - SetDCBrushColor(new_DC, RGB(200,200,200)); + SetDCBrushColor(new_DC, RGB(240,240,240)); FillRect(new_DC,&lprc, ctx->brush_DC); if (ctx->back_DC != 0) { @@ -246,6 +247,12 @@ static REBXYF Zero_Pair = {0, 0}; GOB_WO(gob) = GOB_LOG_W(gob); GOB_HO(gob) = GOB_LOG_H(gob); +#ifdef HAS_WIDGET_GOB + if (GOB_TYPE(gob) == GOBT_WIDGET) { + OS_Init_Gob_Widget(ctx, gob); + } +#endif + CLR_GOB_STATE(gob, GOBS_NEW); } @@ -277,7 +284,13 @@ static REBXYF Zero_Pair = {0, 0}; //RL_Print("draw image gob\n"); OS_Blit_Gob_Image(gob, ctx, offset, top_left, bottom_right); break; - + +#ifdef HAS_WIDGET_GOB + case GOBT_WIDGET: + //RL_Print("draw widget\n"); + break; +#endif + case GOBT_DRAW: //not implemented break; diff --git a/src/os/win32/host-event.c b/src/os/win32/host-event.c index 93556143d3..78981ad1c0 100644 --- a/src/os/win32/host-event.c +++ b/src/os/win32/host-event.c @@ -59,6 +59,9 @@ #include "reb-host.h" #include "host-lib.h" +extern HINSTANCE App_Instance; // Set by winmain function +extern HWND Focused_Window; + //***** Constants ***** // Virtual key conversion table, sorted by first column. @@ -128,6 +131,28 @@ static void Add_Event_Key(REBGOB *gob, REBINT id, REBINT key, REBINT flags) RL_Event(&evt); // returns 0 if queue is full } +static void Add_Event_Widget(HWND widget, REBINT id, REBINT flags) +{ + REBEVT evt; + REBGOB *gob; +#ifdef __LLP64__ + gob = (REBGOB *)GetWindowLongPtr(widget, GWLP_USERDATA); +#else + gob = (REBGOB *)GetWindowLong(widget, GWL_USERDATA); +#endif + // fields are throwing CHANGE event during its creation + // when there is no GWLP_USERDATA yet available! + if (!gob) return; // ... so ignore it in such a case. + + evt.type = id; + evt.flags = 0; //(u8)(flags | (1 << EVF_HAS_XY)); + evt.model = EVM_GUI; + evt.data = 0; + evt.ser = (void*)gob; + + RL_Event(&evt); // returns 0 if queue is full +} + static void Add_File_Events(REBGOB *gob, REBINT flags, HDROP drop) { REBEVT evt; @@ -159,7 +184,7 @@ static void Add_File_Events(REBGOB *gob, REBINT flags, HDROP drop) DragFinish(drop); } -static Check_Modifiers(REBINT flags) +static REBINT Check_Modifiers(REBINT flags) { if (GetKeyState(VK_SHIFT) < 0) flags |= (1< 0); + // //printf("EN h: %d\n", si.nPos); + // break; + case BN_CLICKED: + Add_Event_Widget((HWND)lParam, EVT_CLICK, flags); + break; + case EN_SETFOCUS: + Add_Event_Widget((HWND)lParam, EVT_FOCUS, flags); + break; + case EN_KILLFOCUS: + Add_Event_Widget((HWND)lParam, EVT_UNFOCUS, flags); + break; + } + break; + case WM_DROPFILES: Add_File_Events(gob, flags, (HDROP)wParam); + return 0; + + case WM_CTLCOLORSTATIC: + //SetTextColor((HDC)wParam, RGB(0, 0, 0)); + //SetBkMode((HDC)wParam, COLOR_BACKGROUND); + //return (LRESULT)GetStockObject(COLOR_WINDOW); + break; + + case WM_CTLCOLORBTN: + //printf("\nWM_CTLCOLORBTN wParam: %08X lParam: %08X \n", wParam, lParam); + //SetBkMode((HDC)wParam, COLOR_WINDOW); + //SetBkColor((HDC)lParam, RGB(0, 0, 0)); + //return (LRESULT)GetStockObject(COLOR_WINDOW); + break; + + case WM_NOTIFY: + //printf("\nWM_NOTIFY wParam: %04X %04X %u lParam: %08X \n", LOWORD(wParam), HIWORD(wParam), HIWORD(wParam), lParam); + switch (((LPNMHDR)lParam)->code) { + case DTN_DATETIMECHANGE: + Add_Event_Widget(((LPNMHDR)lParam)->hwndFrom, EVT_CHANGE, flags); + return 0; + } + break; + + case WM_GETMINMAXINFO: + printf("WM_GETMINMAXINFO\n"); + break; + + case WM_SETFOCUS: + puts("WM_SETFOCUS"); + Focused_Window = hwnd; break; case WM_CLOSE: Add_Event_XY(gob, EVT_CLOSE, xy, flags); OS_Close_Window(gob); // Needs to be removed - should be done by REBOL event handling // DestroyWindow(hwnd);// This is done in OS_Close_Window() - break; + return 0; case WM_DESTROY: PostQuitMessage(0); + return 0; + case WM_NCDESTROY: + puts("WM_NCDESTROY"); break; - default: - default_case: - return DefWindowProc(hwnd, msg, wParam, xy); } - return 0; + return DefWindowProc(hwnd, msg, wParam, xy); } diff --git a/src/os/win32/host-lib.c b/src/os/win32/host-lib.c index 6190963175..cab4b1b38b 100644 --- a/src/os/win32/host-lib.c +++ b/src/os/win32/host-lib.c @@ -121,7 +121,8 @@ static void *Task_Ready; } } - +#ifdef removing_this_code +// this function is not needed. Now is possible to use RL_GET_STRING with WIDE flag /*********************************************************************** ** */ REBOOL As_OS_Str(REBSER *series, REBCHR **string) @@ -156,7 +157,7 @@ static void *Task_Ready; *string = (len == 0) ? NULL : str; //empty string check return FALSE; } - +#endif /*********************************************************************** ** diff --git a/src/os/win32/host-window.c b/src/os/win32/host-window.c index 6a289b95bd..7af3672712 100644 --- a/src/os/win32/host-window.c +++ b/src/os/win32/host-window.c @@ -44,12 +44,28 @@ #define WINVER 0x0501 // this is needed to be able use WINDOWINFO struct etc. #endif +/* Forces the use of Visual Styles if compiling with VisualStudio */ +#ifdef _MSC_VER +#pragma comment(linker,"\"/manifestdependency:type='win32' \ + name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ + processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif + + #include +#include +#include // used for setting visual defaults +#include // --//-- #include +#include // used for debuging traces + +#undef IS_ERROR // Windows is using this macro name too, we don't need their version + #include "reb-host.h" #include "host-lib.h" #include "reb-compositor.h" +#include "reb-types.h" #define INCLUDE_EXT_DATA #include "host-ext-window.h" @@ -85,15 +101,17 @@ typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, extern HINSTANCE App_Instance; // Set by winmain function extern void Host_Crash(char *reason); extern LRESULT CALLBACK REBOL_Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); -extern REBOOL As_OS_Str(REBSER *series, REBCHR **string); //***** Locals *****// static REBOOL Registered = FALSE; // Window has been registered -static const REBCHR *Window_Class_Name = TXT("REBOLWindow"); +static const REBCHR *Class_Name_Window = TXT("RebWindow"); +static const REBCHR *Class_Name_Button = TXT("RebButton"); +static const REBCHR *Class_Name_ComboBox = TXT("RebCombo"); static struct gob_window *Gob_Windows; static REBOOL DPI_Aware = FALSE; static REBOOL Custom_Cursor = FALSE; +static HFONT Default_Font = NULL; static u32* window_ext_words; @@ -108,6 +126,9 @@ REBXYF log_size = { 1.0, 1.0 }; // logical pixel size measured in physical pixe REBXYF phys_size = { 1.0, 1.0 }; // physical pixel size measured in logical pixels(reciprocal value of log_size) REBINT window_scale; +//***** Forwards *****// + +static REBCNT Get_Widget_Text(HWND widget, REBVAL *text); /*********************************************************************** ** @@ -246,6 +267,30 @@ REBINT window_scale; } } + +/*********************************************************************** +** +*/ static void Make_Subclass(const REBCHR *new_class, const REBCHR *old_class, WNDPROC *proc, REBOOL system) +/* +** Register super class +** +***********************************************************************/ +{ + HINSTANCE hInstance = system ? NULL : App_Instance; + WNDCLASSEX wcex; + ZeroMemory(&wcex, sizeof(wcex)); + + if(!GetClassInfoEx(hInstance, old_class, &wcex)) { + puts("Failed to get old class info!"); + } + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.lpszClassName = new_class; + wcex.hInstance = App_Instance; + if(!RegisterClassEx(&wcex)) + Host_Crash("Cannot register sub-class"); +} + + /*********************************************************************** ** */ static void Register_Window() @@ -256,10 +301,11 @@ REBINT window_scale; ** ***********************************************************************/ { + puts("Register_Window"); WNDCLASSEX wc; wc.cbSize = sizeof(wc); - wc.lpszClassName = Window_Class_Name; + wc.lpszClassName = Class_Name_Window; wc.hInstance = App_Instance; wc.lpfnWndProc = REBOL_Window_Proc; @@ -281,11 +327,13 @@ REBINT window_scale; ); // If not already registered: - //if (!GetClassInfo(App_Instance, Window_Class_Name, &wclass)) + //if (!GetClassInfo(App_Instance, Class_Name_Window, &wclass)) // RegisterClass(&wclass); if (!RegisterClassEx(&wc)) Host_Crash("Cannot register window"); + Make_Subclass(Class_Name_Button, TEXT("BUTTON"), NULL, TRUE); + Registered = TRUE; } @@ -384,7 +432,7 @@ REBINT window_scale; } if (IS_GOB_STRING(gob)) - osString = As_OS_Str(GOB_CONTENT(gob), (REBCHR**)&title); + RL_Get_String(GOB_CONTENT(gob), 0, (void**)&title, TRUE); else title = TXT("REBOL Window"); @@ -398,18 +446,43 @@ REBINT window_scale; // Create the window: window = CreateWindowEx( - WS_EX_WINDOWEDGE, - Window_Class_Name, + WS_EX_WINDOWEDGE, + Class_Name_Window, title, options, x, y, w, h, parent, NULL, App_Instance, NULL ); + if (!window) { + printf("error: %d %d %d %d %d\n",x,y,w,h, GetLastError()); + Host_Crash("CreateWindow failed"); + } - //don't let the string leak! - if (osString) OS_Free(title); - if (!window) Host_Crash("CreateWindow failed"); + if (!Default_Font) { + LOGFONTW font; + HTHEME *hTheme = NULL; + HRESULT res = -1; + if (IsThemeActive()) { + hTheme = OpenThemeData(window, L"Window"); + if (hTheme) { + res = GetThemeSysFont(hTheme, TMT_MSGBOXFONT, &font); + } + } else { + NONCLIENTMETRICS metrics; + ZeroMemory(&metrics, sizeof(metrics)); + metrics.cbSize = sizeof(metrics); + res = SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); + if (res) font = metrics.lfMessageFont; + } + if ( res >= 0 ) { + Default_Font = CreateFontIndirect(&font); + printf("font: '%ls' %08Xh\n", font.lfFaceName, Default_Font); + } + + if (hTheme) CloseThemeData(hTheme); + if (!Default_Font) Default_Font = GetStockObject(DEFAULT_GUI_FONT); + } // Enable drag and drop if (GET_GOB_FLAG(gob, GOBF_DROPABLE)) @@ -432,9 +505,9 @@ REBINT window_scale; if (!GET_GOB_FLAG(gob, GOBF_HIDDEN)) { if (GET_GOB_FLAG(gob, GOBF_ON_TOP)) SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); ShowWindow(window, SW_SHOWNORMAL); + SendMessage(window, WM_PAINT, 0, 0); SetForegroundWindow(window); } - return window; } @@ -448,7 +521,7 @@ REBINT window_scale; AdjustWindowRect(&rect, options, FALSE); // Create window (use parent if specified): - GOB_WIN(gob) = CreateWindow(Window_Class_Name, title, options, + GOB_WIN(gob) = CreateWindow(Class_Name_Window, title, options, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, (wparent ? GOB_WIN(wparent) : NULL), NULL, App_Instance, NULL); @@ -547,10 +620,8 @@ REBINT window_scale; // SetWindowPos(window, 0, GOB_X(gob), GOB_Y(gob), GOB_W(gob), GOB_H(gob), opts | SWP_NOZORDER); if (IS_GOB_STRING(gob)){ - osString = As_OS_Str(GOB_CONTENT(gob), (REBCHR**)&title); + RL_Get_String(GOB_CONTENT(gob), 0, (void**)&title, TRUE); SetWindowText(window, title); - //don't let the string leak! - if (osString) OS_Free(title); } /* @@ -682,6 +753,243 @@ REBINT window_scale; } +/*********************************************************************** +** +*/ void* OS_Init_Gob_Widget(REBCMP *ctx, REBGOB *gob) +/* +** Creates native GUI widget +** +***********************************************************************/ +{ + puts("OS_Init_Gob_Widget"); + HWND hWnd; + REBCHR *class; + REBCHR *text = NULL; + REBI64 value_i64 = 0; + REBDEC value_dec = 0.0; + REBINT range; + REBOOL vertical; + REBFLG style = WS_CHILD | WS_VISIBLE; // common flags + REBFLG xstyle = 0; + REBVAL *hndl = GOB_WIDGET_HANDLE(gob); + REBVAL *type = GOB_WIDGET_TYPE(gob); + REBVAL *spec = GOB_WIDGET_SPEC(gob); + + if (!IS_HANDLE(hndl) || !IS_BLOCK(spec)) return NULL; + REBVAL *val = BLK_HEAD(VAL_SERIES(spec)); + + if (IS_WORD(val) || IS_LIT_WORD(val)) { // ...where the first value is the type of the widget + SET_INTEGER(type, (REBI64)RL_Find_Word(window_ext_words, val->data.word.sym)); + } else { + return NULL; + } + + while (!IS_END(++val)) { + if (IS_STRING(val)) { + RL_Get_String(VAL_SERIES(val), 0, (void**)&text, TRUE); + } else if (IS_INTEGER(val)) { + value_i64 = VAL_INT64(val); + } else if (IS_DECIMAL(val)) { + value_dec = VAL_DECIMAL(val); + } + } + + switch (VAL_INT64(type)) { + case W_WINDOW_BUTTON: + class = (REBCHR*)Class_Name_Button; + style |= WS_TABSTOP | BS_PUSHBUTTON; // | BS_DEFPUSHBUTTON; + break; + case W_WINDOW_CHECK: + class = (REBCHR*)Class_Name_Button; + style |= WS_TABSTOP | BS_AUTOCHECKBOX; + break; + case W_WINDOW_RADIO: + class = (REBCHR*)Class_Name_Button; + style |= WS_TABSTOP | BS_AUTORADIOBUTTON; + break; + case W_WINDOW_FIELD: + class = TXT("edit"); + style |= ES_LEFT | ES_AUTOHSCROLL | ES_NOHIDESEL; + xstyle |= WS_EX_CLIENTEDGE; + break; + case W_WINDOW_AREA: + class = TXT("edit"); + style |= ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL + | WS_HSCROLL | WS_VSCROLL | ES_NOHIDESEL; + xstyle |= WS_EX_CLIENTEDGE; + break; + case W_WINDOW_TEXT: + class = TXT("STATIC"); + style |= SS_SIMPLE; + break; + case W_WINDOW_TEXT_LIST: + class = TXT("ListBox"); + style |= LBS_NOTIFY | WS_HSCROLL | WS_VSCROLL | LBS_NOINTEGRALHEIGHT; + xstyle |= WS_EX_CLIENTEDGE; + break; + case W_WINDOW_PROGRESS: + class = TXT("msctls_progress32"); + if (gob->size.y > gob->size.x) style |= PBS_VERTICAL; + break; + case W_WINDOW_SLIDER: + class = TXT("msctls_trackbar32"); + vertical = gob->size.y > gob->size.x; + if (vertical) style |= TBS_VERT | TBS_DOWNISLEFT; + break; + case W_WINDOW_DATE_TIME: + class = TXT("SysDateTimePick32"); + vertical = gob->size.y > gob->size.x; + style |= WS_BORDER; //| DTS_SHOWNONE; + break; + case W_WINDOW_GROUP_BOX: + class = TXT("BUTTON"); + style |= BS_GROUPBOX; + break; + default: + puts("unknown widget name"); + return NULL; + } + + hWnd = CreateWindowEx( + xstyle, + class, + text, + style, + gob->offset.x, gob->offset.y, // position + gob->size.x, gob->size.y, // size + WindowFromDC(ctx->wind_DC), // parent + (HMENU)0, + App_Instance, NULL); + + VAL_HANDLE(hndl) = (ANYFUNC)hWnd; + SendMessage(hWnd, WM_SETFONT, (WPARAM)Default_Font, 0); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)gob); + + printf("======== NEW widget: %08Xh for gob: %08Xh\n", hWnd, gob); + + switch (VAL_INT64(type)) { + case W_WINDOW_BUTTON: + + //SendMessage(hWnd, BCM_FIRST + 0x0009, 0, text); + //Button_SetNote(hWnd, text); + break; + case W_WINDOW_PROGRESS: + SendMessage(hWnd, PBM_SETPOS, value_i64, 0); + break; + case W_WINDOW_SLIDER: + range = ROUND_TO_INT(vertical ? gob->size.y : gob->size.x); + SendMessage(hWnd, TBM_SETRANGE, 0, (LPARAM)MAKELONG(0, range)); + SendMessage(hWnd, TBM_SETPAGESIZE, 0, (LPARAM)4); + //SendMessage(hWnd, TBM_SETSEL, 0, (LPARAM)MAKELONG(0, range)); + SendMessage(hWnd, TBM_SETPOS, 1, (LPARAM)(value_dec * (REBDEC)range)); + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + break; + } + + return hWnd; +} + + +/*********************************************************************** +** +*/ REBOOL OS_Get_Widget_Data(REBGOB *gob, REBVAL *ret) +/* +** Returns data according the widget type +** +***********************************************************************/ +{ + REBVAL *hndl = GOB_WIDGET_HANDLE(gob); + REBVAL *type = GOB_WIDGET_TYPE(gob); + REBVAL *data; + + HWND hWnd = (HWND)VAL_HANDLE(hndl); + LRESULT res; + REBDEC range; + REBCNT count; + SYSTEMTIME dat; + + //printf("OS_Get_Widget_Data type: %d\n", VAL_INT64(type)); + + switch (VAL_INT64(type)) { + + case W_WINDOW_PROGRESS: + res = SendMessage(hWnd, PBM_GETPOS, 0, 0); + SET_INTEGER(ret, (REBU64)res); + return TRUE; + + case W_WINDOW_SLIDER: + range = (REBDEC)SendMessage(hWnd, TBM_GETRANGEMAX, 0, 0); + if (range > 0) { + res = SendMessage(hWnd, TBM_GETPOS, 0, 0); + range = (REBDEC)res / range; + } + if (gob->size.y > gob->size.x) range = 1 - range; + SET_DECIMAL(ret, range); + return TRUE; + + case W_WINDOW_FIELD: + case W_WINDOW_AREA: + data = GOB_WIDGET_DATA(gob); + count = Get_Widget_Text(hWnd, data); + SET_STRING(ret, VAL_SERIES(data)); + VAL_TAIL(ret) = count; + return TRUE; + + case W_WINDOW_DATE_TIME: + data = GOB_WIDGET_DATA(gob); + res = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&dat); + if (GDT_VALID == res) { + VAL_SET(data, REB_DATE); + VAL_YEAR(data) = dat.wYear; + VAL_MONTH(data) = dat.wMonth; + VAL_DAY(data) = dat.wDay; + VAL_ZONE(data) = 0; + VAL_TIME(data) = NO_TIME; + //VAL_TIME(data) = TIME_SEC(dat.wHour * 3600 + dat.wMinute * 60 + dat.wSecond) + 1000000 * dat.wMilliseconds; + *ret = *data; + return TRUE; + } + break; + } + //@@ throw an error or return NONE when unhandled type? + SET_NONE(ret); + return TRUE; + //return FALSE; +} + + +/*********************************************************************** +** +*/ REBOOL OS_Set_Widget_Data(REBGOB *gob, REBVAL *data) +/* +** Returns data according the widget type +** +***********************************************************************/ +{ + REBVAL *hndl = GOB_WIDGET_HANDLE(gob); + REBVAL *type = GOB_WIDGET_TYPE(gob); + + HWND hWnd = (HWND)VAL_HANDLE(hndl); + + switch (VAL_INT64(type)) { + case W_WINDOW_PROGRESS: + if (IS_INTEGER(data)) { + SendMessage(hWnd, PBM_SETPOS, VAL_INT32(data), 0); + return TRUE; + } + break; + case W_WINDOW_CHECK: + case W_WINDOW_RADIO: + if (IS_LOGIC(data)) { + SendMessage(hWnd, BM_SETCHECK, VAL_LOGIC(data), 0); + return TRUE; + } + break; + } + return FALSE; +} + + /*********************************************************************** ** */ REBD32 OS_Get_Metrics(METRIC_TYPE type, REBINT display) @@ -1056,8 +1364,41 @@ REBINT window_scale; CLEAR(Gob_Windows, sizeof(struct gob_window) * (MAX_WINDOWS + 1)); Cursor = LoadCursor(NULL, IDC_ARROW); Init_DPI_Awareness(); + + + INITCOMMONCONTROLSEX InitCtrlEx; + InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX); + InitCtrlEx.dwICC = ICC_STANDARD_CLASSES + | ICC_LINK_CLASS + | ICC_UPDOWN_CLASS + | ICC_LISTVIEW_CLASSES + | ICC_PROGRESS_CLASS + | ICC_BAR_CLASSES + | ICC_DATE_CLASSES; + if (!InitCommonControlsEx(&InitCtrlEx)) { + printf("Could not initialize common controls! (%u)\n", GetLastError()); + } +} + + +/**************** helpers ********************/ + +static REBCNT Get_Widget_Text(HWND widget, REBVAL *text) +{ + REBCNT count = SendMessage(widget, WM_GETTEXTLENGTH, -1, 0); + + if (!IS_STRING(text)) { + SET_STRING(text, RL_Make_String(count, TRUE)); + } + else if (count > VAL_REST(text)) { + RL_Expand_Series(VAL_SERIES(text), VAL_TAIL(text), count - VAL_REST(text)); + } + SendMessage(widget, WM_GETTEXT, count + 1, (LPARAM)VAL_BIN(text)); + return count; } + + #ifdef NOT_USED_BUT_MAYBE_LATER HWND Main_Event_Window; diff --git a/src/tools/make-make.r b/src/tools/make-make.r index a206c742b7..7a85d04a95 100644 --- a/src/tools/make-make.r +++ b/src/tools/make-make.r @@ -87,7 +87,7 @@ USE_FLAGS= # Flags for core and for host: RFLAGS= -c -D$(TO_OS) -DREB_API $(RAPI_FLAGS) $(USE_FLAGS) $I -HFLAGS= -c -D$(TO_OS) -DREB_CORE $(HOST_FLAGS) $(USE_FLAGS) $I +HFLAGS= -c -D$(TO_OS) $(HOST_FLAGS) $(USE_FLAGS) $I CLIB= # REBOL is needed to build various include files: diff --git a/src/tools/systems.r b/src/tools/systems.r index aa7f3d63a5..4a255dd2f8 100644 --- a/src/tools/systems.r +++ b/src/tools/systems.r @@ -20,25 +20,25 @@ REBOL [ systems: [ [plat os-name os-base build-flags] - [0.1.03 "amiga" posix [BEN HID NPS +SC CMT COP -SP -LM]] - [0.2.04 "osx" posix [BEN +OS NCM -LM UOP]] ; OSX/PPC; no shared lib possible - [0.2.05 "osxi" posix [LEN +O1 PIC NPS NCM HID STX -LM UOP ARC FCS FCM]] - [0.2.40 "osx_x64" posix [LEN +O1 PIC NPS NCM HID STX -LM UOP L64 FCS FCM]] - [0.3.01 "win32" win32 [LEN +O2 UNI M32 W32 CON S4M EXE DIR -LM]] - [0.3.40 "win32_x64" win32 [LEN +O2 UNI M64 W32 CON S4M EXE DIR -LM P64]] - [0.4.02 "linux" posix [LEN +O2 PIC LDL ST1 -LM]] ; libc 2.3 - [0.4.03 "linux" posix [LEN +O2 PIC LDL ST1 -LM HID]] ; libc 2.5 - [0.4.04 "linux" posix [LEN +O2 PIC LDL ST1 -LM HID M32]] ; libc 2.11 - [0.4.10 "linux_ppc" posix [BEN +O1 PIC LDL ST1 -LM HID]] - [0.4.20 "linux_arm" posix [LEN +O2 PIC LDL ST1 -LM HID]] - [0.4.21 "linux_arm" posix [LEN +O2 PIE LDL ST1 -LM HID]] ; bionic (Android) - [0.4.30 "linux_mips" posix [LEN +O2 PIC LDL ST1 -LM HID]] - [0.4.40 "linux_x64" posix [LEN +O2 PIC LDL ST1 -LM HID L64]] - [0.5.75 "haiku" posix [LEN +O2 ST1 NWK]] - [0.7.02 "freebsd" posix [LEN +O1 ST1 -LM]] - [0.7.40 "freebsd_x64" posix [LEN +O1 ST1 -LM L64]] - [0.9.04 "openbsd" posix [LEN +O1 ST1 -LM]] - [0.13.01 "android_arm" android [LEN HID F64 LDL LLOG -LM CST]] + [0.1.03 "amiga" posix [NOV BEN HID NPS +SC CMT COP -SP -LM]] + [0.2.04 "osx" posix [NOV BEN +OS NCM -LM UOP]] ; OSX/PPC; no shared lib possible + [0.2.05 "osxi" posix [NOV LEN +O1 PIC NPS NCM HID STX -LM UOP ARC FCS FCM]] + [0.2.40 "osx_x64" posix [NOV LEN +O1 PIC NPS NCM HID STX -LM UOP L64 FCS FCM]] + [0.3.01 "win32" win32 [ LEN +O2 UNI M32 W32 CON S4M EXE DIR -LM]] + [0.3.40 "win32_x64" win32 [ LEN +O2 UNI M64 W32 CON S4M EXE DIR -LM P64]] + [0.4.02 "linux" posix [NOV LEN +O2 PIC LDL ST1 -LM]] ; libc 2.3 + [0.4.03 "linux" posix [NOV LEN +O2 PIC LDL ST1 -LM HID]] ; libc 2.5 + [0.4.04 "linux" posix [NOV LEN +O2 PIC LDL ST1 -LM HID M32]] ; libc 2.11 + [0.4.10 "linux_ppc" posix [NOV BEN +O1 PIC LDL ST1 -LM HID]] + [0.4.20 "linux_arm" posix [NOV LEN +O2 PIC LDL ST1 -LM HID]] + [0.4.21 "linux_arm" posix [NOV LEN +O2 PIE LDL ST1 -LM HID]] ; bionic (Android) + [0.4.30 "linux_mips" posix [NOV LEN +O2 PIC LDL ST1 -LM HID]] + [0.4.40 "linux_x64" posix [NOV LEN +O2 PIC LDL ST1 -LM HID L64]] + [0.5.75 "haiku" posix [NOV LEN +O2 ST1 NWK]] + [0.7.02 "freebsd" posix [NOV LEN +O1 ST1 -LM]] + [0.7.40 "freebsd_x64" posix [NOV LEN +O1 ST1 -LM L64]] + [0.9.04 "openbsd" posix [NOV LEN +O1 ST1 -LM]] + [0.13.01 "android_arm" android [NOV LEN HID F64 LDL LLOG -LM CST]] ] compile-flags: [ @@ -64,6 +64,7 @@ compile-flags: [ LEN: "-DENDIAN_LITTLE" ; uses little endian byte order BEN: "-DENDIAN_BIG" ; uses big endian byte order UOP: "-DUSE_OLD_PIPE" ; use pipe() instead of pipe2(), which may not be supported + NOV: "-DREB_CORE" ; no view ] linker-flags: [ @@ -73,7 +74,7 @@ linker-flags: [ LLOG: "-llog" ; on Android, link with liblog.so ARC: "-arch i386" ; x86 32 bit architecture (OSX) M32: "-m32" ; use 32-bit memory model (Linux x64) - W32: "-lwsock32 -lcomdlg32 -lgdi32 -lwinmm" + W32: "-lwsock32 -lcomdlg32 -lgdi32 -lwinmm -lComctl32 -lUxTheme" WIN: "-mwindows" ; build as Windows GUI binary CON: "-mconsole" ; build as Windows Console binary S4M: "-Wl,--stack=4194300"