Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Dynamic OnScreenDisplay (Calculator like)
#1
I want to have up to 3 numbers displayed on screen as typed which will scroll in the 3 numeric places from right to left ( like a 3 character calculator). The enter key will end the input stage and trigger the end macro/end display timer, with an auto end timer if no enter key hit within a certain time. Keys hit after enter will wipe the previous display. This seems to require a Few methods I'm not familiar with. Can you help me with this?
#2
Function dialog_three_numbers
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

str controls = "3"
str e3
if(!ShowDialog("dialog_three_numbers" &dialog_three_numbers &controls)) ret
out e3

;BEGIN DIALOG
;0 "" 0x90C80AC8 0x0 0 0 56 28 ""
;3 Edit 0x54032082 0x200 0 0 56 28 ""
;END DIALOG
;DIALOG EDITOR: "" 0x2030300 "*" "" ""

ret
;messages
__Font-- t_f
sel message
,case WM_INITDIALOG
,t_f.Create("Courier New" 22)
,t_f.SetDialogFont(hDlg "3")
,SendMessage id(3 hDlg) EM_LIMITTEXT 3 0
,SetTimer hDlg 1 10000 0 ;;close after 10 s
,
,case WM_TIMER
,sel wParam
,,case 1 clo hDlg
,
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case EN_CHANGE<<16|3
,SetTimer hDlg 1 10000 0 ;;close after 10 s
,
,case IDOK
,case IDCANCEL
ret 1
#3
I realize I don't know how to do any of this - now I have a starting example. Thanks for the code.

This works well. A few points though; I'm building a display for buttons which aren't labeled (on a MIDI device), sending Ctr+Alt+Win+(1-0 and Enter), simply adding visual feedback for the keys hit (ie 1-9 and 0). - I have no Back/Delete key, so the numbers continually scroll/accept inuput, with the value not sent until the Enter key is hit. I use 0's to fill the digits with "empty" when I've made a mistake or don't know exactly what numbers were hit.

-How to make the numbers scroll/replace from right to left => once 3 digits are displayed, with each new digit added move existing to left?
-This is built with a dialog, but I was hoping to have it as a floating OnScreenDisplay (cleaner aesthetic), is this possible?
-After Enter, another timer instead of immediately killing the display?

Thank you for your assistance.
#4
With OnScreenDisplay, keys would be sent to the active window, therefore I used dialog. We can make dialog transparent.
Function dialog_three_numbers_osd
[removed]
#5
Hhmm... This is mind boggling... Transparent is cool. It's challenging for me to incorporate this, but it's basically what I sought. - Thanks again.
I'm going to refrain from asking you for more (at least for the time being) -until I get a handle on this.....
#6
I added comments to make it easier to understand how everything works.
Also fixed 1 bug.

Function dialog_three_numbers_osd
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

;The above code is created by the Dialog Editor. Don't change it.
;First time hDlg is 0, and we show dialog. Later hDlg is dialog handle, and we go to the dialog procedure.
;_____________________________________________________________________

;THIS CODE SHOWS DIALOG
;Basic code is created by the Dialog Editor. We add more code.
;This code can be in other macro. I usually put everything in the same macro to avoid multiple macros.

;Declare dialog variables. Optionally you can assign initial values.
str controls = "3"
str e3
;Show dialog.
;This code waits until the dialog is closed.
;If closed using OK button or Enter or by calling DT_Ok(), it returns 1, else it returns 0 and we exit (then macro ends).
if(!ShowDialog("dialog_three_numbers_osd" &dialog_three_numbers_osd &controls)) ret
;Now we have dialog variables and can do whatever we want.
out e3
;...

ret ;;exit (then macro ends)
;_____________________________________________________________________

;This is dialog definition, craeted by the Dialog Editor.
;In Dialog Editor we can set certain styles for dialog and edit control.
;For example, remove dialog caption, and make edit control text right-to left.

;BEGIN DIALOG
;0 "" 0x90000A48 0x88 0 0 67 39 ""
;3 Edit 0x50002082 0x0 0 0 70 40 ""
;END DIALOG
;DIALOG EDITOR: "" 0x2030300 "*" "" ""
;_____________________________________________________________________

;THIS CODE IS DIALOG PROCEDURE
;Basic code is created by the Dialog Editor. We add more code.
;It runs while the above code is waiting in ShowDialog.
;It runs many times. It runs whenever the dialog receives a message (WM_INITDIALOG, WM_TIMER, etc).
;Messages are used to notify dialog about various events. Windows messages are documented in MSDN library.
;We add case for each message that we need. Then code below the case is executed whenever the dialog receives the message.

;messages
__Font-- t_f
sel message
,case WM_INITDIALOG ;;we receive this message when dialog is just created, but still invisible
,;set font for the edit control
,t_f.Create("Veranda" 40 1)
,t_f.SetDialogFont(hDlg "3")
,t_f.SetDialogFontColor(hDlg 0xff0080 "3")
,
,Transparent hDlg 255 0xffffff ;;make dialog transparent, using white as transparent color
,SetTimer hDlg 1 10000 0 ;;close after 10 s
,
,case WM_TIMER ;;we repeatedly receive this message if we called SetTimer; wParam is argument 2 of SetTimer; period is argument 3 of SetTimer
,sel wParam
,,case 1
,,DT_Cancel hDlg ;;close dialog; let ShowDialog return 0
,,case 2
,,DT_Ok hDlg ;;close dialog; let ShowDialog return 1; also populates dialog variables
,
,case WM_COMMAND goto messages2 ;;we receive this message when some controls want to notify parent dialog about some events
ret
;messages2
sel wParam
,case EN_CHANGE<<16|3 ;;text changed in edit control
,_s.getwintext(lParam); if(_s.len>3) _s.remove(0 _s.len-3); EditReplaceSel(lParam 0 _s 1) ;;if text length > 3, remove characters from beginning (then text will scroll)
,SetTimer hDlg 1 10000 0 ;;close after 10 s
,
,case IDOK ;;OK button or Enter
,SetTimer hDlg 2 1000 0 ;;close after 1 s
,ret ;;prevent closing dialog now
,
,case IDCANCEL ;;Cancel button or Esc
ret 1
;_____________________________________________________________________

;MORE HELP
;1. This function allows you to execute code while the dialog is open and this way change dialog behavior.
;2. It is implicitly called whenever the dialog receives a message.
;3. It is called multiple times. Values of local variables are not preserved. Instead you can use thread variables or SetProp/GetProp.
;4. To execute code when the dialog is just created but still invisible, place the code under case WM_INITDIALOG. Add tabs at the beginning, as well as under other case's.
;5. To execute code when the dialog is being destroyed, place it under WM_DESTROY.
;6. To execute code when the user clicks OK, place the code under case IDOK. When Cancel - under IDCANCEL.
;7. To prevent closing the dialog on OK/Cancel, return 0 under case IDOK/IDCANCEL.
;8. To insert code that must be executed on other events (messages), use the Events button in the dialog editor.
;,;For example, to execute code when button 3 (3 is its id) clicked, in the dialog editor click the button, click Events, OK.
;,;It inserts 'case 3' line. Add your code below.
;9. To generate code that shows the dialog, click Apply in the dialog editor. Copy the code from the QM output.
;,;You can insert code that shows the dialog in other macro(s) or functions(s), or in this function.
;,;If you insert it in this function, insert it below 'if(hDlg) goto messages'.
;,;To run the dialog instead of edit when you click the Run button, right click the selection bar by the '\Dialog_Editor' line. To edit again, right click there again.
;10. While the dialog is open, dialog variables cannot be used. Instead use window/control functions to get/set values of controls.
;,;To set/get text, use something like _s.getwintext(id(3 hDlg)).
;,;To see if a checkbox checked, use something like if(but(id(3 hDlg))) ... .
;,;To get selected listbox item, use something like _i=LB_SelectedItem(id(3 hDlg)). Use CB_SelectedItem for combobox.
;,;In control messages, lParam usually is control handle, and can be used instead of id(n hDlg).
;11. While the dialog is open, dialog variables can be used if you use an user-defined type for them. You can set it in dialog editor options.
;,;But you must explicitly populate the variables using DT_GetControls. Example:
;,;DIALOGVARTYPE& dv=+DT_GetControls(hDlg)
;,;out dv.e3
;,;if(val(dv.c4Che)) out "checked"
;12. To return a value can be used SetWindowLong or DT_Ret. Example: ret DT_Ret(hDlg 1000).
;13. The dialog procedure used in QM is similar to the dialog box procedure that is documented in the MSDN Library on the Internet.
;,;All the messages (events) are documented there.
;,;The dialog editor's Events dialog contains some mostly used messages but you can use any messages.
;,;Don't use EndDialog and similar functions. QM dialogs are managed by QM extensions.
#7
Wow...that's the dialog bible right there! What a great resource for folks just coming into QM! I really wish I had had that sitting around when I build my first dialogs!
#8
Well, this Bible looks like it's still in Hebrew :?

I'm getting somewhere, but I'm getting frustration too.

-How to give the dialog set coordinates? I've been searching/trying, but all I can do is stretch the dialog or make it appear soemwhere not visible. I want to use 3 huge dialogs at the top of the screen - for MIDI Bank Select MSB, LSB, and Program change. I did discover DS_Center, but I can't define x y.
-How to trigger the code/run dialog - then populate with the triggering key (digit 1)? I'm presently using delays when converting MIDI to numeric to wait for the dialog to exist to send initial key but perhaps it can be simpler? I probably need the delay anyway to wait for the dialog to be enable. Do I need unique dialogs for each number?
-How to kill existing display before (re)opening another to prevent overlap? I'm using a font called LCD (11 segment calculator like). I didn't mention it but I've also got previous/next buttons to step throught the values, which can happen very rapidly. This is working, as the MIDI value is static [dialog(display -key 24- enter)] instead of dynamic [dialog(display -key 2- display -key 4- enter)]. When a program change occurs, the static value is converted into text, the dialog is opened, a delay, the text is inserted then enter. This works fine but the display looks "sluggish" as I have it set for a 2 second delay after IDOK. Killing existing dialog is actually necessary, as this MIDI translation method will open the dialog when Any Program Change is executed. I do realize I could use 2 dialogs, one dynamic and one static (dynamic IDOK no delay, static IDOK delay=(2) second). That way the dynamic would display numbers as typed, enter would immediately end that display, and instantly the static display would appear with the delayed close. Prv/Nxt would only trigger static display.

I Am learning something in all this... I realize you've given me bundles of info, I'm really in new territory and don't know how to understand a lot of it (yet).
My initial idea was to use OSD with Ctr+Alt+Win+(1-9 and 0), whcih wouldn't cause any active window confilicts. I wonder if that would simplify anything? In translating MIDI to QWERTY I try to avoid standard keystorkes, instead using special multikey keystrokes and letting QM send the standard key so I can have arguments, and to avoid any keystroke 'slipping through'. Either way, you've given me much essential info, It's just a slow process comprehending it all for my specific purposes... Many thanks.


Attached Files Image(s)
   
#9
I moved ShowDialog etc to a separate function DTN_Show.
You can create many functions, like DTN_1, with different triggers, and call DTN_Show with different values.
This code does all that you asked (sets x y, initial value, closes previous OSD). But maybe this is not the best way to do what you need.

Function DTN_1
Trigger C1     Help - how to add the trigger to the macro
Code:
Copy      Help
DTN_Show "MSB" "1" 300 100 0xff0080 40 ;;show OSD "green", with initial value "1", at 300 100, color 0xff0080, font size 40

Function DTN_Show
Code:
Copy      Help
;/DTN_1
function $OsdId $trigger x y color size

;Shows OSD three-numbers dialog.

;OsdId - any string that identifies the OSD. This function kills this OSD if exists.
;trigger - initial value to display.
;x, y - coordinates. If 0 - center. If negative - from right.
;color - text color in format 0xBBGGRR.
;size - font size.


;THIS CODE SHOWS DIALOG
;Basic code is created by the Dialog Editor. We add more code.

;Declare dialog variables. Optionally you can assign initial values.
str controls = "0 3"
str d0 e3
d0=F"dialog_three_numbers_osd_{OsdId}"
e3=trigger

clo win(d0 "#32770"); err ;;kill existing dialog

type DTN_DATA color size ;;define type to pass more info to the dialog procedure
DTN_DATA z; z.color=color; z.size=size ;;create and set variable of that type

;Show dialog.
;This code waits until the dialog is closed.
;If closed using OK button or Enter or by calling DT_Ok(), it returns 1, else it returns 0 and we exit (then macro ends).
if(!ShowDialog("dialog_three_numbers_osd" &dialog_three_numbers_osd &controls 0 0 0 0 &z x y)) ret
;Now we have dialog variables and can do whatever we want.
out e3
;...

;then macro ends

Function dialog_three_numbers_osd
Code:
Copy      Help
;/DTN_1
;\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

;I use " /DTN_1" at the beginning for easier testing. When you run this function, instead runs DTH_1. To open dialog editor instead, move the second line to the beginning.
;_____________________________________________________________________

;This is dialog definition, craeted by the Dialog Editor.
;In Dialog Editor we can set certain styles for dialog and edit control.
;For example, remove dialog caption, and make edit control text right-to left.

;BEGIN DIALOG
;0 "" 0x90000A48 0x88 0 0 67 39 ""
;3 Edit 0x50002082 0x0 0 0 70 40 ""
;END DIALOG
;DIALOG EDITOR: "" 0x2030300 "DTN_Show" "" ""
;_____________________________________________________________________

;THIS CODE IS DIALOG PROCEDURE
;Basic code is created by the Dialog Editor. We add more code.
;It runs while the above code is waiting in ShowDialog.
;It runs many times. It runs whenever the dialog receives a message (WM_INITDIALOG, WM_TIMER, etc).
;Messages are used to notify dialog about various events. Windows messages are documented in MSDN library.
;We add case for each message that we need. Then code below the case is executed whenever the dialog receives the message.

;messages
__Font-- t_f
sel message
,case WM_INITDIALOG ;;we receive this message when dialog is just created, but still invisible
,;get data passed through ShowDialog's param
,DTN_DATA& z=+DT_GetParam(hDlg)
,
,;set font for the edit control
,t_f.Create("LCD" z.size 1)
,t_f.SetDialogFont(hDlg "3")
,t_f.SetDialogFontColor(hDlg z.color "3")
,
,Transparent hDlg 255 0xffffff ;;make dialog transparent, using white as transparent color
,SetTimer hDlg 1 10000 0 ;;close after 10 s
,
,case WM_TIMER ;;we repeatedly receive this message if we called SetTimer; wParam is argument 2 of SetTimer; period is argument 3 of SetTimer
,sel wParam
,,case 1
,,DT_Cancel hDlg ;;close dialog; let ShowDialog return 0
,,case 2
,,DT_Ok hDlg ;;close dialog; let ShowDialog return 1; also populates dialog variables
,
,case WM_COMMAND goto messages2 ;;we receive this message when some controls want to notify parent dialog about some events
ret
;messages2
sel wParam
,case EN_CHANGE<<16|3 ;;text changed in edit control
,_s.getwintext(lParam); if(_s.len>3) _s.remove(0 _s.len-3); EditReplaceSel(lParam 0 _s 1) ;;if text length > 3, remove characters from beginning (then text will scroll)
,SetTimer hDlg 1 10000 0 ;;close after 10 s
,
,case EN_SETFOCUS<<16|3 ;;edit control focused
,SendMessage lParam EM_SETSEL -1 -1 ;;remove selection and move caret to the end of text
,
,case IDOK ;;OK button or Enter
,SetTimer hDlg 2 1000 0 ;;close after 1 s
,ret ;;prevent closing dialog now
,
,case IDCANCEL ;;Cancel button or Esc
ret 1
;_____________________________________________________________________

;MORE HELP
;1. This function allows you to execute code while the dialog is open and this way change dialog behavior.
;2. It is implicitly called whenever the dialog receives a message.
;3. It is called multiple times. Values of local variables are not preserved. Instead you can use thread variables or SetProp/GetProp.
;4. To execute code when the dialog is just created but still invisible, place the code under case WM_INITDIALOG. Add tabs at the beginning, as well as under other case's.
;5. To execute code when the dialog is being destroyed, place it under WM_DESTROY.
;6. To execute code when the user clicks OK, place the code under case IDOK. When Cancel - under IDCANCEL.
;7. To prevent closing the dialog on OK/Cancel, return 0 under case IDOK/IDCANCEL.
;8. To insert code that must be executed on other events (messages), use the Events button in the dialog editor.
;,;For example, to execute code when button 3 (3 is its id) clicked, in the dialog editor click the button, click Events, OK.
;,;It inserts 'case 3' line. Add your code below.
;9. To generate code that shows the dialog, click Apply in the dialog editor. Copy the code from the QM output.
;,;You can insert code that shows the dialog in other macro(s) or functions(s), or in this function.
;,;If you insert it in this function, insert it below 'if(hDlg) goto messages'.
;,;To run the dialog instead of edit when you click the Run button, right click the selection bar by the '\Dialog_Editor' line. To edit again, right click there again.
;10. While the dialog is open, dialog variables cannot be used. Instead use window/control functions to get/set values of controls.
;,;To set/get text, use something like _s.getwintext(id(3 hDlg)).
;,;To see if a checkbox checked, use something like if(but(id(3 hDlg))) ... .
;,;To get selected listbox item, use something like _i=LB_SelectedItem(id(3 hDlg)). Use CB_SelectedItem for combobox.
;,;In control messages, lParam usually is control handle, and can be used instead of id(n hDlg).
;11. While the dialog is open, dialog variables can be used if you use an user-defined type for them. You can set it in dialog editor options.
;,;But you must explicitly populate the variables using DT_GetControls. Example:
;,;DIALOGVARTYPE& dv=+DT_GetControls(hDlg)
;,;out dv.e3
;,;if(val(dv.c4Che)) out "checked"
;12. To return a value can be used SetWindowLong or DT_Ret. Example: ret DT_Ret(hDlg 1000).
;13. The dialog procedure used in QM is similar to the dialog box procedure that is documented in the MSDN Library on the Internet.
;,;All the messages (events) are documented there.
;,;The dialog editor's Events dialog contains some mostly used messages but you can use any messages.
;,;Don't use EndDialog and similar functions. QM dialogs are managed by QM extensions.
#10
I'd hoped to come back with flying colors, but no so yet...

The (DTN_Show) functionality is great. Dynamic entry is working properly as requested with a one caveat - the Enter key doesn't end the input stage/reactivate previous window. That may ease the difficulty I'm having (re)allocating static entry which isn't coeexisting with dynamic now. I've been poking around attempting a fix with varied results. I think Enter ending input will help as a type of footer message - I'm deactivating the dialog effectively as below, but it seems Enter should have a more discreet action. Canceling the dialog returns to previously active window as expected.

Function KP_Close_KC_AL
Trigger CWq     Help - how to add the trigger to the macro
Code:
Copy      Help
ifa(win("dialog_PRG_PRG" "#32770"))
,key Y
,act
,end

ifa(win("dialog_LSB_LSB" "#32770"))
,key Y
,act
,end

ifa(win("dialog_MSB_MSB" "#32770"))
,key Y
,act
,end

else end

The above method works well enough for dynamic entry, but my previous method for static entry is unreliable - after Enter new numbers are often appended to an existing dialog before it's timer closes. I'm dealing more with the triggering process now, which is nearly a different matter, but it should be the final stage.
Just to note, an MSB message will 'bang/display' LSB and PRG, and an LSB message will 'bang/display' PRG, which are static input functions. I also need to build a trigger to bang/display MSB LSB PRG to see all present values, all static mechanisms (but not exactly what I asked for originally.) I originally expected I'd be able to "easily" adapt code for all this, but I overestimated.

Below is an example of a digit trigger for reference. I'm open to any suggestion.

Function KP_1_KC_PRG_AL
Trigger CAW1     Help - how to add the trigger to the macro
Code:
Copy      Help
ifa-(win("dialog_PRG_PRG" "#32770"))
,PRG_Show "PRG" "1" 780 1 0x00FFFF 220
,end

ifa(win("dialog_PRG_PRG" "#32770"))
,key 1
,end

else end

If you test the below attachment you'll see the difference between using the (Q) vs Enter then typing in new numbers.


Attached Files
.qml   MSB LSB PRG.qml (Size: 28.84 KB / Downloads: 355)
#11
After overlooking the above code I realized errors. I thought I had cleaned everything up....
#12
wait, i"ll make simpler, with OSD
#13
with OSD, without dialog


Attached Files
.qml   MLP.qml (Size: 3.83 KB / Downloads: 359)
#14
I apologize for all the requests, and I really thank you for all the efforts. I ended up reverting to the last Dialog method, as that was far easier to allocate than the OSD way, only to realize that the Dialog system is nearly perfect. The problems I'd been having all stemmed from the timing of the auto static keys input execution from the MIDI to text conversion. I haven't perfected it all quite yet, but It's just a matter of - time.
Again, thanks for all you've done, your coding is immaculate, I'm always impressed.


Forum Jump:


Users browsing this thread: 1 Guest(s)