determining item at top of Listview gadget

Just starting out? Need help? Post your questions and find answers here.
User avatar
Blue
Addict
Addict
Posts: 964
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

determining item at top of Listview gadget

Post by Blue »

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.

I've tried many tricks (i call them algorithms, to generate more respect and admiration), but none were satisfactory.

Following is a simple ListView program showing what i want.
It's Windows ONLY, since it makes Windows API calls, the very thing i wish to replace :

Code: Select all

EnableExplicit
;-
;- ********************************
;- gadgets ids
Enumeration 
  #cmd_SCROLL
  #input
  #liste
  #top_LABEL
  #top
EndEnumeration

; ********************************
Procedure CheckTopItem()
  Static iPrec
  Define iTop
  iTop = SendMessage_(GadgetID(#liste),#LB_GETTOPINDEX,0,0)    ;- .... read top item
  If iTop <> iPrec
    SetGadgetText(#top,Str(iTop))
    iPrec = iTop
  EndIf
EndProcedure

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

;- creating gadgets
Define gX = 10
Define gW = WindowWidth(0) - gX - gX
ListViewGadget(#liste,10,10,280,160) 

Define gY = GadgetY(#liste) + GadgetHeight(#liste) + 6
gW = 56
TextGadget(#top_LABEL,gX,gY+2,gW,20,"Top item = ", #PB_Text_Right)

gX + gW + 4
gW = 24
TextGadget(#top,gX,gY+2,gW,20,"0")
SetGadgetColor(#top, #PB_Gadget_FrontColor, #Red)

gW = 30
gX = GadgetX(#liste) + GadgetWidth(#liste) - gW
StringGadget(#input ,gX,gY,gW,20,"22", #PB_String_Numeric)

gW = 75
gX - gW - 4
ButtonGadget(#cmd_SCROLL ,gX,gY,gW,20,"Scroll to >>>")
; ********************************

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

;-
;- ********************************
;- Windows loop
Define event, evType, gadget, iTop, iPrec
Repeat
  event=WaitWindowEvent(222)

  CheckTopItem()

  Select event
    Case #PB_Event_CloseWindow : Break
    Case #PB_Event_Gadget
      gadget = EventGadget()
      Select gadget
        Case #liste
          SetGadgetText(#input,Str(GetGadgetState(#liste)))

        Case #cmd_SCROLL
          i = Val(GetGadgetText(#input))
          SendMessage_(GadgetID(#liste), #LB_SETTOPINDEX, i, 0)

        Case #input
          i = Val(GetGadgetText(#input))
          If i >= CountGadgetItems(#liste)
            SetGadgetState(#liste,CountGadgetItems(#liste)-1)
            SetGadgetText(#input,Str(GetGadgetState(#liste)))
          EndIf
      EndSelect
  EndSelect
ForEver

End 
 
Last edited by Blue on Tue Apr 07, 2015 4:00 am, edited 5 times in total.
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: determining item at top of Listview gadget

Post by Dude »

Blue wrote:Does anyone know how to figure out, using only PB code, which item is currently at the top of a Listview gadget ?

Code: Select all

item$=GetGadgetItemText(#gad,0) ; 0 = First item in list.
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 »

That's not what i mean, Dude.

Suppose a Listview with 200 entries, displaying 10 items at a time in its viewport.
The gadget gets scrolled, so that items #78 to #87 are now visible in the viewport.

I want to be able to determine that the item visible on the first line is #78.

I have an API function that allows me to scroll the gadget until #78 is the first visible item.
But i want to avoid using it.
...
Knowing the number of lines displayable makes it possible (i think...) to scroll until a specific item is at the top.
What i can't figure out is how to know which item is on that first top line if i didn't scroll it there myself.
I keep thinking it's just a matter of finding a clever algorithm, but it escapes my feeble mind so far !
...
An idea just popped into my tiny head all of a sudden...
I'm off to try it.
"I'll be back" like the big Austrian used to say.
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: determining item at top of Listview gadget

Post by Dude »

I see what you mean, now. But you never said "first item visible" in your original post, though. :mrgreen:
User avatar
mrv2k
User
User
Posts: 53
Joined: Fri Jan 20, 2012 3:40 pm
Location: SE England
Contact:

Re: determining item at top of Listview gadget

Post by mrv2k »

Very cludgy but...

Code: Select all

If OpenWindow(0,0,0,640,480,"Test",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ListIconGadget(0,5,5,600,400,"Demo",300,#PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
  AddGadgetColumn(0,1,"Bar",200)
  HideGadget(0,1)
  For i = 0 To 1000
    AddGadgetItem(0,-1,"Foo "+Str(i))
  Next
  HideGadget(0,0)
EndIf

Repeat
  
  event.l=WaitWindowEvent()
  type.l=EventType()
  gadget.l=EventGadget()
  
  Debug GetScrollPos_(GadgetID(0), #SB_VERT)

Until event=#PB_Event_CloseWindow
Most important line...

Code: Select all

GetScrollPos_(GadgetID(0), #SB_VERT)
Regards

Paul
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 »

mrv2k wrote:Very cludgy but...
[...]
Most important line...

Code: Select all

GetScrollPos_(GadgetID(0), #SB_VERT)
Thanks for the effort, Paul, but that's not it. :(
The cludgyness is not a problem. Cludgy is totally fine since it can always be de-cludgified (Yes, that is a word in the Blue Dictionary of the Modern English Language) eventually.
The problem is with that most important line to which you point my attention : it's an API (windows only) call, and that's what I want to avoid.
If you try the code in the first message, you'll see that it works perfectly with the most exact and appropriate API call, so I already have such a solution that works perfectly.

But i'm looking for an all platforms solution, written with PB only code. :idea:
It's my Holy Grail of the month. :D
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
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.
Post Reply