Jump to content

Incus J

Members
  • Content Count

    157
  • Joined

  • Last visited

Posts posted by Incus J


  1. I've found a post from April 2019 by Dave Nottage that looks like it might be adapted to do the trick:

    // Bit of a "hack" to get the exact paths of the pdf files deployed to /Contents/Resources/Startup
      LSourceName := TPath.GetDirectoryName(ParamStr(0));
      LSourceName := TPath.Combine(StringReplace(LSourceName, '/MacOS', '', []), 'Resources/Startup/' + AFileName);

    ...seems to work OK 🙂.  Would make a nice addition to the built in TPath class:

    class function TPath.GetAppResourcesPath:string;
    var
      appPath:string;
    begin
      appPath:=TPath.GetDirectoryName(ParamStr(0));
      result:=TPath.Combine(StringReplace(appPath,'/MacOS','',[]),'Resources/Startup/');
    end;

    ...though it would need adapting further to produce a sane result on all supported platforms.

    • Like 1

  2. I’ve added a folder of sample images to an FMX application.  They appear listed in the Deployment manager.  When the app is deployed on macOS, these images are included inside the application bundle in Contents/Resources/Startup/

     

    How can I obtain a normal directory path to this internal folder, for internal use by the application?

     

    For example is there something like TPath.GetInternalResourcesFolder; ?

     

    I’m looking for the path to the folder only, rather than a full path to one single image file, so my app can load up all sample images in that folder when it starts up, without me having to reference each image filename individually in code.


  3. On 10/9/2020 at 9:28 PM, Lajos Juhász said:

    var x: TCaretPosition;
    begin
      x.Line:=5;
      x.Pos:=1;
      MyMemo.Model.SelectText(x, 50);

    I've just tried your suggestion, and it works perfectly.  I don't think I would ever have thought to look under .Model for a SelectText method.  It even scrolls the line into view, and it works when the memo is ReadOnly too.  Thank you Lajos!

    • Thanks 2

  4. I'd like to calculate the width of a Popup Menu before it is displayed, so that I can position and align it accurately when I call its .Popup(x,y) method.

    function CalcPopupMenuWidth(popup : TPopupMenu) : single;
    var
      i : integer;
      w : single;
      item : TMenuItem;
    begin
      result := 0;
      for i := 0 to popup.ItemsCount-1 do begin
        item := popup.Items[i];
        w := popup.Items[i].Width;
        if w > result then result := w;
      end;
    end;

    The above approach iterates over the individual menu items, to determine the widest item.  It almost works, in that it calculates the width correctly, but only after the Popup Menu has been displayed once.  If the Popup Menu has never been displayed (yet) then this code always returns a value of 50.  I'm guessing maybe that's a default width for a TMenuItem, which gets updated with the actual width shortly before it is displayed.

     

    How can I calculate the dimensions of a Popup Menu before displaying it?


  5. I'll go with the above code for now, until I find a better way to do it.  I'm just trying to figure out how to calculate the width of the Popup Menu before it appears, so I can align it centrally or right-aligned.  At the moment it displays left aligned (to the left edge of the button) - right aligned to the right edge of the button would be particularly useful as the button is positioned at the very right hand side of the form.

     

    I could do with a property like :

    PopupMenu.Width; //no such property

    TMenuItem has CalcSize and CalcRenderSize so perhaps I could iterate over the Items to determine a value for the widest Menu Item.


  6. Yes, I'm probably trying to use it for something other than intended (this happens to me quite a lot).  Still, click a button for a menu to appear, must be a fairly common requirement.  I'll see whether FMX offers another control better suited for this.

     

    Ah - I've just seen your last reply, thank you - I will take a look.


  7. Hi Virgo - Yes, it pops up OK on right click (but the menu tends to overlap the button, obscuring it).  I've come up with this for left click :

    procedure TmyForm.myButtonClick(Sender: TObject);
    var
      h:single;
      pt:TPointF;
    begin
      h:=myButton.Height;
      pt:=myButton.LocalToAbsolute(PointF(0,h));
      pt:=Self.ClientToScreen(pt);
      myButton.PopupMenu.Popup(pt.x,pt.y);
      end;

    ...though it seems a bit convoluted, and I'm not sure how robust it is.


  8. I've created a TPopupMenu, and assigned it to the PopupMenu property of a TButton.  When the button is left clicked I'd like the assigned PopupMenu to appear just below the button.

     

    The documentation suggests "Use PopupMenu to set the context menu of the current control. The pop-up window is displayed when ShowContextMenu is called."

     

    In the button OnClick handler, I've added :

    procedure TmyForm.myButtonClick(Sender: TObject);
    begin
      myButton.ShowContextMenu;
      end;

    This doesn't compile :

    [dccosx64 Error] myForm.pas(73): E2362 Cannot access protected symbol TControl.ShowContextMenu

    The procedure ShowContextMenu seems to be Protected.  Perhaps it is not meant to be called directly, but I haven't spotted an auto-popup option yet.  ShowContextMenu takes coordinates, but I'd like FMX to decide the best place to display the PopupMenu for me, rather than me having to specify coordinates, because the form can be resized or maximised, and the width of the PopupMenu may vary depending on the length of the menu items, so the ideal position is likely to be variable.

     

    How do I get a button to show its assigned popup menu (in a sensible position) when left clicked?


  9. Thanks Uwe,

     

    I've had a look in the Object Inspector, and in the documentation, but there doesn't seem to be a HideSelection property on TMemo.  However there is HideSelectionOnExit.  I have that set that to false at the moment, but no joy - when I make a selection in code via SelStart and SelLength, no highlight is displayed.


  10. I'd like to search for, and then highlight a line or phrase of text in a TMemo.  I can get the Caret to move to the start of the correct location via SelStart, but when I set SelLength the selection does not expand visibly.  The text highlight does not appear.  Here's my approach :

    s := 'section';
    i := MyMemo.Lines.Text.IndexOf('['+s+']');
    if i>-1 then begin
      MyMemo.SetFocus;
      MyMemo.SelStart := i+1;
      MyMemo.SelLength := s.Length;
      MyMemo.Repaint;
      //MyMemo.ScrollTo(MyMemo.Caret.Pos.Y,0);

    I suspect the selection is actually being made successfully, internally behind the scenes, but the control is failing to highlight its current selection.  If I start typing, the not-visibly-selected text is replaced with what I type.  I added Repaint to see whether that would give the control a gentle kick to persuade it to display its selection highlight, but no joy so far.  I can highlight text with the mouse OK, and the control is set to display its highlight whether focussed or not (HideSelectionOnExit is false).  The memo is not currently ReadOnly.  I've also tried toggling the control type between Platform and Styled, but the behaviour remains the same.

     

    (You can also spot that I'd eventually like to scroll the selected line into view, and I'd also like this to happen on a ReadOnly memo without giving the memo focus - but one step at a time.)


  11. That's unfortunate, I did like the concept of helpers.  Really I just wanted to add a couple of extras, to make my coding a little less verbose with fewer brackets e.g.

    mystring := mystring.Trim.Capitalise;
    
    vs
    
    mystring := TMyStringUtils.Capitalise(Trim(mystring));

     

    ...the flow and readability of the first version has some appeal.  I find it easier and quicker to parse.  I think adding the ability to inherit from an existing helper would make helpers more practical.  Better would be types supporting multiple helpers simultaneously.  Hey, I can dream 🙂

     

    Thank you for the heads up!


  12. I’m creating a record helper for string.

    It seemed like a good idea at first, until I realised string already has a helper, and I can’t inherit from it (?)

     

    So I think I need to duplicate those existing default string helper functions in my new string helper, and call into the original.  Something like this :

    function TStringHelper.ToInteger: integer;
    begin
      result := System.SysUtils.TStringHelper.ToInteger(Self);
    end;
    
    [dccosx64 Error] My.Str.Helper.pas(36): E2018 Record, object or class type required

    Don’t laugh, am I anywhere close…?


  13. I have a two column TStringGrid on my main form.  I set the column widths in the form designer at design time.  I think this used to work, but at runtime most designer column widths are now ignored - all columns default to the width of the first column.

     

    Is it possible to get the app to apply the other column widths set at design time?  I can set a column width in code :

    MyGrid.Columns[1].Width:=200;

    but that involves a bit of trial and error, so I'd prefer to set it visually.

     

    Incidentally while playing around with this, I found :

    MyGrid.Columns[1].Align:=TAlignLayout.Client;

    autosizes the last column to fill remaining space, which is useful.  However this TStringColumn Align property does not appear in the Object Inspector - it is not a Published property, so this can only be set in code.


  14. Oh, I never thought of that.  At the moment the following seems to work but requires two event handlers :

    procedure TMainForm.MyListViewChange(Sender: TObject);
    begin
      MyController.ShowMatch(MyListView.ItemIndex); //Pass the new index to my code for processing.
    end;
    
    procedure TMainForm.MyListViewItemClick(const Sender: TObject;
      const AItem: TListViewItem);
    begin
      MyListView.ItemIndex:=AItem.Index; //Set the item index manually.
      MyListView.OnChange(Sender);       //Call the OnChange handler.
    end;

    So OnChange is the same as before (fired for arrow keys, and also click-and-hold)

    OnItemClick fires for a normal mouse click, sets the ItemIndex itself, then simulates an OnChange call.

     

    That combination seems to catch all the various ways an item can be selected.  However it's likely OnChange will get called multiple times per selection in some cases (i.e. when Click and Change both fire).

     

    There's also a workaround posted in the bug report comments, which looks to achieve the same kind of thing in reverse.


  15. OK, I've voted! (and added a comment)

     

    Now I have to figure out a workaround.  OnClick seems to fire, but unfortunately it fires before the new selection comes into effect.  So ItemIndex still points to the previous item.  I wonder if there is a way to retrieve the new upcoming index value from within the OnClick event handler?  Or by the time OnMouseUp fires, ItemIndex might have updated by then?

     

    Or maybe in OnClick I could call Application.ProcessMessages followed by a call to the OnChange event handler manually.  Could get messy.


  16. I have an FMX project with a TListView control.  I need to detect when the user selects an item in the list.  There's no OnItemSelected event, but previously I used the control's OnChange event for this.  Since upgrading from 10.4 to 10.4.1 this no longer works and I'm seeing odd behaviour :

     

    • Click on any item in the listview - OnChange event does not trigger 😞
    • Select an item using the Up/Down arrow keys on the keyboard - OnChange event triggers OK 🙂
    • Click and hold down on an item for a couple of seconds until the blue selection rectangle updates - OnChange event then triggers on release 😕

     

    It's as though if the user releases their mouse button before the selection rectangle moves to the new item (which takes a second or so) then the item is still selected visually as expected, but the accompanying OnChange event fails to fire. 

     

    The documentation says FMX.ListView.TListViewBase.OnChange "Occurs when the ItemIndex property changes as a result of a user selecting a different item.  Write an OnChange event handler to respond to changes of the ItemIndex property. OnChange allows a response once the list has been successfully changed."

     

    I've tried using the similar OnChangeRepainted event instead, but get the same result.  Has anyone else encountered an issue with TListView OnChange?

     

    (In case it is significant I'm using a laptop, so trackpad rather than a mouse)


  17. Thanks Lars.  I had no luck switching thread, but found the routine mentioned in the call stack manually.  It is useful that the code gives the thread a label in Debug mode.  I've set a Breakpoint prior to the hang so I can step through the code and see what parameters are being passed in.  Unfortunately Breakpoints don't seem to be working in this unit.  Breakpoints look to be set OK at design time, but become unavailable (X) at runtime, so the program runs past.

     

    394627394_BreakpointUnavailable.thumb.png.1504bd7d4e885af384c81d114046b45a.png

     

     


  18. I've had a go at stepping through code from my app.  It's a bit tricky because the units such as Ics.Fmx.OverbyteIcsWndControl contain no actual code, just an include {$I OverbyteIcsWndControl.pas}, which the Debugger isn't pulling in, so I don't get to see the lines of code I Trace Into or Step Over in the IDE.  It is stepping though, because the call stack changes when I press F7, and I occasionally step into the System unit.

     

    Shortly before the hang, the call stack looks like this :

    Ics.Fmx.Overbyteicshttpprot.THttpCli.SetTerminated(bool)(0x0000000203a33170,false)
    Ics.Fmx.Overbyteicswndcontrol.TIcsWndControl.TIcsWndControl(System.Classes.TComponent*)(0x0000000203a33170,false,0x000000020bd3b640)
    Ics.Fmx.Overbyteicshttpprot.THttpCli.THttpCli(System.Classes.TComponent*)(0x0000000203a33170,true,0x000000020bd3b640)

    And the hang occurs after/when the System unit executes :

    constructor TObject.Create;
    begin
    end;

    Oh, here we go - this looks more useful :

    :00007FFF6E68AE36 semaphore_wait_trap
    :00007FFF6E4F0AED _dispatch_sema4_wait
    :00007FFF6E4F0FBF _dispatch_semaphore_wait_slow
    System.Syncobjs.TEvent.WaitNoReset(unsigned int)(0x0000000205815ff0,4294967295)
    System.Syncobjs.TEvent.WaitFor(unsigned int)(0x0000000205815ff0,4294967295)
    Ics.Posix.Messages.TMultiReadExclusiveWriteSynchronizer.WaitForWriteSignal()(0x0000000105809620)
    Ics.Posix.Messages.TMultiReadExclusiveWriteSynchronizer.BeginWrite()(0x0000000105809620)
    Ics.Posix.Messages.TIcsMessagePump.AfterConstruction()(0x000000020beeb1b0)
    System._AfterConstruction(System.TObject*)(0x000000010bed9a40)
    System.TObject.TObject()(0x000000010bed9a40,true)
    Ics.Fmx.Overbyteicswndcontrol.TIcsWndControl.AllocateHWnd()(0x0000000203a33170)
    Ics.Fmx.Overbyteicshttpprot.THttpCli.THttpCli(System.Classes.TComponent*)(0x0000000203a33170,true,0x000000020bd3b640)

    So the app hangs at semaphore_wait_trap.  Not sure what that is yet, but there is a Thread #7 labelled :

     

    TIcsAsyncSocketThread (7) with the following Call Stack

    kevent
    Ics.Fmx.Overbyteicswsocket.TIcsEventQueue.HandleEvents()
    Ics.Fmx.Overbyteicswsocket.TIcsAsyncSocketThread.Execute()
    System.Classes.ThreadProc(System.Classes.TThread*)
    System.ThreadWrapper(void*)
    _pthread_start
    thread_start

    Could Thread #1 be waiting for Thread #7 to complete or sync?  Other threads do exist (4, 10, 14, 19) but are all unlabelled.


  19. The latest overnight zip looks good.  I can get both IcsHttpsTst and also my own app to Build successfully for macOS 64-bit (but not run successfully at present).

     

    IcsHttpsTst hangs when it starts up (the main form is never displayed).

    My own app runs OK, and is functional until it tries to use ICS - at which point it hangs too.

     

    Both apps are actually still running, but seem stuck in an infinite loop.  If I select Program Pause from the IDE, a CPU tab appears filled with assembly language, showing the current execution point in Thread #1.  I can perform commands such as Run Until Return.

    Thread #1
    -> 00007FFF6E68AE36 C3               ret 
       00007FFF6E68AE37 90               nop 
       00007FFF6E68AE38 4989CA           mov r10, rcx
       00007FFF6E68AE3B B825000001       mov eax, 0x1000025
       00007FFF6E68AE40 0F05             syscall 
       00007FFF6E68AE42 C3               ret 
       00007FFF6E68AE43 90               nop 
       00007FFF6E68AE44 4989CA           mov r10, rcx
       00007FFF6E68AE47 B826000001       mov eax, 0x1000026
       00007FFF6E68AE4C 0F05             syscall 

    Is there a 'Run Until Inside A Delphi Unit' command somewhere?


  20. I think FMX renders its UI to a surface/canvas, rather than having the OS render the controls (?) - so yes, it could be different.

     

    This is where the IcsHttpsTst seems to get stuck at startup:  Line 1962 of Ics.Posix.Messages

    function TMultiReadExclusiveWriteSynchronizer.BeginWrite: Boolean;
    var
      Thread: PThreadInfo;
      HasReadLock: Boolean;
      ThreadID: Cardinal;
      Test: Integer;
      OldRevisionLevel: Cardinal;
    begin
    ...
    ThreadID := GetCurrentThreadID;

    The top of the call stack looks like this :

     

    :000000010001D0F0 System::_DbgExcNotify(int, void*, System::SmallString<(unsigned char)255>*, void*, void*)
    :000000010001D12E System::NotifyReRaise(System::TObject*, void*)
    :000000010001D1CA System::_RaiseAtExcept(System::TObject*, void*)
    :0000000100056AB8 System::Sysutils::ErrorHandler(unsigned char, void*)
    :0000000100015F30 System::ErrorAt(unsigned char, void*)
    :000000010001B252 System::_BoundErr()
    Ics.Posix.Messages.TMultiReadExclusiveWriteSynchronizer.BeginWrite()(0x0000000102521120)
    Ics.Posix.Messages.TIcsMessagePump.AfterConstruction()(0x000000020245bd80)
    :0000000100016B45 System::_AfterConstruction(System::TObject*)
    :0000000100011443 System::TObject::TObject()
    Ics.Fmx.Overbyteicswndcontrol.TIcsWndControl.AllocateHWnd()(0x0000000203080ff0)
    Ics.Fmx.Overbyteicshttpprot.THttpCli.THttpCli(System.Classes.TComponent*)(0x0000000203080ff0,true,0x000000020801e5f0)

    ...

     

    Maybe that contains useful information.  On Posix, the System unit defines:

    TThreadID = NativeUInt;

    But local var ThreadID is Cardinal.  Maybe Cardinal and NativeUInt are not compatible.  If I redefine ThreadID as a NativeUInt too, then the ERangeError disappears, and the app is "[Running]" - but the UI does not appear.  I think Thread #1 might be waiting for something.


  21. I remember back in Delphi 10.2 Tokyo FMX ProcessMessages seemed to behave differently to VCL ProcessMessages.

     

    With VCL I can call ProcessMessages and any queued mouse clicks are processed.

    With FMX I found that a call to ProcessMessages did not process queued mouse clicks.

     

    It was as though FMX UI events were handled separately.  I don't know whether this is still the case.


  22. That sounds good.  I've just tested the latest overnight zip, and D104InstallVclFmx builds successfully for macOS 64-bit.

     

    The sample app IcsHttpsTst builds successfully by adding the Ics.Fmx. prefix to a few IcsHttpTst1.pas uses units.

     

    When the sample app deploys and runs on macOS 64-bit I get an ERangeError "Range check error" at runtime.  I think it occurs in Ics.Posix.Messages.pas but the IDE debugger can't find that unit, so I'm not sure.  I must need to tell the IDE/Debugger the path to the ICS source units so it can step into them - but I'm not sure how to do this.  The Delphi compiler already knows where the source units are, because the app builds and deploys OK, but the IDE itself seems unaware at the moment.

×