Recent

Author Topic: [SOLVED] TCheckbox bug  (Read 854 times)

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
[SOLVED] TCheckbox bug
« on: January 14, 2020, 12:22:55 am »
The TCheckbox.OnChange event is incorrectly called if TCheckBox.Checked is True in IDE.

« Last Edit: January 15, 2020, 10:08:07 pm by VTwin »
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2347
    • havefunsoft.com
Re: TCheckbox bug
« Reply #1 on: January 14, 2020, 02:32:32 am »
please try r62545
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: TCheckbox bug
« Reply #2 on: January 14, 2020, 04:11:37 pm »
Excellent, it is working in trunk. Thanks!!
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: [SOLVED] TCheckbox bug
« Reply #3 on: January 14, 2020, 05:21:26 pm »
Fixed in fixes too!  :)
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: TCheckbox bug - not quite solved
« Reply #4 on: January 14, 2020, 05:49:59 pm »
Unfortunately now it is not possible to change checkbox.checked in the IDE. Checked ;) both in trunk and fixes.

 :(
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2347
    • havefunsoft.com
Re: TCheckbox bug - not quite solved
« Reply #5 on: January 15, 2020, 04:45:39 am »
svn up
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: TCheckbox bug - not quite solved
« Reply #6 on: January 15, 2020, 06:15:29 pm »
Thanks, I can now change checked true/false in the IDE. :)

However, if it is unchecked (false) in the IDE, and I have:

Code: Pascal  [Select]
  1. procedure TForm1.FormShow(Sender: TObject);
  2. begin
  3.   CheckBox1.Checked := true;
  4. end;


The Change event is triggered. :(
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

lucamar

  • Hero Member
  • *****
  • Posts: 2264
Re: TCheckbox bug - not quite solved
« Reply #7 on: January 15, 2020, 07:35:17 pm »
[..] if it is unchecked (false) in the IDE, and I have:
Code: Pascal  [Select]
  1. procedure TForm1.FormShow(Sender: TObject);
  2. begin
  3.   CheckBox1.Checked := true;
  4. end;
The Change event is triggered.

I'm curious, why do you think it shouldn't? After all it's supposed to be triggered on any property change.

Or is it because you change it in code vs. user action?
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.4/2.0.6  - FPC 3.0.4 on:
(K|L)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: TCheckbox bug - not quite solved
« Reply #8 on: January 15, 2020, 07:52:00 pm »
I'm curious, why do you think it shouldn't? After all it's supposed to be triggered on any property change.

Or is it because you change it in code vs. user action?

It is my understanding, and observation, that a control should respond to a user action, and not trigger a change event if assigned a value in code. For, example, if you set a ComboBox item index in code, that should not (and does not) trigger a change event.

I have many dialog boxes with controls that are set from stored preferences. In Carbon there was a persistent bug where some controls would trigger a change event when set in code. My code was littered with code like:

Code: Pascal  [Select]
  1. procedure TForm1.ComboBox1Change(Sender: TObject);
  2. begin
  3.   {$IFDEF DARWIN}
  4.   if fSettingControls then
  5.     exit; // because the control is being set in code to a saved preference value
  6.   {$ENDIF}
  7.   // now do something that should be done when the user changes the control
  8.   ...
  9. end;

This was never a problem in Linux or Windows. I have recently been happily deleting all of these defines as the Cocoa widgetset has now surpassed Carbon.
« Last Edit: January 15, 2020, 08:01:47 pm by VTwin »
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2347
    • havefunsoft.com
Re: TCheckbox bug - not quite solved
« Reply #9 on: January 15, 2020, 08:22:28 pm »
However, if it is unchecked (false) in the IDE, and I have:

The Change event is triggered. :(
That's expected actually.

It is my understanding, and observation, that a control should respond to a user action, and not trigger a change event if assigned a value in code. For, example, if you set a ComboBox item index in code, that should not (and does not) trigger a change event.
The answer is "inconsistency".

For example in Web-browser (JS) world "onchange" is triggered due to a user action. Changing the value via the software will not trigger the event.

In Cocoa API, "onchange" is triggered due to a user action. Changing the value via the software will not trigger the event.

In WinAPI world (inherited by Delphi, inherited by LCL), changing the value triggers the same "OnChange" event as user action does. BUT in WinAPI, there's typically a way to suppress the notification.
Specifically the checkbox:
Code: Pascal  [Select]
  1. class procedure TWin32WSCustomCheckBox.SetState(const ACustomCheckBox: TCustomCheckBox; const NewState: TCheckBoxState);
  2. var
  3.   Flags: WPARAM;
  4. begin
  5.   case NewState of
  6.     cbChecked: Flags := Windows.WParam(BST_CHECKED);
  7.     cbUnchecked: Flags := Windows.WParam(BST_UNCHECKED);
  8.   else
  9.     Flags := Windows.WParam(BST_INDETERMINATE);
  10.   end;
  11.   //Pass SKIP_LMCHANGE through lParam to avoid the OnChange event be fired
  12.   Windows.SendMessage(ACustomCheckBox.Handle, BM_SETCHECK, Flags, SKIP_LMCHANGE);
  13. end;
  14.  
Yet LCL itself would send out the notification.

Code: Pascal  [Select]
  1. procedure TCustomCheckBox.SetChecked(Value : Boolean);
  2. var
  3.   OldState: TCheckBoxState;
  4. begin
  5.   OldState := FState;
  6.   if Value then
  7.     FState := cbChecked
  8.   else
  9.     FState := cbUnChecked;
  10.   //debugln('TCustomCheckBox.SetChecked ',dbgsname(Self),' ',dbgs(ord(FState)));
  11.   if FState <> OldState then
  12.   begin
  13.     if Action is TCustomAction then
  14.       TCustomAction(Action).Checked := FState = cbChecked;
  15.     ApplyChanges;
  16.     //some widgetsets (gtk*) does not allow to uncheck a radio button
  17.     //only call OnChange if effectivelly changed
  18.     FState := RetrieveState;
  19.     if FState <> OldState then
  20.       DoClickOnChange;
  21.   end;
  22. end;
  23.  

Most of LCL Controls would trigger the notification, either way. And the documentation should be referred to.

In case of the check box, you can avoid the notification by using
Code: Pascal  [Select]
  1.   checkbox1.State:=csChecked;
  2.  
instead of using checked property
« Last Edit: January 15, 2020, 08:30:21 pm by skalogryz »
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: TCheckbox bug - not quite solved
« Reply #10 on: January 15, 2020, 09:46:11 pm »
Thanks for the illuminating answer skalogryz. It has left me puzzled though.

The answer is "inconsistency".

I figured the controls should behave the same on each platform. :(

For example in Web-browser (JS) world "onchange" is triggered due to a user action. Changing the value via the software will not trigger the event.

In Cocoa API, "onchange" is triggered due to a user action. Changing the value via the software will not trigger the event.

In WinAPI world (inherited by Delphi, inherited by LCL), changing the value triggers the same "OnChange" event as user action does.

So, Lazarus is emulating the Windows/Delphi model? From my experiences (but I should test), Linux and Windows seem not to  send an OnChange when set in code, while some of Carbon's did. I figured it was a Carbon bug.  Maybe it is a Windows/Linux bug instead? %)

BUT in WinAPI, there's typically a way to suppress the notification.

Yet LCL itself would send out the notification.

Most of LCL Controls would trigger the notification, either way. And the documentation should be referred to.

So LCL does (or is supposed to) send an OnChange when a control state is set in code?

In case of the check box, you can avoid the notification by using
Code: Pascal  [Select]
  1.   checkbox1.State:=csChecked;
  2.  
instead of using checked property

That does indeed work, and is good to know.

So, I guess I should assume that setting a control state in code does (or is supposed to) send an OnChange  event and work around that, or be careful to check the docs and always use a method such as "State := csChecked".

The JS/Cocoa model makes more sense to me, but as long as I know what is supposed to happen I can deal with it.

Thanks again for your explanations and quick fixes, they are much appreciated.
« Last Edit: January 15, 2020, 09:54:39 pm by VTwin »
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: TCheckbox bug - not quite solved
« Reply #11 on: January 15, 2020, 10:05:16 pm »
I just remembered that I use a helper class for all these dialog boxes, so changing:

Code: Pascal  [Select]
  1. procedure TVCustomCheckBoxHelper.SetBool(const b: boolean);
  2. begin
  3.   Checked := b;
  4. end;
  5.  

to:

Code: Pascal  [Select]
  1. procedure TVCustomCheckBoxHelper.SetBool(const b: boolean);
  2. begin
  3.   if b then
  4.    State := csChecked
  5.   else
  6.    State := csUnchecked
  7. end;

is a quick fix that should take care of it.  :)
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2347
    • havefunsoft.com
Re: TCheckbox bug - not quite solved
« Reply #12 on: January 15, 2020, 10:16:54 pm »
So, Lazarus is emulating the Windows/Delphi model? From my experiences (but I should test), Linux and Windows seem not to  send an OnChange when set in code, while some of Carbon's did. I figured it was a Carbon bug.  Maybe it is a Windows/Linux bug instead? %)
I would encourage you to play around and see how (and if) things are different.

But the common rule is "follow Windows widgetset (as it is most likely does what Delphi does)", no matter if it's poor design or not.
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

VTwin

  • Hero Member
  • *****
  • Posts: 841
  • Former Turbo Pascal 3 user
Re: TCheckbox bug - not quite solved
« Reply #13 on: January 15, 2020, 10:20:12 pm »
But the common rule is "follow Windows widgetset (as it is most likely does what Delphi does)", no matter if it's poor design or not.

Will do!


I found this bug report:

https://bugs.freepascal.org/view.php?id=33076

that suggests changing TCheckBox.State should trigger an OnChange event. :o

That appears to contradict. Is the bug report incorrect? 
“Talk is cheap. Show me the code.” -Linus Torvalds

macOS 10.13.6: Lazarus 2.0.7 fixes svn r62562 (64 bit Cocoa)
Ubuntu 18.04.3: Lazarus 2.0.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.0.6 (64 bit on VBox)
fpc 3.0.4

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2347
    • havefunsoft.com
Re: [SOLVED] TCheckbox bug
« Reply #14 on: January 15, 2020, 11:26:49 pm »
ahaha... I've just tried Delphi (10.3) and VCL TCheckBox doesn't have OnChange event at all!

now you see why it would ok for a LCL developer to suffer from a substance abuse :)


But technically the report is incorrect.

If we're seeking Delphi compatibility, THEN:
changing State via code should trigger "OnClick" event.
changing Checked via code should trigger "OnClick" event
(you can check that, and if it's not this way, you can surely file a bug report with the reference to Delphi compatibility)

Triggering event of OnChange is so solely in LCL responsibility. So, it should be strictly documented (in LCL documentation) and/or Requested For Comments (with the future documentation).

If it's documented, we can accepted the current behavior as expected and desired, and the bug report was created without any base. (solely on the developer's expectations). And should be closed as "no changes required".

P.S. The most sad thing for me, personally, is that LCL is out there for about 20 years now. But we're still have to handle such basic stuff as checkbox notifications.
« Last Edit: January 15, 2020, 11:30:39 pm by skalogryz »
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz