No announcement yet.

User Controls

  • Filter
  • Time
  • Show
Clear All
new posts

  • User Controls

    Edit: Updated w/ Dominic's comments plus some bug fixes.
    Edit: Cleaned up/sped up drawing, added options to change sorting
    Edit: Implemented lazy calcs and layout, fixed a word sizing bug with ignore.
    Edit: Removed inline source, added an emphasis color, split sizing distribution to even sizing out, added an ignore count, allowed adding in addition to loading, added build.cmd to compile from command line, clean.cmd to remove all but source files.
    Edit: Created a DLL, added an example using the DLL.

    I wrote some user controls: TagCloud and a LinkLabel. Their my first PB native window controls from scratch, so I appreciate feedback on what I didn't do, or could do better. has the definitions and main.bas has an example of use.
    Last edited by Larry Charlton; 12 Jun 2011, 05:28 AM.
    Sometimes life's a dream, sometimes it's a scream

  • #2
    For us sometimes lazy folks, could you provide a zip file with all of the files in it?


    • #3
      I will just critique the lcWordCloud control, but the others have the same drawbacks.
        PostMessage( GetParent(hWnd), %WCN_Clicked, @[email protected][@p.index].cnt, VarPtr(@[email protected][@p.index].key) )
      This way of notififying the parent window will make the control difficult to work with
      for the following reasons:

      1) Programmers using this control will have to ensure that the numeric value of WCN_Clicked
      does not conflict with other messages that are sent to the parent.

      2) If there are multiple instances of the control on the parent, there is no way to tell which
      control was clicked.

      In addition to the improper construction of the notification, you defined it as a class specific message.
      Messages in Windows are divided into the following sections:
      | Message section          |           Meaning                                                   |
      | 0x00000000 to WM_USER-1  | All standared window messages.  This inclues all messages with the  |
      |                          | WM_ prefix.  Programmers should never define messages in this range.|
      | WM_USER to WM_APP - 1    | Class-specific integer messages.                                    |
      | WM_APP to 0x0000BFFF     | Application-specific integer messages used for a single process.    |
      | 0x0000C000 to 0x0000FFFF | System-global string messages. The values return by the             |
      |                          | RegisterWindowMessage function fall in this range.                  |
      | 0x00010000 to 0xFFFFFFFF | Messages reserved by Microsoft for exclusive use  by the system.    |                                
      + -------------------------+---------------------------------------------------------------------+
      From the table above, WCN_Clicked is a class specific message, therefore, it sould be a message that
      is sent to the window procedure for the control not to the window procedure for the parent window.

      To avoid possible conflicts with other messages, notifications from controls should be sent via

      Notifications sent via WM_COMMAND are constructed as follows.

      SendMessage|PostMessage hWndParent, WM_COMMAND, MAKWRD(<control ID>, <notification code>), hWndControl
      Note, SendMessage or PostMessage can be used to send WM_COMMAND notifications.

      In the case of this control, WM_COMMAND is inadequate. Therefore, you will have to use WM_NOTIFY.

      Note, notifications sent via WM_NOTIFY must use SendMessage not PostMessage.

      The following is an example of how to send the WCN_Clicked notification via WM_NOTIFY.

      1) First, define a structure.

        hdr       AS NMHDR          ' structure that contains information about this notification message
        cnt       LONG
        pszKey    ASCIIZ PTR
      END TYPE
      2) Then fill it with the appropriate values and send it using SendMessage.

      SUB WordCloud_Click( BYVAL hWnd AS DWORD )
      	LOCAL p     AS WordCloudData PTR
      	LOCAL i     AS LONG
      	p = GetWordCloudData( hWnd )
      	@p.flash = 1
      	CALL WordCount_RecreateImage( hWnd )
      	InvalidateRect( hWnd, BYVAL %NULL, 0 )
      	THREAD CREATE WordCloud_Flashing( hWnd ) TO i
      	THREAD CLOSE i TO i
        hWndParent = GetParent(hWnd)
        wCtrlId    = GetDlgCtrlID(hWnd)
        twc.hdr.hwndFrom = hWnd
        twc.hdr.idFrom   = GetDlgCtrlID(hWnd)
        twc.hdr.code     = %WCN_CLICKED
        twc.cnt          = @[email protected][@p.index].cnt  
        twc.pszKey       = VARPTR(@[email protected][@p.index].key)                      
        SendMessage GetParent(hWnd), %WM_NOTIFY, GetDlgCtrlID(hWnd), BYVAL VARPTR(twc)
      END SUB
      3) In the window procedure for the parent window, the notification is processed as follows.

        LOCAL v       AS ASCIIZ PTR   
        LOCAL ptnmhdr AS NMHDR PTR
        SELECT CASE wMsg
          CASE %WM_NOTIFY
            ptnmhdr = lParam
            SELECT CASE @ptnmhdr.idfrom
              CASE <whatever id the control was created with>
                SELECT CASE @ptnmhdr.code
                  CASE %WCN_CLICKED 
                    ptwc = lParam
                    MSGBOX @[email protected] & " = " & STR$(@ptwc.cnt) 
                END SELECT
            END SELECT
        END SELECT
        FUNCTION = DefWindowProc( hWnd, wMsg, wParam, lParam )
      The processing of notifications by the parent window brings to light another problem with the controls, no
      unique numeric identifiers. The programmer can always use CreateWindowEx to get around this limitation with
      the NewWordCloud function.

      By the way, did you use code by Borje as a model?
      Dominic Mitchell
      Phoenix Visual Designer


      • #4
        Wow thanks! I'll update the notifications for sure, it felt weird and I wasn't sure why

        No I just sort of sat down and hacked at the control, I had a model of what I wanted that I'd implemented in .Net, but it was a compound control and tended to slow down with many words.

        I think I'm going to try to do away with the double buffering and just extend information I have on the words. I think that should speed up the control as well as use less memory.

        Thanks again for taking the time to look it over.
        Sometimes life's a dream, sometimes it's a scream


        • #5
          I created my first PB DLL, so if you see anything odd with how it was done, please let me know.
          Sometimes life's a dream, sometimes it's a scream


          • #6
            Rounded out some of the messages so you can both get and set control values, fixed a couple of bugs, switched to enums for constants, made message names a bit clearer, updated the pb demo, created control using the dll with a demo similar to the pb one. Note, if you want to design the program you need control.dll in somewhere in your path.

            Currently the control can be built as a DLL or compiled directly into the exe.

            The library isn't working right now for some reason, still trying to figure out why.
            Sometimes life's a dream, sometimes it's a scream


            • #7
              Fixed a couple of rendering artifacts
              Sometimes life's a dream, sometimes it's a scream


              • #8
                Added the ability to change the font, added code to release resources and class registration when the last instance of word cloud was destroyed.
                Sometimes life's a dream, sometimes it's a scream


                • #9
                  Removed some spurious files and included the compiled versions and dll's. You can get it here.
                  Sometimes life's a dream, sometimes it's a scream


                  • #10
                    Hi Larry,

                    Very nice. I never thought much about how the tag clouds are generated. Now that you brought it up, I can see generators all over web - from simple to very complicated.

                    I started to make a few suggestions on changing the output, but all you have to do is type tagcloud into Google to see a large number of variations. Random positioning (not follow lines) and transparency to match frequency are two that caught my eye. Also, an animated output would be cool - moving the words around.

                    I released some code a while back on doing automatic placement algorithms - positioning items most efficiently in a 2D area. It might be of some help to you in creating a non-line based layout of words. It wasn't exactly random positioning but might be a start, if you're interested.


                    • #11
                      What does a moving a word around mean? Do you mean jiggling the words when you mouse over them or something else?

                      Transparency seems intriguing.

                      I finally found your post on positioning about 15 pages back on topics you've started ... Cool post.

                      Anyway thanks for the ideas, I was thinking of revisiting this. At some point it really must have a scroll bar when it exceeds it's area and I was thinking of changing it to use classes and the font, renderers, and layout stuff I've been doing with InfoView.

                      Some other thoughts I've had in the back of my mind for a while has been doing a "spiral" layout, with the most frequent word in the center tapering off to lower frequency words out a ways. Another thought I had was word hinting, i.e. when you hover over a word, highlighting other words based on similarity of definition, that might be too ambitious for me Also thought about using translation tables to consolidate the words used. For example: Hot, Heat, Warm, Heating, Smoking might all get mapped to a single word. The maps I wrote could help with that.

                      I've been sitting back thinking about what I could do with infoview. I think it's flexible enough, but now I'm thinking about how to make it easy to use. This might provide just the right break, thanks.
                      Sometimes life's a dream, sometimes it's a scream


                      • #12
                        Re: moving ...

                        Here is an animated tagcloud that prompted my comment.


                        • #13
                          OMG your mean, that looks like too much fun. Be back in a few weeks
                          Sometimes life's a dream, sometimes it's a scream


                          • #14

                            I've done some looking for tag cloud algorithms. Here's some random stuff I've found - links and a few quotes.

                            I haven't yet tried to put any of the ideas from these pages into code, but hope to give it a try tomorrow (today?).

                            Note the quote from the Wordle guy.

                            DotNet Article

                            Random space filling tiling of the plane

                            Random Non-Overlapping Distribution of Objects

                            Tag-Cloud Drawing: Agorithms for Cloud Visualization

                            Creating Tag Cloud Using R and Flash / JavaScript (SWFObject)

                            A Nearly Self-organizing Tag Cloud

                            Efficient Tag Cloud Algorithm

                            Wordle Guy:
                            I'm the creator of Wordle. Here's how Wordle actually works:

                            Count the words, throw away boring words, and sort by the count, descending. Keep the top N words for some N. Assign each word a font size proportional to its count. Generate a Java2D Shape for each word, using the Java2D API.

                            Each word "wants" to be somewhere, such as "at some random x position in the vertical center". In decreasing order of frequency, do this for each word:

                            place the word where it wants to be
                            while it intersects any of the previously placed words
                            move it one step along an ever-increasing spiral

                            That's it. The hard part is in doing the intersection-testing efficiently, for which I use last-hit caching, hierarchical bounding boxes, and a quadtree spatial index (all of which are things you can learn more about with some diligent googling).

                            Random Blog (lost the URL):
                            You can model this by a set of "free" rectangles, starting with single one with coordinates of 0,0, size (x, y). Each time you need to add one more rectangle, choose one of remaining "free" rectangles, generate new rectangle (with top-left coordinate and size such that it will be fully contained), and split that rectangle as well as any other overlapping "free" rectangle, such that children express remaining free space. This will result in 0 to 4 new rectangles (0 if new rectangle was exactly the size of old free rectangle; 4 if it's in the middle and so on). Over time you will get more and more smaller and smaller free areas, so rectangles you create will be smaller as well.

                            Ok, not a very elaborate explanation, it's easier to show on whiteboard. But the model is one I used for finding starting location for newly cut'n pasted gui components; it's easy to keep track of available chunks of screen, and choose (for example) left or topmost such area.


                            • #15
                              There were some interesting links there, thanks! Placement / packing is an interesting problem. Personally from a use based perspective, I find alpha one of the best placements from a usability stand-point. I.e. when presented with a word cloud, can I find words that might be of interest while still seeing their relative importance. Frequency is also interesting in that I either have really important things early or late, and they naturally pack well, while not having as much of a word-cloudy feel. The downside being that I can't find anything... Having words on a line also helped me scan for words, because my eye could predict where the neighboring words were.

                              When placing the words I found I didn't asthetically like top or bottom aligned variants, I opted for middle alignment which worked fairly well, but I suspect, baseline will be even better.

                              So I'm thinking of trying a word cloud with a filter. Perhaps if I can limit what shows in the cloud based on some sequence of characters, having interesting placements might be more beneficial because they will already be limited to some dimension of the thing I'm looking for.

                              My original goal was to provide an alternative searching method, that assisted with finding something when you didn't know what it was. I often find the reason I can't find something is because I simply have different vocabulary than the source I'm searching. Might be interesting to couple word clouds with a thesaurus but unless anyone has a suggestion on where a free to distribute thesaurus in a usable format is, I'll probbbly limit myself to translators. Besides the thesauruses tend to be highly discipline focused.

                              In any case here's the link I used initially. I ended up splitting font size distribution in half because I found I often had clumps of frequencies, mostly a ton of smaller frequency words. By splitting the list in half based on frequency it tended to magnify the significant terms and balance size a bit better.

                              I'm looking forward to seeing what you come up with.
                              Last edited by Larry Charlton; 3 Oct 2011, 04:16 AM.
                              Sometimes life's a dream, sometimes it's a scream


                              • #16
                                Hi Larry,
                                One more time, someone (that would be you, this time) tossed out an idea, and then ZIP! - I lose days out of my life!

                                I wasn't interested in writing a control as you've done, but the creating of a word cloud was pretty interesting. So for the last day or so I've set aside my other projects to write a cloud generator app (gbCloud, of course!).

                                I feel like a horse that needs blinders in order to walk down the street in a straight line - in this case, the blinders are to protect me from random ideas which highjack my train of thought!

                                I'll try to post it tonight so I can get on with my life, such as it is!

                                Here's a sneak preview:


                                • #17
                                  Sweet looks like you've been having fun! Looking forward to see it.
                                  Sometimes life's a dream, sometimes it's a scream


                                  • #18
                                    Hi Larry!

                                    I can't believe it's been over 2 weeks since you tempted me into writing a tag cloud generator. What you had done looked like fun, so I decided to try one of my own.

                                    I just posted the result, called gbCloud, in the Source Code forums.

                                    Here are some examples of the types of clouds you can make.

                                    There were a lot of new things to learn to be able to make it happen. You probably saw a variety of posts I made, talking about issues that were giving me troubles.

                                    But, I'm pretty happy with the results. I just wish every project didn't take weeks to complete. I had a working version the first week, but hesitated to release something for public consumption/critique until it was more usable. Once I got it working, the list of "just one more feature" just kept getting longer. At this point, I only have a few other items on my feature list, so I thought it was time to share.