Confused with code behaviour

General help with the Ecere Cross Platform GUI toolkit: Window, common controls, events, etc.
Help with the 2D Graphics library: Surface, Display, Bitmap, Font and others.
Post Reply
shakeshuck
Posts: 31
Joined: Tue Dec 01, 2015 6:52 pm

Confused with code behaviour

Post by shakeshuck »

Sorry Jerome, I'm here again...

I know it's not your job to help me debug my programs, but I'm struggling to work out what's going on with some sample code I've created; I can only guess that it's my lack of knowledge about the underlying gui code that's the problem. Either that or I've made a simple and stupid error and I'm looking in the wrong place.

Here's my sample code:

Code: Select all

import "ecere"
 
class MainWindow : Window
{
   caption = $"Make Active Test";
   background = { r = 244, g = 248, b = 248 };
   borderStyle = fixed;
   hasMaximize = false;
   hasMinimize = true;
   hasClose = true;
   tabCycle = true;
   hasMenuBar = true;
   hasStatusBar = true;
   clientSize = { 407, 388 };
   position = { 112, 96 };
   dontAutoScrollArea = true;
   fullRender = true;
 
   SelectionWindow selectionWindow { this };
 
   MainWindow() {
 
//    Header Fields
      selectionWindow.selectionHeader.AddField( selectionWindow.selectionHeaderField );
      selectionWindow.selectionHeader.currentRow = selectionWindow.selectionHeader.AddRow();
      selectionWindow.selectionHeader.SetData( selectionWindow.selectionHeaderField, "Heading" );
 
//    Data Fields
      selectionWindow.selection1.AddField( selectionWindow.selection1Field );
      selectionWindow.selection1.currentRow = selectionWindow.selection1.AddRow();
 
      selectionWindow.selection2.AddField( selectionWindow.selection2Field );
      selectionWindow.selection2.currentRow = selectionWindow.selection2.AddRow();
 
      selectionWindow.selection3.AddField( selectionWindow.selection3Field );
      selectionWindow.selection3.currentRow = selectionWindow.selection3.AddRow();
 
      selectionWindow.selection4.AddField( selectionWindow.selection4Field );
      selectionWindow.selection4.currentRow = selectionWindow.selection4.AddRow();
   }
 
   void selectionMovement (int row, char upDown) {
 
      switch (upDown) {
         case 'd' : {
            switch (row) {
               case 1 : {
                  selectionWindow.selection2.MakeActive();
                  break;
               }
               case 2 : {
                  selectionWindow.selection3.MakeActive();
                  break;
               }
               case 3 : {
                  selectionWindow.selection4.MakeActive();
                  break;
               }
            }
         }
 
         case 'u' : {
            switch (row) {
               case 2 : {
                  selectionWindow.selection1.MakeActive();
                  break;
               }
               case 3 : {
                  selectionWindow.selection2.MakeActive();
                  break;
               }
               case 4 : {
                  selectionWindow.selection3.MakeActive();
                  break;
               }
            }
         }
      }
 
      return;
   }
}
 
class SelectionWindow : Window
{
   caption = $"Selection Window";
   background = { r = 235, g = 235, b = 235 };
   borderStyle = contour;
   tabCycle = true;
   size = { 387, 306 };
   position = { 10, 13 };
   fullRender = true;
 
   int vTop, vGap, vNext, vCheckTop;vTop = 14;vNext = vTop + 24;vGap = 22;vCheckTop = vTop + 24 + 4;
 
   ListBox selectionHeader { this, caption = $"SelectionHeader", inactive = true, size = { 180, 22 }, position = { 13, vTop }, hasVertScroll = false, snapVertScroll = false, rowHeight = 19 };
   DataField selectionHeaderField { editable = false, alignment = center, width = 180 };
//
   ListBox selection1
   {
      this, caption = $"Selection1", size = { 180, 22 }, hasVertScroll = false, snapVertScroll = false, rowHeight = 19, position = { 13, vNext };;;;;;;;
 
      bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
      {
         if(key == down) {
            form1.selectionMovement(1, 'd');
         }
         if(key == up) {
            form1.selectionMovement(1, 'u');
         }
 
         return true;
      }
   };
   DataField selection1Field { editable = true, alignment = left, width = 180 };
//
   ListBox selection2
   {
      this, caption = $"Selection2", size = { 180, 22 }, hasVertScroll = false, snapVertScroll = false, rowHeight = 19, position = { 13, ( vNext += vGap ) };;
 
      bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
      {
         if(key == down) {
            form1.selectionMovement(2, 'd');
         }
         if(key == up) {
            form1.selectionMovement(2, 'u');
         }
 
         return true;
      }
   };
   DataField selection2Field { editable = true, alignment = left, width = 180 };
//
   ListBox selection3
   {
      this, caption = $"Selection3", size = { 180, 22 }, hasVertScroll = false, snapVertScroll = false, rowHeight = 19, position = { 13, ( vNext += vGap ) };
 
      bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
      {
         if(key == down) {
            form1.selectionMovement(3, 'd');
         }
         if(key == up) {
            form1.selectionMovement(3, 'u');
         }
 
         return true;
      }
   };
   DataField selection3Field { editable = true, alignment = left, width = 180 };
//
   ListBox selection4 { this, caption = $"Selection4", size = { 180, 22 }, position = { 13, ( vNext += vGap ) }, hasVertScroll = false, snapVertScroll = false, rowHeight = 19 };
   DataField selection4Field { editable = true, alignment = left, width = 180 };
}
   MainWindow form1 {};
 
What is supposed to happen is that if you select (say) the second blank field, you should be able to use the up and down arrows to move focus (not surprisingly) up and down. Of course that is not what happens when it is tried.

Stepping through with the debugger appears to follow the correct logic, but what is then displayed doesn't follow what should be shown. Also, removing the break statements from the 'case' results in different behaviour, even though the debugger follows the same steps as before.

It's probably me, but I'd like to make sure... :?
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Confused with code behaviour

Post by jerome »

Hi shakeshuck,

I'm not exactly sure of the behavior you're looking for, but I imagine the biggest problem in the code you pasted is the fact that you're missing 'break' statements for the higher level case statements of the switch(upDown) in selectionMovement().

Code: Select all

   void selectionMovement(int row, char upDown)
   {
      switch(upDown)
      {
         case 'd':
            switch(row)
            {
               case 1: selectionWindow.selection2.MakeActive(); break;
               case 2: selectionWindow.selection3.MakeActive(); break;
               case 3: selectionWindow.selection4.MakeActive(); break;
            }
            break;
         case 'u' :
            switch (row)
            {
               case 2: selectionWindow.selection1.MakeActive(); break;
               case 3: selectionWindow.selection2.MakeActive(); break;
               case 4: selectionWindow.selection3.MakeActive(); break;
            }
            break;
      }
   }
You're also missing a NotifyKeyDown on the 'selection4' ListBox.
Note that you could use a common NotifyKeyDown method on all 4 list boxes if you set the 'id' property of each listBox to 1,2,3,4 respectively (and then pass that to selectionMovement())

Another thing that may also be useful is ensuring you return 'false' in NotifyKeyDown() if you do process the key, so that it does not end up doing something else as well in another input handler.

Cheers,

Jerome
shakeshuck
Posts: 31
Joined: Tue Dec 01, 2015 6:52 pm

Re: Confused with code behaviour

Post by shakeshuck »

jerome wrote:I'm not exactly sure of the behavior you're looking for, but I imagine the biggest problem in the code you pasted is the fact that you're missing 'break' statements for the higher level case statements of the switch(upDown) in selectionMovement().
This fixed it. Ah, the quirks of different languages... ;)
I also notice the lack of brackets in your example which makes it more readable.
You're also missing a NotifyKeyDown on the 'selection4' ListBox.
In this case it was a small section I'd taken from a larger program, I'll make sure it's all there in the full one!
Note that you could use a common NotifyKeyDown method on all 4 list boxes if you set the 'id' property of each listBox to 1,2,3,4 respectively (and then pass that to selectionMovement())
This is a good tip, thanks. I was wondering if there was a tidier way of doing it.
Another thing that may also be useful is ensuring you return 'false' in NotifyKeyDown() if you do process the key, so that it does not end up doing something else as well in another input handler.
This is the thing I was worried about the most; me not understanding the underlying processes and the consequences of changing stuff. I'll make a note to remember, thanks.

Cheers Jerome!
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Confused with code behaviour

Post by jerome »

Ah, the quirks of different languages...
Yes ;) You have to be careful to always have a 'break;' after each case in most C derived languages, unless you really intend for the code of the next case statement to be executed as well.
I was wondering if there was a tidier way of doing it.
Note that there is also the CycleChildren() method that you could invoke to do something similar (instead of calling MakeActive() on the controls)...
You could handle that e.g. in the OnKeyHit() of the parent form:

Code: Select all

bool CycleChildren(bool backward, bool clientOnly, bool tabCycleOnly, bool cycleParents)
shakeshuck
Posts: 31
Joined: Tue Dec 01, 2015 6:52 pm

Re: Confused with code behaviour

Post by shakeshuck »

Following on from my original code posting, I've found another 'funny'.

With this Button code:

Code: Select all

   Button biasBox1
   {
      this, background = white, isCheckbox = true, checked = true, position = { 351, vCheckTop };
 
      bool OnKeyDown(Key key, unichar ch)
      {
         if(key == down) {
            form1.biasMovement(1, 'd');
         }
         if(key == left) {
            form1.biasMovement(1, 'l');
         }
         if(key == right) {
            form1.biasMovement(1, 'r');
         }
         if(key == up) {
            form1.biasMovement(1, 'u');
         }
         if(key == tab) {
            form1.biasMovement(1, 'd');
         }
         if(key == space) {
            return Button::OnKeyDown(key, ch);
         }
 
         return false;
      }
   };
 
<Note I've not done the improvements mentioned yet>

Pressing the up key, for example, works as advertised as long as the key is not held down. Holding the key down seems to allow the behaviour to jump out of the requested flow. I am assuming it is some sort of timing issue (I guess the UI is threaded?). Is there someplace I need to add a 'pause' to stop this happening?
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Confused with code behaviour

Post by jerome »

Hi shakeshuck,

Holding the key down will generate additional OnKeyHit events.
The first key down generates both an OnKeyDown, and if the input is allowed to continue (return true), an OnKeyHit.

Try overriding OnKeyHit as well and returning false?
Buttons by default have up/down selecting the next/previous control.
(The ListBox or EditBox do not have that behavior because up/down is useful within those controls)
You probably want to pass down input to the base Button class most of the times though (I see you're doing it specifically here for 'space').

You should also not return 'false' by default, because that will block a lot of things. You should only return 'false' if you're doing something for this control as a result of the specific key message.

The keyboard input model is also so that the parent window has greater control over the child controls. So if a parent form processes a key and returns false (it will receive the message first), then the children controls will not receive the keys.

All events are processed in the main thread, the implementation of managing them could either use additional threads or not based on the UI driver implementation.

Another note, if you're processing many keys you may want to use a 'switch' statement rather than multiple ifs :)
shakeshuck
Posts: 31
Joined: Tue Dec 01, 2015 6:52 pm

Re: Confused with code behaviour

Post by shakeshuck »

jerome wrote:Hi shakeshuck,

Holding the key down will generate additional OnKeyHit events.
The first key down generates both an OnKeyDown, and if the input is allowed to continue (return true), an OnKeyHit.

Try overriding OnKeyHit as well and returning false?
Yup, that does it.
You probably want to pass down input to the base Button class most of the times though...
You should also not return 'false' by default, because that will block a lot of things. You should only return 'false' if you're doing something for this control as a result of the specific key message.
I get what you're saying. Changed.

Thanks. :)
Post Reply