Recent

Author Topic: Setting TPairSplitter position at runtime  (Read 1140 times)

wp

  • Hero Member
  • *****
  • Posts: 6224
Re: Setting TPairSplitter position at runtime
« Reply #15 on: September 14, 2019, 12:51:40 am »
Landslyde, this was not my question. It was about getting the same effect using the AnchorEditor (which offers more flexibility than using the Align property).
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

jamie

  • Hero Member
  • *****
  • Posts: 1980
Re: Setting TPairSplitter position at runtime
« Reply #16 on: September 14, 2019, 01:34:02 am »
That makes no sense really, did you call the UPDATE or repaint after setting from INiFile?

What I said: UpdatePosition. The FPosition field (hence Position property) appears to not track manual changes, at least on gtk2.

MarkMLl

 When I said UPDATE or REPAINT, I wasn't talking about the POSTION property, I was referring to the UPDATE/REPAINT of the control
 MySplitter.Update or Repaint;

 This is after you set your new position

MarkMLl

  • Full Member
  • ***
  • Posts: 130
Re: Setting TPairSplitter position at runtime
« Reply #17 on: September 14, 2019, 09:28:58 pm »
Well, all I can say is that it's what fixed it for me but it could be specific to Debian "Buster" (i.e. current stable version).

Are you seeing the Position property track manual movement of the splitter?

MarkMLl

Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

wp

  • Hero Member
  • *****
  • Posts: 6224
Re: Setting TPairSplitter position at runtime
« Reply #18 on: September 15, 2019, 01:25:11 am »
Are you seeing the Position property track manual movement of the splitter?
After calling a "Label1.Caption := IntToStr(PairSplitter1.Position)" in the OnResize event of a TPairSplitterSize I see the label text varying according to the splitter position during dragging. Moreover, setting "PairSplitter1.Position := 100" in the OnClick event of a button is executed immediately, and the new position value is displayed in the label.

So, the answer is: Yes.

This is on Lubuntu 18.10 / gtk2.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

MarkMLl

  • Full Member
  • ***
  • Posts: 130
Re: Setting TPairSplitter position at runtime
« Reply #19 on: September 15, 2019, 09:07:26 am »
Are you seeing the Position property track manual movement of the splitter?
After calling a "Label1.Caption := IntToStr(PairSplitter1.Position)" in the OnResize event of a TPairSplitterSize I see the label text varying according to the splitter position during dragging. Moreover, setting "PairSplitter1.Position := 100" in the OnClick event of a button is executed immediately, and the new position value is displayed in the label.

So, the answer is: Yes.

This is on Lubuntu 18.10 / gtk2.

I phrased that badly. If you single-step into this code

Code: [Select]
function TCustomPairSplitter.GetPosition: integer;
begin
  if HandleAllocated and (not (csLoading in ComponentState)) then
    UpdatePosition;
  Result := FPosition;
end;

(which is what you called when you read the position) you might see that FPosition is not updated until after UpdatePosition is called.

If you look at the setter you will see that nothing happens if the parameter is the same as the current value of FPosition:

Code: [Select]
procedure TCustomPairSplitter.SetPosition(const AValue: integer);
begin
  if FPosition = AValue then
    Exit;
...

So a manual adjustment of position followed by a change of position under program control will do noting if the manual change didn't update FPosition- which I think is what caused my original question. The fix is either to call UpdatePosition, or to read the property.

MarkMLl




Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

wp

  • Hero Member
  • *****
  • Posts: 6224
Re: Setting TPairSplitter position at runtime
« Reply #20 on: September 15, 2019, 10:28:46 am »
I probably don't understand the issue since the PairSplitter is working correctly for me...

My demo reads the splitter position from the ini file and sets it accordingly. This happens in the OnCreate event of the form where the splitter does not have a handle yet. Therefore the setter writes the ini value to FPosition only but cannot apply it to the widths of the sides, yet. Later, when handles are created, the splitter's CreateWnd is called which finally sets the widths of the sides.

Instead of using the Position property you can also write directly to the Sides[0].Width property of the TPairSplitterSide. This way the FPosition property is bypassed, and there is a discrepancy between the real width of the left splitter and the FPosition value internally. But this is irrelevant because you can detect this only by reading the Position property which calls UpdatePosition and brings them back into agreement.

Note that setting Sides[1].Width does not call UpdatePosition which might be considered a bug. At least the user should know...

If you look at the setter you will see that nothing happens if the parameter is the same as the current value of FPosition:
Code: [Select]
procedure TCustomPairSplitter.SetPosition(const AValue: integer);
begin
  if FPosition = AValue then
    Exit;
...
So a manual adjustment of position followed by a change of position under program control will do noting if the manual change didn't update FPosition
This is standard behaviour of most property setters: when the new property value already is identical to the old value there's nothing else to be done.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

MarkMLl

  • Full Member
  • ***
  • Posts: 130
Re: Setting TPairSplitter position at runtime
« Reply #21 on: September 15, 2019, 10:36:51 am »
Note that setting Sides[1].Width does not call UpdatePosition which might be considered a bug. At least the user should know...

Neither, it appears, does moving the splitter using the mouse. FPosition is not updated until the property is read or UpdatePosition is called, so writing to the property before this is done will not work as expected.
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

wp

  • Hero Member
  • *****
  • Posts: 6224
Re: Setting TPairSplitter position at runtime
« Reply #22 on: September 15, 2019, 11:06:44 am »
So, the problem is that you cannot set the splitter position by writing to Sides[1].Width? I said that this is possibly a bug - write a bug report to get it fixed (or better: fix it yourself and submit a patch). But there are two ways to work around this issue: set Sides[0].Width or Position to the complementary value.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

jamie

  • Hero Member
  • *****
  • Posts: 1980
Re: Setting TPairSplitter position at runtime
« Reply #23 on: September 15, 2019, 03:30:24 pm »
@MarkMLI
 
  Just a side note about updating properties.

 If you use a "WITH" statement on a structural property, like a record or something that contains sub members and you alter the values of these members within the WITH block, the Setter of the property will not get called.

 There are many cases where a property can return a class or record for example of some conditions taking place and using this approach does not work via the "WITH" block, which I love using because it makes code for me compact looking.

 I did suggest a fix for this in another thread but I am sure it will get shot down as it has already received a single negative comment, only one that is. To me I think it's a great expansion for the compiler..
 
 SomePropery := With AProperty do Begin ….. End;

 With that, the finalizing "END" of the block will perform a Setter call to the property using the background, otherwise wasted entity to call the Setter.

 Just bare in mind that using a WITH on a complex property and expecting the change of a member to take place isn't going to happen.

wp

  • Hero Member
  • *****
  • Posts: 6224
Re: Setting TPairSplitter position at runtime
« Reply #24 on: September 15, 2019, 03:59:22 pm »
OK, I found a situation now which is similar to what you, MarkMLI, describe - see attached demo.
  • Compile and run the demo
  • Using the mouse drag the splitter to some other location
  • Click on button "Set Position" --> the splitter moves to Position = 100, as coded --> ok
  • Again, drag the splitter to some other location
  • Click on button "Set Position" again --> the splitter does not change its position!
  • Restart the program and repeat the same operations, but now click on "Set Sides[0].Width" --> the splitter always changes its position.
MarkMLI, is this what you mean?

A possible solution would be to add a Resize method to TPairSplitterSide which calls UpdatePosition:
Code: Pascal  [Select]
  1. procedure TPairSplitterSide.Resize;
  2. begin
  3.   inherited;
  4.   if Self = Splitter.Sides[0] then Splitter.UpdatePosition;
  5. end;
However, explicitly using Splitter.Sides[0] here makes me doubt that this is correct because the widgetset implementation is very general (see comment in TWSCustomPairSplitter.AddSide (in lcl/widgetset/wspairsplitter.pp).
« Last Edit: September 15, 2019, 04:35:22 pm by wp »
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

MarkMLl

  • Full Member
  • ***
  • Posts: 130
Re: Setting TPairSplitter position at runtime
« Reply #25 on: September 15, 2019, 08:31:56 pm »
Yes, except what I was doing was saving (form size and) splitter positions immediately after form creation, and then later on- possibly after the splitter(s) had beed dragged manually- restoring them. I think the way it interacted with the properties was the same as you've got though.
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

MarkMLl

  • Full Member
  • ***
  • Posts: 130
Re: Setting TPairSplitter position at runtime
« Reply #26 on: September 15, 2019, 08:43:31 pm »
If you use a "WITH" statement on a structural property, like a record or something that contains sub members and you alter the values of these members within the WITH block, the Setter of the property will not get called.

Thanks for that, extremely interesting and yet another reason why  with  should be approached with caution- particularly for code which isn't ones own. However I'm confident that it's not relevant in this case, since the only places I'm referencing the property are in the save and restore code and I've single-stepped in to see what's happening.

I've suggested in the mailing lists in the past that  with  define a temporary alias i.e. something like

with const foo = bar^ do begin
  foo.bletch :=...

but needless to say the idea didn't get anywhere: I think the whole idea of in-block declarations is too much like C (and ALGOL-60) for people to stomach :-)

MarkMLl

Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

wp

  • Hero Member
  • *****
  • Posts: 6224
Re: Setting TPairSplitter position at runtime
« Reply #27 on: September 15, 2019, 08:53:26 pm »
Since still your observations are different from mine (reading from ini file): Could you please check these changes whether they fix the issue, too? I think this version is more conformal to the widgetset implementation used:

- in Unit wspairsplitter.pp (in lcl/widgetset) add the method GetPosition to the interface declaration of TWSCustomPairSplitter:
Code: Pascal  [Select]
  1. type
  2.   TWSCustomPairSplitter = class(TWSWinControl)
  3.   published
  4.     class function AddSide(ASplitter: TCustomPairSplitter; ASide: TPairSplitterSide; Side: integer): Boolean; virtual;
  5.     class function RemoveSide(ASplitter: TCustomPairSplitter; ASide: TPairSplitterSide; Side: integer): Boolean; virtual;
  6.     class function GetPosition(ASplitter: TCustomPairSplitter): Integer; virtual;    // <--------------- ADDED
  7.     class function SetPosition(ASplitter: TCustomPairSplitter; var NewPosition: integer): Boolean; virtual;
  8. ...

- Somewhere in the implementation part of the unit add this code:
Code: Pascal  [Select]
  1. class function TWSCustomPairSplitter.GetPosition(ASplitter: TCustomPairSplitter): Integer;
  2. begin
  3.   if WSCheckHandleAllocated(ASplitter, 'GetPosition') then
  4.   begin
  5.     if ASplitter.SplitterType = pstHorizontal then
  6.       Result := ASplitter.Sides[0].Width
  7.     else
  8.       Result := ASplitter.Sides[0].Height;
  9.   end else
  10.     Result := ASplitter.Position;
  11. end;

- in unit pairsplitter.pas (in lcl) replace TCustomPairSplitter.SetPosition by this code:
Code: Pascal  [Select]
  1. procedure TCustomPairSplitter.SetPosition(const AValue: integer);
  2. begin
  3.   if (FPosition = AValue) and
  4.     (TWSCustomPairSplitterClass(WidgetSetClass).GetPosition(Self) = FPosition)   // <---- ADDED
  5.   then
  6.     Exit;
  7.  
  8.   FPosition := AValue;
  9.   if FPosition < 0 then
  10.     FPosition := 0;
  11.   if HandleAllocated and (not (csLoading in ComponentState)) then
  12.     TWSCustomPairSplitterClass(WidgetSetClass).SetPosition(Self, FPosition);
  13. end;

If the issue is fixed I can add the modification to trunk.
   
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

MarkMLl

  • Full Member
  • ***
  • Posts: 130
Re: Setting TPairSplitter position at runtime
« Reply #28 on: September 21, 2019, 09:48:29 pm »
Sorry about the delay. Yes, I think that fixes it.

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

wp

  • Hero Member
  • *****
  • Posts: 6224
Re: Setting TPairSplitter position at runtime
« Reply #29 on: September 21, 2019, 10:51:19 pm »
Comitted to trunk (r61909) and requested back-porting to fixes.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10