Announcement

Collapse
No announcement yet.

Text Cursor Width

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Text Cursor Width

    Terminology not the same: TextCursorThickness (Settings) vs CaretWidth (Registry).

    Windows automatically changes the text cursor height to match the font height but by default does not change the text cursor width.

    In earlier programs I've used API to set the width of the text cursor to a value that grows in sync with changes in font size. That usually works but has a few quirks, no doubt because my code doesn't cover all possibilities.

    Windows 10 Settings now allows me to change the cursor width but I want to to that programmatically, such as in this ...

    HKEY_CURRENT_USER\Control Panel\Desktop
    • Select Desktop then in the right window pane double click on CaretWidth DWORD. Under Base select Decimal then in the Value data field type in a number between 1 – 20 for the cursor thickness you want, and click OK.
    After registry value changed, it requires trigger to apply these update.

    It can be done using SystemParametersInfo function with SPIF_UPDATEINIFILE and SPIF_SENDCHANGE to writes the new system-wide parameter setting to the user profile and broadcasts the WM_SETTINGCHANGE message after updating the user profile.
    But I would rather not directly change a users Registry settings so I'm looking for an alternative way to make the change.

    I'd change it within the app when the font size is changed, then restore it when the app closes.

    As for the "trigger", my quick test showed that the Registry change takes place immediately, at least in the couple of apps that I tried.

    The change made in Settings takes effect immediately.

  • #2
    Have you looked at SystemParametersInfo SPI_GETCARETWIDTH and SPI_SETCARETWIDTH ?

    https://docs.microsoft.com/en-us/win...arametersinfoa

    Comment


    • #3
      Not sure I understand. I have used CreateCaret for creating carets for Insert / Overwrite (wide) use in local apps. There can be only one - MS speaks: "The caret is a shared resource; there is only one caret in the system. A window should create a caret only when it has the keyboard focus or is active. The window should destroy the caret before losing the keyboard focus or becoming inactive".
      I usually do DestroyCaret under WM_KILLFOCUS...

      Comment


      • #4
        Howdy Borje!

        Yep, to change the width I have been using CreateCaret for several years, along with DestroyCaret as you suggest. It generally works but I've seen that the newly created caret doesn't alwaysblink. I've also seen that the increased width sometimes isn't shown. It's easy to assume that my code is at fault, but in lieu of finding that possible change, I thought I've give the Window 10 setting a try.

        Howdy, Stuart!

        No, I was not aware of SPI_SetCaretWidth. You'd think I'd have run across it back when I first searched for a solution and started using CreateCaret but I apparently missed it! I'll go give it try. Thanks!

        Comment


        • #5
          Howdy Stuart!

          Yep, this seems to work ...

          Code:
          Local retVal, w As Long
          w = 20
          retval = SystemParametersInfo(%SPI_SETCaretWidth,0,ByVal w,%SPIF_SENDCHANGE) '1=default 20=custom
          I think when I started using CreateCaret I didn't realize that the caret height was handled automatically, so I used CreateCaret to allow me to set both. So perhaps I didn't keep looking for just the SPI_ option. That's a bit far back in time and the thought process has long since dropped into oblivion!

          Thanks again!

          Comment


          • #6
            Originally posted by Gary Beene View Post
            Howdy Stuart!

            Yep, this seems to work ...

            Code:
            Local retVal, w As Long
            w = 20
            retval = SystemParametersInfo(%SPI_SETCaretWidth,0,ByVal w,%SPIF_SENDCHANGE) '1=default 20=custom
            I think when I started using CreateCaret I didn't realize that the caret height was handled automatically, so I used CreateCaret to allow me to set both. So perhaps I didn't keep looking for just the SPI_ option. That's a bit far back in time and the thought process has long since dropped into oblivion!
            Whatever you do, DON"T FORGET THE "BYVAL" !!!
            (Tried to do a demo last night. Until I saw this post, I couldn't see why I was getting a caret that was 1701716 pixels wide all the time (I have to keep logging out and in again to get it back to the default size)

            Comment


            • #7
              Two approaches ... CreateCaret and SPI_SetCaretWidth.

              The SPI code is certainly simpler, but only by a dozen lines of code.

              Playing with both, I found that both stop blinking after a period of time. But I don't see that with non-PBWin applications in general. For example, this browser keeps the text cursor blinking non-stop. The PowerBASIC IDE caret does stop blinking.

              Also, neither the browser nor IDE display the wide cursor. For that matter, a 2nd PBWin app does not display the wide cursor.

              There are details, it would seem, to work out!

              Code:
              'Compilable Example:
              #Compile Exe
              #Dim All
              %Unicode = 1
              #Include "Win32API.inc"
              
              Enum Equates Singular
                 IDC_TextBox = 500
              End Enum
              
              Global hDlg,hEdit As Dword, FontSize As Long
              
              Function PBMain() As Long
                 FontSize = 72
                 Dialog Default Font "Tahoma", FontSize, 1
                 Dialog New Pixels, 0, "PowerBASIC",300,300,400,200, %WS_OverlappedWindow To hDlg
                 Control Add TextBox, hDlg, %IDC_TextBox,"Text", 10,10,380,150
                 Control Handle hDlg, %IDC_TextBox To hEdit
                 Dialog Show Modal hDlg Call DlgProc
              End Function
              
              CallBack Function DlgProc() As Long
                 Select Case Cb.Msg
                    Case %WM_InitDialog
                       ResetCaret hEdit
                    Case %WM_SetCursor
                       ResetCaret hEdit
                 End Select
              End Function
              
              Sub RESetCaret(hWnd As Dword)
                 DestroyCaret()
                 CreateCaret(hWnd, ByVal %NULL, 20, 1.5*FontSize)
                 ShowCaret(hWnd)
              End Sub
              Code:
              'Compilable Example:
              #Compile Exe
              #Dim All
              %Unicode = 1
              #Include "Win32API.inc"
              
              Enum Equates Singular
                 IDC_TextBox = 500
              End Enum
              
              Global hDlg,hEdit As Dword, FontSize As Long
              
              Function PBMain() As Long
                 Local retVal, w As Long
                 FontSize = 72
                 Dialog Default Font "Tahoma", FontSize, 1
                 Dialog New Pixels, 0, "PowerBASIC",300,300,400,200, %WS_OverlappedWindow To hDlg
                 Control Add TextBox, hDlg, %IDC_TextBox,"Text", 10,10,380,150
                 Control Handle hDlg, %IDC_TextBox To hEdit
                 w = 20
                 retval = SystemParametersInfo(%SPI_SETCaretWidth,0,ByVal w,%SPIF_SENDCHANGE) '1=default 20=custom
                 Dialog Show Modal hDlg
              End Function

              Comment


              • #8
                GetCaretBlinkTime / SetCaretBlinkTime? Don't know about less code - you'll always need a callback procedure, so size difference i "real" code would be small.

                Comment


                • #9
                  I found that both stop blinking after a period of time.
                  Get / SetCaretTimeout

                  New to Windows10 - default set in Registry (Computer\HKEY_CURRENT_USER\Control Panel\Desktop\CaretTimeout) = 0x00001388 (5000) ie 5000 mS

                  Can be accessed by (undocumented) %SPI_GetCaretTimeout and %SPI_SetCaretTimeout

                  'New Winuser.h values
                  %SPI_GETCARETTIMEOUT = &h2022
                  %SPI_SETCARETTIMEOUT = &h2023
                  Last edited by Dave Biggs; 18 Jun 2021, 05:54 AM. Reason: Add new equates values for use in calls to SystemParametersInfo()
                  Rgds, Dave

                  Comment


                  • #10
                    Whatever you do, DON"T FORGET THE "BYVAL" !!! [At point of call]
                    If you "need" a point of call BYVAL override, your DECLARE statement is probably wrong. If your DECLARE uses 'BYVAL newwidth' then no POC override is needed.
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      é
                      Originally posted by Michael Mattias View Post

                      If you "need" a point of call BYVAL override, your DECLARE statement is probably wrong. If your DECLARE uses 'BYVAL newwidth' then no POC override is needed.
                      MS documentation:
                      pvParam
                      Type: PVOID
                      A parameter whose usage and format depends on the system parameter being queried or set.

                      If you read the documentation, will see that in some cases this is an actual value such as caret width. In other cases it is a pointer to a variable or structure



                      José Roca Includes Declaration:
                      DECLARE FUNCTION SystemParametersInfoA IMPORT "USER32.DLL" ALIAS "SystemParametersInfoA" ( _
                      BYVAL uiAction AS DWORD _ ' __in UINT uiAction
                      , BYVAL uiParam AS DWORD _ ' __in UINT uiParam
                      , BYREF pvParam AS ANY _ ' __inout_opt PVOID pvParam
                      , BYVAL fWinIni AS DWORD _ ' __in UINT fWinIni
                      ) AS LONG ' BOOL


                      PB Includes Declaration:
                      DECLARE FUNCTION SystemParametersInfoA LIB "User32.dll" _
                      ALIAS "SystemParametersInfoA" (BYVAL uAction AS DWORD, _
                      BYVAL uParam AS DWORD, lpvParam AS ANY, BYVAL fuWinIni AS DWORD) AS LONG
                      , BYREF pvParam AS ANY _ ' __inout_opt PVOID pvParam


                      So how would you declare it to not require ByVal ?

                      Comment


                      • #12
                        Howdy, Dave!

                        SetCaretTimeout? Well, that's a hoot. It never occurred to me that someone would specifically want the cursor to stop blinking. Here I was blaming my code for somehow stopping the blink and all along it was a "feature" of Windows!

                        Thanks for the post!

                        Comment


                        • #13
                          So how would you declare it to not require ByVal ?
                          I had not read the doc for SystemParametersInfo() when I made that comment. Hell, I didn't even look at what WinAPI function was getting called. I was making more a "general" reference to functions with fixed arguments. These functions with options which are sometimes pointers, sometimes values can be hairy.

                          So, I could set up DECLARE statements for TWO functions... One passing param three by value (BYVAL LONG) and one passing an address (AS ANY) .

                          e.g.
                          Code:
                          DECLARE FUNCTION SystemParametersInfoABYREF LIB "User32.dll" _
                          ALIAS "SystemParametersInfoA" (BYVAL uAction AS DWORD, _
                          BYVAL uParam AS DWORD, lpvParam AS ANY, BYVAL fuWinIni AS DWORD) AS LONG
                          , BYREF pvParam AS ANY _ ' __inout_opt PVOID pvParam
                          
                          and
                          DECLARE FUNCTION SystemParametersInfoABYVAL LIB "User32.dll" _
                          ALIAS "SystemParametersInfoA" (BYVAL uAction AS DWORD, _
                          BYVAL uParam AS DWORD, BYVAL pvParam AS LONG, BYVAL fuWinIni AS DWORD) AS LONG
                          , BYREF pvParam AS ANY _ ' __inout_opt PVOID pvParam
                          Then I would CALL the version appropriate to the operation of SystemParametersInfo I want.

                          Or, maybe I'd just use a BYVAL at point of call.

                          As I said above, my point was more about a generic case where functions don't have these flexible arguments. You read in the papers about "anti-vaxxers"... well, I am an "anti-point of call overrider."

                          MCM
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            Originally posted by Michael Mattias View Post
                            . You read in the papers about "anti-vaxxers"... well, I am an "anti-point of call overrider."
                            Must resist snide remark, must resist....
                            <bites tongue>




                            Comment


                            • #15
                              Must resist snide remark, must resist....
                              <bites tongue>
                              Don't resist! Doc says you have to let it out! Send it via PM!

                              Michael Mattias
                              Tal Systems (retired)
                              Port Washington WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment


                              • #16
                                So Gary can you show how to program to set the caret to blink longer than 5000ms in the PB programs? Without changing it in the registry. Check the time out before changing it and then change it. When exiting the program change it back.

                                Comment


                                • #17
                                  can you show how to program to set the caret to blink longer than 5000ms in the PB programs? Without changing it in the registry. Check the time out before changing it and then change it. When exiting the program change it back.
                                  What have you tried so far? If your code not working, post it here and we can all take a look.
                                  Michael Mattias
                                  Tal Systems (retired)
                                  Port Washington WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                  • #18
                                    I haven't tried any code I don't understand the entire concept. Don't even know how to start.

                                    Comment


                                    • #19
                                      lRes = GetCaretBlinkTime returns current blink time (580 ms in my system) and SetCaretBlinkTime(iNewBlinkTime) sets new blink time. Must remember to do this under for example %WM_NCACTIVATE and also reset original blink time (very important) when program loses focus / closed. Why anyone ever would want to change blink time is beyond me, but at least that's one way of doing it. As always, when dealing with system wide functions and values it's always important to store original values and restore so changes doesn't affect other program and do potentional damage.

                                      Comment


                                      • #20
                                        Howdy, Borje!

                                        Why anyone ever would want to change blink time is beyond me
                                        When you have several controls on the screen you'd like to know which one has focus so you know where your keyboard action will be applied.

                                        WIthout blinking, the default caret is very thin and difficult to see, especially if it is located to the left or right side of a line in the control. This difficulty is especially true for my low vision users for whom something like 20px width is needed to see the caret successfully. Changing the width and enabling blinking are two ways to help my customers user the PC more easily.

                                        It's a very difficult world out there for low vision (legally blind) users, and I work to make my apps improve their ability to access computer capabilities as easily as you and I do!

                                        Comment

                                        Working...
                                        X