determining item at top of Listview gadget

Just starting out? Need help? Post your questions and find answers here.
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: determining item at top of Listview gadget

Post by Shardik »

Blue wrote:Does anyone know how to figure out, using only PB code, which item is currently at the top of a Listview gadget ?
I know how to get this information using some API function, but i'm looking into a solution that would work on any platform.
Blue wrote:But i'm looking for an all platforms solution, written with PB only code. :idea:
Some features can't be achieved without using API functions. And it's not necessary to rule out the use of API functions completely only because the resulting code wouldn't be cross-platform anymore. You simply have to implement the respective API functions for each OS. I have already put together a list with links to cross-platform code examples which use API functions to implement functions currently not available in PureBasic. And some of these functions have already become implemented natively as PB functions... :wink:

The above list contains already links to cross-platform examples to display a double-clicked row at the top of a ListIconGadget or display a selected row in the center of a ListIconGadget.

For your conveniance I have taken your code example and added the respective API functions for Linux and MacOS to determine the currently visible row at the top of your ListViewGadget. I have tested the modified example successfully on these operating systems:
- MacOS X 10.6.8 (Snow Leopard) x86
- Ubuntu 14.04 x64 with KDE
- Windows 7 x64 SP1

But this example has one problem which you wouldn't know if only working with Windows: in Windows the top row is normally always visible as a whole row whereras in Linux and MacOS it's possible that only a fraction of the current top row is visible. So as a result it's possible on Linux and MacOS that for example a top row of 0 is reported although the top row seems to be row 1 because only a very small (and nearly unvisible) part of top row 0 is displayed. To solve this problem the example would have to be extended to also count the displayed rows and define a top row fraction of for example a minimum of 80% which has to be visible to be reported as the currently visible top row...

Code: Select all

EnableExplicit

#lignes = 12 ; number of lines displayed by the ListView gadget

Enumeration
  #cmd_OK
  #input
  #liste
  #infos_LABEL
  #infos
EndEnumeration

CompilerIf #PB_Compiler_OS = #PB_OS_Linux
  ImportC ""
    gtk_tree_view_get_visible_range(*TreeView.GtkTreeView, *StartPath, *EndPath)
  EndImport
CompilerEndIf

Procedure.I GetVisibleTopRow(ListIconID.I)
  Protected VisibleTopRow.I

  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
      Protected *EndPath
      Protected *StartPath

      If gtk_tree_view_get_visible_range(GadgetID(ListIconID), @*StartPath, @*EndPath)
        VisibleTopRow = PeekI(gtk_tree_path_get_indices_(*StartPath))
        gtk_tree_path_free_(*StartPath)
        gtk_tree_path_free_(*EndPath)
      EndIf
    CompilerCase #PB_OS_MacOS
      Protected ContentView.I
      Protected EnclosingScrollView.I
      Protected VisibleRange.NSRange
      Protected VisibleRect.NSRect
      
      ; ----- Get scroll view inside of ListIconGadget
      EnclosingScrollView = CocoaMessage(0, GadgetID(ListIconID), "enclosingScrollView")
      
      If EnclosingScrollView
        ContentView = CocoaMessage(0, EnclosingScrollView, "contentView")
        ; ----- Get visible area
        ;       (automatically subtract horizontal scrollbar if shown)
        CocoaMessage(@VisibleRect, ContentView, "documentVisibleRect")
        ; ----- Subtract border width
        If CocoaMessage(0, EnclosingScrollView, "borderType") > 0
          VisibleRect\size\height - 5
        EndIf
        ; ----- Get visible top row
        CocoaMessage(@VisibleRange, GadgetID(ListIconID), "rowsInRect:@", @VisibleRect)
        VisibleTopRow = Int(VisibleRange\location)
      EndIf
    CompilerCase #PB_OS_Windows
      VisibleTopRow = SendMessage_(GadgetID(ListIconID), #LB_GETTOPINDEX, 0, 0)
  CompilerEndSelect

  ProcedureReturn VisibleTopRow
EndProcedure

If 0 = OpenWindow(0,10,10,300,200,"ListView",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  End
EndIf

ButtonGadget(#cmd_OK,180,175,60,20,"Go to >>>")
StringGadget(#input,244,175,30,20,"50", #PB_String_Numeric)

TextGadget(#infos_LABEL,20,175,48,20,"top item = ")
TextGadget(#infos,20+48+6,175,60,20,"0")

Define lvID = ListViewGadget(#liste,10,10,280,160)

Define i
For i = 0 To 100
  AddGadgetItem(#liste, i, "Ligne "+Str(i))
Next   

Define event, evType, gadget, item
     
Repeat
  event=WaitWindowEvent()

  Select event
    Case #PB_Event_CloseWindow : Break
    Case #PB_Event_Gadget
      gadget = EventGadget()
      Select gadget
        Case #cmd_OK
          i = Val(GetGadgetText(#input))
          SetGadgetState(#liste,i)
          SetGadgetText(#infos,Str(GetVisibleTopRow(#liste)))
        Case #liste
          SetGadgetText(#infos,Str(GetVisibleTopRow(#liste)))
      EndSelect
  EndSelect
ForEver
End
Update: As suggested by Blue (Thank you!), I have replaced

Code: Select all

        Case #liste
          SetGadgetText(#infos,Str(item))
with

Code: Select all

        Case #liste
          SetGadgetText(#infos,Str(GetVisibleTopRow(#liste)))
Last edited by Shardik on Mon Apr 06, 2015 8:48 am, edited 1 time in total.
User avatar
Blue
Addict
Addict
Posts: 964
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: determining item at top of Listview gadget

Post by Blue »

Thank you Shardisk.
I know very well your list of platform equivalent solutions, as i've spent quite a bit of interesting time jumping through the many links you provide. I hope you keep adding to that precious list. It's a major time-saver and headache reliever.

Plus, of course, you're right.
Absent a PB-only solution, nothing is lost. That's the beauty of PB.
If one knows the equivalent platform-specific system calls, CompilerIf..CompilerEndIf switches will do the job very nicely. And, come to think of it, the solutions you offer are, very much a PB-only solutions, but in a broader sense.

So this post is not to imply that there is no salvation if basic-language commands don't provide the answer. It's just that this is what i'm out fishing for. 8)

Many thanks for the code you provided.
Now all I've got to do to fully enjoy your contribution is to scrape together a few thousand dollars to buy myself the Mac of my dreams... :)
Next week maybe... :|

PS: an important element is missing from the last Case in the code you modified :

Code: Select all

SetGadgetText(#infos,Str(item))
; should be replaced by 
SetGadgetText(#infos,Str(GetVisibleTopRow(#liste)))
   
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: determining item at top of Listview gadget

Post by RASHAD »

Just select any item after scrolling
With keyboard it will be easier
Tested only with Windows
Still using Shardik code(API for Windows\Linux\Mac) is # 1

Code: Select all

Procedure GetIndex()
  Sel = GetGadgetState(0)
  For index = Sel To 0 Step -1
      SetGadgetState(0,index)
      StartDrawing(WindowOutput(0))
        Color = Point(GadgetX(0,#PB_Gadget_WindowCoordinate)+GadgetWidth(0)-20, GadgetY(0,#PB_Gadget_WindowCoordinate)+2)
      StopDrawing()
      If Color <> GetGadgetColor(0,#PB_Gadget_BackColor)
         Debug index
         Break
      EndIf
  Next
  SetGadgetState(0,Sel)
EndProcedure  
  
OpenWindow(0, 0, 0, 270, 140, "ListViewGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ListViewGadget(0, 10, 10, 250, 120)
  For a = 0 To 20
    AddGadgetItem (0, -1, "Item " + Str(a) + " of the Listview")
  Next
 SetGadgetState(0,0)
 SetGadgetColor(0,#PB_Gadget_BackColor,$FEFEFE)
 SetActiveGadget(0)
Repeat           
  Select WaitWindowEvent()      
      Case #PB_Event_CloseWindow
            Quit = 1
      
      Case #PB_Event_Gadget
          Select EventGadget()
           Case 0
                GetIndex()
            
          EndSelect
  EndSelect 
Until Quit = 1
End
Egypt my love
User avatar
Blue
Addict
Addict
Posts: 964
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: determining item at top of Listview gadget

Post by Blue »

Yesssssss !
That's what i'm talking about.

And quite the clever solution, RASHAD ! :shock:
Nice thinking outside the box.

I like your solution for its originality, its accuracy and its cleverness.
But does it ever flicker ! :(
Another limitation : it can't report the item on the top line if the selected item is not in the viewport of the Listview.

Stop the flicker, and i'll mail you the chocolate medal.
Hurry up before it's fully melted... :wink:
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: determining item at top of Listview gadget

Post by RASHAD »

For the first req.
You must initiate the gadget after selecting unseen item

Code: Select all

Procedure GetIndex()  
  Sel = GetGadgetState(0)  
  For index = Sel To 0 Step -1
      SetGadgetState(0,index)
      StartDrawing(WindowOutput(0))
        Color = Point(GadgetX(0,#PB_Gadget_WindowCoordinate)+GadgetWidth(0)-20, GadgetY(0,#PB_Gadget_WindowCoordinate)+2)
      StopDrawing()
      If Color <> GetGadgetColor(0,#PB_Gadget_BackColor)
         Debug index
         Break
      EndIf
  Next
  SetGadgetState(0,Sel)
EndProcedure  
  
OpenWindow(0, 0, 0, 270, 200, "ListViewGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ListViewGadget(0, 10, 10, 250, 120)
  ButtonGadget(1,10,170,60,20,"TEST")
  For a = 0 To 20
    AddGadgetItem (0, -1, "Item " + Str(a) + " of the Listview")
  Next
 SetGadgetState(0,0) 
 SetGadgetColor(0,#PB_Gadget_BackColor,$FEFEFE)
 SetActiveGadget(0)
Repeat           
  Select WaitWindowEvent()      
      Case #PB_Event_CloseWindow
            Quit = 1
      
      Case #PB_Event_Gadget
          Select EventGadget()
           Case 0
                GetIndex()
                
           Case 1
                 SetGadgetState(0,15)
                 GetIndex()
            
          EndSelect
  EndSelect 
Until Quit = 1
End
I will try today to fix the flicker(If I can :P )
Egypt my love
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: determining item at top of Listview gadget

Post by Dude »

Don't forget that SetGadgetColor(0,#PB_Gadget_BackColor,$FEFEFE) will clash with other color schemes that the user may have set for their computer.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: determining item at top of Listview gadget

Post by RASHAD »

Hi Dude
No you can select any color you like as long as it is not the default color(-1)
or the highlight selection color
Egypt my love
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: determining item at top of Listview gadget

Post by Danilo »

I can't see any difference of RASHAD's code to:

Code: Select all

Debug GetGadgetState(0)
That's all it does. It returns the selected item, not the top index.

EDIT:
When changing

Code: Select all

        Color = Point(GadgetX(0,#PB_Gadget_WindowCoordinate)+GadgetWidth(0)-20, GadgetY(0,#PB_Gadget_WindowCoordinate)+2)
to

Code: Select all

        Color = Point(GadgetX(0,#PB_Gadget_WindowCoordinate)+GadgetWidth(0)-30, GadgetY(0,#PB_Gadget_WindowCoordinate)+2)
it works on my Windows 8.1. ( GadgetWidth(0)-30 instead GadgetWidth(0)-20 )

Oh well... :lol: :lol:
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: determining item at top of Listview gadget

Post by Dude »

Danilo wrote:I can't see any difference of RASHAD's code to:

Code: Select all

Debug GetGadgetState(0)
That's all it does. It returns the selected item, not the top index.
That's what I first thought, too, but that's not the request. The OP wants to get the top visible item in the list, not the first item in the list. GetGadgetState(0) doesn't do that.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: determining item at top of Listview gadget

Post by Danilo »

Dude wrote:The OP wants to get the top visible item in the list, not the first item in the list. GetGadgetState(0) doesn't do that.
I know what the OP wants, and it was already answered by Shardik.

RASHAD's kludge did not work here (before my small correction), so I didn't see any difference to a simple GetGadgetState(0).
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: determining item at top of Listview gadget

Post by Dude »

Danilo wrote:I know what the OP wants
Sorry Danilo, I didn't realise that. I misunderstood what you were saying.
User avatar
Blue
Addict
Addict
Posts: 964
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Re: determining item at top of Listview gadget

Post by Blue »

Danilo wrote:I can't see any difference of RASHAD's code to:

Code: Select all

Debug GetGadgetState(0)
That's all it does. It returns the selected item, not the top index.

Oh well... :lol: :lol:

Hi Danilo
.
Rashad's solution (or kludge as you call it) works as long as there's a selected line in the viewport of the gadget. Run the code in the initial message of this post to better see what I'm looking for. That code does exactly what i want, but of course it uses an API call, which is cheating since the objective is to do it without any API call. That may not be totally possible (although Rashad may prove me wrong!) and you may wonder "but WHY bother?".
It's just curiosity : i spent a long time trying to get that done, and got frustrated that i couldn't. So i figured i'd ask others if they had better luck, or ideas, or skills.

So far, Rashad's code is the closest, as it solves part of the problem. And you've got to admit, kludge or not, his approach is clever...

Rashad's original code (with the (-20) displacement) worked on my 8.1 machine as is.
I'm just wondering why, on yours, the testing point had to be moved another 10 pixels back...
But that made me realize that his code may not provide the expected cross-platform result after all, since other systems surely don't draw Listview gadgets the same way Windows does.


   
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: determining item at top of Listview gadget

Post by Danilo »

Blue wrote:Rashad's original code (with the (-20) displacement) worked on my 8.1 machine as is.
I'm just wondering why, on yours, the testing point had to be moved another 10 pixels back...
But that made me realize that his code may not provide the expected cross-platform result after all, since other systems surely don't draw Listview gadgets the same way Windows does.
The vertical scrollbar width may be different between OS, and different between system DPI settings.

Code: Select all

Debug GetSystemMetrics_(#SM_CXVSCROLL)
Outputs 21 with my 125% DPI setting (2560x1440 @ 27"). When switching to 100% DPI, it returns 17 here.
Of course, that's completely different on other configurations and OS. '-24' works for me (21 + 2 px border + 1).

Just make sure the pixel you check is never part of a character, and always the gadget background vs. selection (depends on system, text length, (system) fonts, DPI settings, gadget borders, etc.)
In my personal opinion that way is not reliable, but of course you can use what you like...

Anyway... ;)
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: determining item at top of Listview gadget

Post by Shardik »

I have always been an admirer of RASHAD's creative solutions but unfortunately they are rarely usable for cross-platform solutions even if they should work when utilizing only native PB functions. RASHAD's current example simply can't work in MacOS because it seems not possible to use Point() in MacOS for reading the color of a pixel in a Gadget. Point() always returns 0 independant of its position in the ListViewGadget...
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: determining item at top of Listview gadget

Post by infratec »

Hi,

for performnace reasons:
Put the StartDrawing() and StopDrawing() outside of the for next loop.

Bernd
Post Reply