458 views
in GUI Development by
Hi,

I've got a VerticalList that shows a 'SelectedItem' in a different colour. The SelectedItem is determined using 'GetItemAtPosition' (i.e. item at the centre of the control is the one we want to show 'selected') and UpdateViewState on the List Item component is used to change the View by checking if it is 'Selected'.

When the List Scrolls, the SelectedItem is set to '-1' and the UpdateViewState is used to redraw it as 'unselected.' (This is done in the 'Slide' slot).

This works perfectly when Endless is 'false'.

However, when Endless is 'true', 'UpdateViewState' is not *always* called when SelectedItem is set to -1,

It seems that when VerticalList.UpdateViewState is called after SelectedItem is changed, the following line evaluates to false:

if (( pure SelectedItem < reuseHead ) || ( pure SelectedItem > reuseTail ))

        Focus = null;

[Example, I've seen SelectedItem = -1, reuseHead = -1 and reuseTail=5]

As a consequence, Focus = null is not called - this means that the current item does not have 'UpdateViewState' called on it and it remains 'Selected' and 'Focused'. (Changing focus to a new value changes the state of the current component that has focus.)

Is this correct behaviour? I do *not* see this problem when Endless=false.

[As a workaround, I call Focus=null myself before SelectedItem = -1]

1 Answer

0 votes
by
 
Best answer

Hello,

thank you very much for this report. You have encountered an error. In particular cases the selection worked unreliably if the list was configured as endless. I have redesigned the related code. Since we are actually in the preparation phase of the next release 9.0 it would be very helpful for us if you could try this modification. The modification affects the UpdateViewState() method in the Mosaic class Core::VerticalList. To test it, you would need to replace the original code of this method by following code:

// Avoid Chora warnings
aState;

// No class to create items
if ( pure ItemClass == null )
{
  // Inform the owner of the list, that it has been changed
  postsignal OnUpdate;
  return;
}

// From now the list is performing the update of its items.
loading = true;

var int32 paddingTop = 0;

if ( !pure Endless )
  paddingTop = pure PaddingTop;

// Calculate the index of the first and the last visible item. How many items 
// can be shown?
var int32 head  = ( -paddingTop    - pure ScrollOffset     ) / pure ItemHeight;
var int32 tail  = (  pure Bounds.h - paddingTop - pure ScrollOffset - 1 ) / pure ItemHeight;
var int32 count = (  pure Bounds.h + pure ItemHeight   - 1 ) / pure ItemHeight;

// Try to maintain at least 1 item in the cache - even if the list boundary
// area has reached the height <= 0 pixel.
if ( count < 1 )
  count = 1;

// How many invisible items should additionally be cached in the list to optimize
// the scrolling?
var int32 cache2 = count / 2;
var int32 cache3 = count / 3;

// ... at least one item!
if ( cache2 == 0 ) cache2 = 1;
if ( cache3 == 0 ) cache3 = 1;

// Take in account additional items above the visible area
if ( head < validHead )
{
  head = head - cache2;
  tail = tail + cache3;
}

// ... or below the area
else if ( tail > validTail )
{
  head = head - cache3;
  tail = tail + cache2;
}

// Otherwise all visible items lie still within the valid area
else
{
  head = validHead;
  tail = validTail;
}

// Respect the lower/upper boundary
if ( !pure Endless )
{
  if      ( head >= pure NoOfItems ) { head = 0; tail = -1; }
  else if ( tail <  0              ) { head = 0; tail = -1; }
  if ( tail >= pure NoOfItems ) tail = pure NoOfItems - 1;
  if ( head < 0 )               head = 0;
}

// Endless list without any content?
else if ( pure NoOfItems <= 0 )
{
  head = 0;
  tail = -1;
}

var int32 reuseHead = validHead;
var int32 reuseTail = validTail;
var int32 loadHead  = 0;
var int32 loadTail  = -1;

// Does the new and the old areas intersect? If true reuse the affected items
if ( head > reuseHead ) reuseHead = head;
if ( tail < reuseTail ) reuseTail = tail;

// Can we reuse already confirmed items?
if ( reuseHead <= reuseTail )
{
  // Restack leading items to the tail
  while (( validTail < tail ) && ( validHead < reuseHead ))
  {
    releaseHeadItem();
    confirmTailItem();
  }

  // Restack items to the head
  while (( validHead > head ) && ( validTail > reuseTail ))
  {
    releaseTailItem();
    confirmHeadItem();
  }
}

// None of the currently confirmed items can be reused. The confirmed
// area should be reloaded completely
else
{
  validTail = validTail - validHead + head;
  validHead = head;
  loadHead  = validHead;
  loadTail  = validTail;
}

// Remove superfluous items or append and load items if necessary
while ( validHead < head ) releaseHeadItem();
while ( validTail > tail ) releaseTailItem();
while ( validHead > head ) confirmHeadItem();
while ( validTail < tail ) confirmTailItem();

var Core::View item = first;
var int32      inx  = validHead;
var point      pos  = point( 0, paddingTop + pure ScrollOffset + ( inx * pure ItemHeight ));
var int32      h    = Bounds.h;
var int32      ih   = ItemHeight;
var Core::View fi   = null;

// Initialize and arrange items
while ( item != null )
{
  var point ofs  = pos - item.GetExtent().origin;
  var int32 iy1  = pos.y;
  var int32 iy2  = pos.y + ih;
  var int32 inxN = inx;

  // In case of endless lists calculate the index of the real item
  if ( pure Endless )
  {
    if ( inxN < 0 ) inxN = pure NoOfItems - ( -inxN % pure NoOfItems );
    if ( inxN > 0 ) inxN = inxN % pure NoOfItems;
  }

  // Arrange the item at its position
  if ( ofs != <0,0>)
    item.MoveView( ofs, true );

  // Should the items content be reloaded? In the first pass reload the visible
  // items only
  if (((( inxN >= invalidHead ) && ( inxN <= invalidTail )) ||
       (( inx  >= loadHead    ) && ( inx  <= loadTail    ))) &&
       ( iy1 < h ) && ( iy2 > 0 ))
  {
    View = item;
    Item = inxN;
    signal OnLoadItem;
  }

  // Continue with the next following item
  item    = item.next;
  inx     = inx + 1;
  pos.y   = pos.y + pure ItemHeight;
}

inx  = validHead;
item = first;
pos  = point( 0, paddingTop + pure ScrollOffset + ( inx * pure ItemHeight ));

// Second pass -> reload invisible cached items
while ( item != null )
{
  var int32 iy1  = pos.y;
  var int32 iy2  = pos.y + ih;
  var int32 inxN = inx;

  // In case of endless lists calculate the index of the real item
  if ( pure Endless )
  {
    if ( inxN < 0 ) inxN = pure NoOfItems - ( -inxN % pure NoOfItems );
    if ( inxN > 0 ) inxN = inxN % pure NoOfItems;
  }

  // Should the items content be reloaded? In the second pass reload the invisible
  // items only 
  if (((( inxN >= invalidHead ) && ( inxN <= invalidTail )) ||
       (( inx  >= loadHead    ) && ( inx  <= loadTail    ))) &&
       !(( iy1 < h ) && ( iy2 > 0 )))
  {
    View = item;
    Item = inxN;
    signal OnLoadItem;
  }

  // Focus the item? Special case of an endless list. Always try to select the
  // first visible item if there are several copies of the same item.
  if (( inxN == pure SelectedItem ) && pure Endless && ( fi != null ))
  {
    var rect b = pure Bounds.orect;

    if (( item.GetExtent() & b ).area > ( fi.GetExtent() & b ).area )
      fi = item;
  }

  // Otherwise just select this item
  else if ( inxN == pure SelectedItem )
    fi = item;

  // Continue with the next following item
  item    = item.next;
  inx     = inx + 1;
  pos.y   = pos.y + pure ItemHeight;
}

// Finished
invalidHead = 0;
invalidTail = -1;
View        = null;
Item        = -1;

// Assign the focus to the selected item. If the list is endless and there are
// several items with the same selected index visible, the first of the items
// which is most visible will become focused.
Focus = fi;

// Update is finished
loading = false;

// Inform the owner of the list, that it has been changed
postsignal OnUpdate;

 Does it work now as expected?

If yes, you can retain this modification in your Mosaic. The unique difficulty here, the entire Embedded Wizard installation including the Mosaic is write-protected. You will need to use an external text editor and edit the file Core.ewu found in the directory Mosaic just below the directory where your Embedded Wizard Studio is installed. Depending on your text editor, Windows will ask you to override the protected file.

Hope it solves the problem.

Best regards

Paul Banach

by
Hi Paul

Thanks for your reply, I've just tried this fix and it works as expected now.

Thanks!

Embedded Wizard Website | Privacy Policy | Imprint

...