Today (2 hours ago) I was playing with buttons and the result was the code pasted here. The idea is good (was born while I was playing with Blender 3D (3 hours ago)). And in a while, I will be playing the "try sleeping" thing. Maybe the code gives some ideas to somebody.



;Filename: editbtn.asm
.486
.model flat, stdcall
option casemap:none

;_______________________________________________________________________________
;INCLUDE FILES

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

;_______________________________________________________________________________
;MACROS

include \masm32\macros\macros.asm

;_______________________________________________________________________________
;PROTOTYPES

WinMain proto :dword,:dword,:dword,:dword

;_______________________________________________________________________________
.data

szClassName db "win32asmblenderbutton",0
szHowItWorks db "Start editing button: [SHIFT]+[LBUTTON]",13,10
db "End editing button: [ENTER] or [ESC]",0

;_______________________________________________________________________________
.data?

hInstance HINSTANCE ?
hwnd HWND ?
oldButtonProc dword ?
hBlenderButton1 dword ?
hBlenderButton2 dword ?
hBlenderButton3 dword ?
hBlenderButton4 dword ?
hBlenderButton5 dword ?

;_______________________________________________________________________________
.code

;===============================================================================
;WinMain (hInst, hPrevIns, CmdLine, CmdShow)
;===============================================================================
WinMain proc hInst:HINSTANCE, hPrevIns:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG

;----- [register superclass button (Blender style button)] -----
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx, NULL, CADD("BUTTON"),addr wc
push wc.lpfnWndProc
pop oldButtonProc
mov wc.lpfnWndProc, offset ButtonProc
push hInst
pop wc.hInstance
mov wc.lpszClassName, CTXT("BLENDERBUTTON")
invoke RegisterClassEx, addr wc

;----- [register our app] -----
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
mov wc.lpfnWndProc, offset WndProc
push hInst
pop wc.hInstance
mov wc.lpszClassName, offset szClassName
mov wc.lpszMenuName, NULL
mov wc.hbrBackground, COLOR_WINDOW
invoke LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, NULL
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL, addr szClassName, CADD("Testing an editable button (by Kecol)"),\
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,\
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL
mov hwnd, eax
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp

;===============================================================================
;WndProc (hWnd, uMsg, wParam, lParam)
;===============================================================================
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam;LPARAM
LOCAL _buffer[256]:byte

.IF uMsg == WM_CREATE
invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("Click me with SHIFT key down"),\
WS_CHILD or WS_VISIBLE, 10, 50, 400, 100, hWnd, 500, hInstance, NULL
mov hBlenderButton1, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 2"),\
WS_CHILD or WS_VISIBLE, 500, 400, 120, 20, hWnd, 501, hInstance, NULL
mov hBlenderButton2, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 3"),\
WS_CHILD or WS_VISIBLE, 100, 300, 160, 30, hWnd, 502, hInstance, NULL
mov hBlenderButton3, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 4"),\
WS_CHILD or WS_VISIBLE, 600, 100, 100, 20, hWnd, 503, hInstance, NULL
mov hBlenderButton4, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 5"),\
WS_CHILD or WS_VISIBLE, 300, 270, 200, 30, hWnd, 504, hInstance, NULL
mov hBlenderButton5, eax

invoke MessageBox, hWnd, addr szHowItWorks, CADD("How it works"), MB_OK

.ELSEIF uMsg == WM_COMMAND
mov eax, wParam
.IF ax >= 500 && ax <=504
invoke GetWindowText, lParam, addr _buffer, 255
mov _buffer[255], 0
invoke MessageBox, hWnd, addr _buffer, CADD("Button is Talking!!!"), MB_OK
.ENDIF
.ELSEIF uMsg == WM_CLOSE
invoke DestroyWindow, hBlenderButton1
invoke DestroyWindow, hBlenderButton2
invoke DestroyWindow, hBlenderButton3
invoke DestroyWindow, hBlenderButton4
invoke DestroyWindow, hBlenderButton5
invoke PostQuitMessage, NULL
ret
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF

xor eax, eax
ret
WndProc endp

;===============================================================================
;ButtonProc(hWnd, uMsg, wParam, lParam)
;===============================================================================
ButtonProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL _buffer[256]:byte
LOCAL point:POINT
LOCAL hParent:HWND
LOCAL hEdit:HWND
LOCAL msg:MSG
LOCAL rect:RECT
LOCAL _width:dword
LOCAL _height:dword

;----- [WM_LBUTTONUP is the only message that need be intercepted] -----
;----- [and it works only if SHIFT KEY IS DOWN] -----

.IF uMsg==WM_LBUTTONUP && wParam == MK_SHIFT

invoke GetWindowText, hWnd, addr _buffer, 255 ;save the text to be
mov _buffer[255], 0 ;used in edit control.
invoke ShowWindow, hWnd, SW_HIDE ;hide button.
mov hParent, FUNC (GetParent, hWnd) ;get button parent.
invoke GetWindowRect, hWnd, addr rect ;get button screen pos
mov eax, rect.right ;and transform it
sub eax, rect.left ;width ;to client ones.
mov _width, eax
mov eax, rect.bottom
sub eax, rect.top ;height
mov _height, eax
invoke ScreenToClient, hParent, addr rect

;----- [Create an EDIT control in button place with button text] -----

invoke CreateWindowEx,\
NULL, CADD("EDIT"), addr _buffer,\
WS_CHILD + WS_VISIBLE + WS_BORDER + ES_CENTER,\
rect.left, rect.top, _width, _height,\
hParent, NULL, hInstance, NULL
mov hEdit, eax
invoke ShowWindow, hEdit, SW_NORMAL ;show edit control
invoke SetFocus, hEdit ;set focus in it
invoke SendMessage, hEdit, EM_SETSEL, 0, -1
;----- [empty queue processing the remaining messages] -----

.WHILE TRUE
invoke PeekMessage, addr msg, hParent, 0, 0, PM_REMOVE
.BREAK .IF eax == 0 ;empty queue
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW

;----- [queue is empty and now I can process only hEdit message] -----
;----- [exit from loop only if ENTER or ESC are pressed] -----

.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
mov eax, msg.hwnd
.IF eax == hEdit
.IF msg.message == WM_KEYDOWN
.IF msg.wParam == VK_RETURN
mov eax, TRUE
.BREAK
.ELSEIF msg.wParam==VK_ESCAPE
mov eax, FALSE
.BREAK
.ENDIF
.ENDIF
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDIF
.ENDW
;----- [if(eax) set new text in button] -----
.IF eax == TRUE
invoke GetWindowText, hEdit, addr _buffer, 255
mov _buffer[255], 0
.IF eax != 0
invoke SetWindowText, hWnd, addr _buffer
;here can go a routine to send hParent window that
;the text was changed. EXAMPLE:
;invoke GetWindowLong, hWnd, GWL_ID
;mov edx, 12345 ;text was changed msg
;shl edx, 16
;or edx, eax
;invoke SendMessage, hParent, WM_COMMAND, eax, hWnd
.ENDIF
.ENDIF
invoke ShowWindow, hWnd, SW_NORMAL ;show the button
invoke DestroyWindow, hEdit ;destroy edit control
.ELSE
defproc: invoke CallWindowProc, oldButtonProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
ButtonProc endp

;===============================================================================
;PROGRAM START HERE
;===============================================================================
start:
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke WinMain, hInstance, NULL, NULL, SW_NORMAL
invoke ExitProcess, eax
end start


Kecol.-
Posted on 2004-12-15 23:32:24 by Kecol
Very nice :)

I can't think of a specific case in which you'd use buttons like this this late at night, but I'm sure there's some application out there somewhere.
Posted on 2004-12-16 01:18:06 by sirchess
sirchess, it's too late for me (sun is just for appearing again right now). Maybe tomorrow play again with the "edit button" and get a better use of it. Blender 3D use this kind of button (MUCH BETTER) in its UI (user interface).

Thank you for reading, saving, compiling and replying.

Kecol.-
Posted on 2004-12-16 02:40:54 by Kecol
I have used Blender before and I remember it being very nice.

;----- [empty queue processing the remaining messages] -----


.WHILE TRUE
invoke PeekMessage, addr msg, hParent, 0, 0, PM_REMOVE
.BREAK .IF eax == 0 ;empty queue
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW

;----- [queue is empty and now I can process only hEdit message] -----
;----- [exit from loop only if ENTER or ESC are pressed] -----

.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
mov eax, msg.hwnd
.IF eax == hEdit
.IF msg.message == WM_KEYDOWN
.IF msg.wParam == VK_RETURN
mov eax, TRUE
.BREAK
.ELSEIF msg.wParam==VK_ESCAPE
mov eax, FALSE
.BREAK
.ENDIF
.ENDIF
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDIF
.ENDW
;----- [if(eax) set new text in button] -----
.IF eax == TRUE
invoke GetWindowText, hEdit, addr _buffer, 255
mov _buffer[255], 0
.IF eax != 0
invoke SetWindowText, hWnd, addr _buffer
;here can go a routine to send hParent window that
;the text was changed. EXAMPLE:
;invoke GetWindowLong, hWnd, GWL_ID
;mov edx, 12345 ;text was changed msg
;shl edx, 16
;or edx, eax
;invoke SendMessage, hParent, WM_COMMAND, eax, hWnd
.ENDIF
.ENDIF


This code is only letting editbox messages be processed while in "button-edit" mode (at least on WinXP); was this planned, or something that needs to be fixed? Also, if the program using your button was doing some special processing during their message loop, that processing wouldn't occur while button-editing.

There must be some good way to get the WM_KEYDOWN message without overlaying the main message loop, but how? Maybe a function called during the main loop?

I played with it a little, here is what I got (apparently no attachments on the board at this time?):



;Filename: editbtn.asm
.486
.model flat, stdcall
option casemap:none

;_______________________________________________________________________________
;INCLUDE FILES

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

;_______________________________________________________________________________
;MACROS

include \masm32\macros\macros.asm

;_______________________________________________________________________________
;PROTOTYPES

WinMain proto :dword,:dword,:dword,:dword
ProcessBtn proto :dword

;_______________________________________________________________________________
;EQUATES

; Flags a BlenderBtnEdit when <ENTER> or <ESC> is pressed in the editbox
; wParam: (not used) lParam: TRUE - <ENTER>; FALSE - <ESC>
UM_BTNEDITKEY equ WM_USER

; Flags a BlenderButton when the edit box processes a quit msg
; wParam: hEdit lParam: TRUE - <ENTER>; FALSE - <ESC>
UM_BTNEDITEXIT equ WM_USER+1

;_______________________________________________________________________________
.data

szClassName db "win32asmblenderbutton",0
szHowItWorks db "Start editing button: [SHIFT]+[LBUTTON]",13,10
db "End editing button: [ENTER] or [ESC]",0

;_______________________________________________________________________________
.data?

hInstance HINSTANCE ?
hwnd HWND ?
oldButtonProc dword ?
oldBtnEditProc dword ?
hBlenderButton1 dword ?
hBlenderButton2 dword ?
hBlenderButton3 dword ?
hBlenderButton4 dword ?
hBlenderButton5 dword ?

;_______________________________________________________________________________
.code

;===============================================================================
;WinMain (hInst, hPrevIns, CmdLine, CmdShow)
;===============================================================================
WinMain proc hInst:HINSTANCE, hPrevIns:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG

;----- [register superclass button (Blender style button)] -----
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx, NULL, CADD("BUTTON"),addr wc
push wc.lpfnWndProc
pop oldButtonProc
mov wc.lpfnWndProc, offset ButtonProc
push hInst
pop wc.hInstance
mov wc.lpszClassName, CTXT("BLENDERBUTTON")
invoke RegisterClassEx, addr wc

;----- [register superclass button (Blender style button)] -----
invoke GetClassInfoEx, NULL, CADD("EDIT"),addr wc
push wc.lpfnWndProc
pop oldBtnEditProc
mov wc.lpfnWndProc, offset BtnEditProc
push hInst
pop wc.hInstance
mov wc.lpszClassName, CTXT("BLENDERBTNEDIT")
invoke RegisterClassEx, addr wc

;----- [register our app] -----
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
mov wc.lpfnWndProc, offset WndProc
push hInst
pop wc.hInstance
mov wc.lpszClassName, offset szClassName
mov wc.lpszMenuName, NULL
mov wc.hbrBackground, COLOR_WINDOW
invoke LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, NULL
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL, addr szClassName, CADD("Testing an editable button (by Kecol)"),\
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,\
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL
mov hwnd, eax
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke ProcessBtn,addr msg
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp

;===============================================================================
;WndProc (hWnd, uMsg, wParam, lParam)
;===============================================================================
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam;LPARAM
LOCAL _buffer[256]:byte

.IF uMsg == WM_CREATE
invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("Click me with SHIFT key down"),\
WS_CHILD or WS_VISIBLE, 10, 50, 400, 100, hWnd, 500, hInstance, NULL
mov hBlenderButton1, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 2"),\
WS_CHILD or WS_VISIBLE, 500, 400, 120, 20, hWnd, 501, hInstance, NULL
mov hBlenderButton2, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 3"),\
WS_CHILD or WS_VISIBLE, 100, 300, 160, 30, hWnd, 502, hInstance, NULL
mov hBlenderButton3, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 4"),\
WS_CHILD or WS_VISIBLE, 600, 100, 100, 20, hWnd, 503, hInstance, NULL
mov hBlenderButton4, eax

invoke CreateWindowEx, NULL, CADD("BLENDERBUTTON"), CADD("button 5"),\
WS_CHILD or WS_VISIBLE, 300, 270, 200, 30, hWnd, 504, hInstance, NULL
mov hBlenderButton5, eax

invoke MessageBox, hWnd, addr szHowItWorks, CADD("How it works"), MB_OK

.ELSEIF uMsg == WM_COMMAND
mov eax, wParam
.IF ax >= 500 && ax <=504
invoke GetWindowText, lParam, addr _buffer, 255
mov _buffer[255], 0
invoke MessageBox, hWnd, addr _buffer, CADD("Button is Talking!!!"), MB_OK
.ENDIF
.ELSEIF uMsg == WM_CLOSE
invoke DestroyWindow, hBlenderButton1
invoke DestroyWindow, hBlenderButton2
invoke DestroyWindow, hBlenderButton3
invoke DestroyWindow, hBlenderButton4
invoke DestroyWindow, hBlenderButton5
invoke PostQuitMessage, NULL
ret
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF

xor eax, eax
ret
WndProc endp

;===============================================================================
;ButtonProc(hWnd, uMsg, wParam, lParam)
;===============================================================================
ButtonProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL _buffer[256]:byte
LOCAL point:POINT
LOCAL hParent:HWND
LOCAL hEdit:HWND
LOCAL msg:MSG
LOCAL rect:RECT
LOCAL _width:dword
LOCAL _height:dword

;----- [WM_LBUTTONUP is the only message that need be intercepted] -----
;----- [and it works only if SHIFT KEY IS DOWN] -----

.IF uMsg==WM_LBUTTONUP && wParam == MK_SHIFT

invoke GetWindowText, hWnd, addr _buffer, 255 ;save the text to be
mov _buffer[255], 0 ;used in edit control.
invoke ShowWindow, hWnd, SW_HIDE ;hide button.
mov hParent, FUNC (GetParent, hWnd) ;get button parent.
invoke GetWindowRect, hWnd, addr rect ;get button screen pos
mov eax, rect.right ;and transform it
sub eax, rect.left ;width ;to client ones.
mov _width, eax
mov eax, rect.bottom
sub eax, rect.top ;height
mov _height, eax
invoke ScreenToClient, hParent, addr rect

;----- [Create an EDIT control in button place with button text] -----

invoke CreateWindowEx,\
NULL, CADD("BLENDERBTNEDIT"), addr _buffer,\
WS_CHILD + WS_VISIBLE + WS_BORDER + ES_CENTER,\
rect.left, rect.top, _width, _height,\
hParent, NULL, hInstance, NULL
mov hEdit, eax
invoke SetWindowLong,hEdit,GWL_USERDATA,hWnd ;remember button "parent"
invoke ShowWindow, hEdit, SW_NORMAL ;show edit control
invoke SetFocus, hEdit ;set focus in it
invoke SendMessage, hEdit, EM_SETSEL, 0, -1

;----- [if(eax) set new text in button] -----
.ELSEIF uMsg == UM_BTNEDITEXIT
.IF lParam
invoke GetWindowText, wParam, addr _buffer, 255
mov _buffer[255], 0
.IF eax != 0
invoke SetWindowText, hWnd, addr _buffer
;here can go a routine to send hParent window that
;the text was changed. EXAMPLE:
;invoke GetWindowLong, hWnd, GWL_ID
;mov edx, 12345 ;text was changed msg
;shl edx, 16
;or edx, eax
;invoke SendMessage, hParent, WM_COMMAND, eax, hWnd
.ENDIF
.ENDIF
invoke ShowWindow, hWnd, SW_NORMAL ;show the button
invoke DestroyWindow, wParam ;destroy edit control
.ELSE
defproc: invoke CallWindowProc, oldButtonProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
ButtonProc endp

;===============================================================================
;BtnEditProc(hWnd, uMsg, wParam, lParam)
;===============================================================================
BtnEditProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==UM_BTNEDITKEY
invoke GetWindowLong,hWnd,GWL_USERDATA
invoke SendMessage,eax,UM_BTNEDITEXIT,hWnd,lParam
.ELSE
invoke CallWindowProc, oldBtnEditProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax,eax
ret
BtnEditProc endp

;===============================================================================
;ProcessBtn(lpMsg)
;===============================================================================
ProcessBtn proc lpMsg:DWORD
LOCAL msg:MSG

mov eax,lpMsg
push (MSG ptr [eax]).message
pop msg.message
.IF msg.message == WM_KEYDOWN
push (MSG ptr [eax]).wParam
pop msg.wParam
push (MSG ptr [eax]).hwnd
pop msg.hwnd
.IF msg.wParam == VK_RETURN
invoke SendMessage,msg.hwnd,UM_BTNEDITKEY,NULL,TRUE
.ELSEIF msg.wParam ==VK_ESCAPE
invoke SendMessage,msg.hwnd,UM_BTNEDITKEY,NULL,FALSE
.ENDIF
.ENDIF

xor eax,eax
ret
ProcessBtn endp

;===============================================================================
;PROGRAM START HERE
;===============================================================================
start:
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke WinMain, hInstance, NULL, NULL, SW_NORMAL
invoke ExitProcess, eax
end start
Posted on 2004-12-16 11:22:40 by sirchess
Hey, much better now. The thing is that my code block the thread on propouse, just for fun, and because while I was playing with the original idea, came to my mind a question about how MessageBox blocks windows. I am sure this is not the way, but it works.
Now, it is much better than mine (how it works).
I'm working on another idea. But when I finish it, I will come to the button again to add a new intersting function (like Blender does).

sirchess, thanks for your interest.

Kecol.-

PD: Blender interface is getting better day to day. How it splits windows, or how "editable number buttons" works, or zooming of panels are something to admire from the program. I think this kind of UI can be ported or re-written in win32asm, making a better and faster UI.
Posted on 2004-12-16 12:00:33 by Kecol
sirchess, thanks for your interest.


No problem :)

Actually, your code will help me with a project of my own. I was trying to get a subclassed editbox to process WM_KEYDOWN messages, but didn't realize that these messages are never even sent to the window proc because of TranslateMessage. But now I know how to do it :alright:
Posted on 2004-12-16 13:35:30 by sirchess