Firemonkey中的游标处理

问题描述 投票:0回答:1

谁能澄清Delphi FMX 10.3.1中的游标如何工作?我有一个冗长的动作,我希望应用程序的光标显示为动作执行时的crHourglass。在以下代码中,我介绍了3个用于将光标设置为crHourglass的选项。

procedure TFormMain.ActionFindExactMatchesExecute(Sender: TObject);
const
  CCursorOption= 2;
var
  IterationContextHits: TIterationContextHits;
begin
  PanelResults.SendToBack;
  PanelProgress.BringToFront;
  case CCursorOption of
    0: Self.Cursor:= crHourglass;
    1: ButtonFindExactMatches.Cursor:= crHourglass;
    2: CursorManager.SetCursor(crHourglass);
  end;
  {Create TIterationContextHits object to hold progress variables:}
  IterationContextHits:= TIterationContextHits.Create;
  try
    {Lengthy code that searches multiple files for string matches}
    {Report result of operation:}
    ShowMessage('Number of matches found: ' + IntToStr(FHitCount));
    {Update GUI:}
    DataToControls;
    PanelResults.BringToFront;
  finally
    IterationContextHits.Free;
    case CCursorOption of
      0: Self.Cursor:= crDefault;
      1: ButtonFindExactMatches.Cursor:= crDefault;
      2: CursorManager.RestorePrevCursor;
    end;
  end;
end;

在第一个选项中,我希望将MainForm的Cursor属性设置为crHourGlass,应用程序将显示InheritedCursor属性,该属性应在组件的z顺序堆栈中搜索所有返回光标值不是crDefault的第一个组件的主窗体的方法。但这不起作用。

在第二个选项中,我设置了链接到该动作的按钮的cursor属性。如果单击该按钮以启动操作,光标更改起作用。但是,如果操作是从主菜单项启动的,则不会。

在第三个选项中,我使用编写的TCursorManager类的对象包装与平台相关的服务IFMXCursorService。这通常有效,但并非总是如此。该代码是:

TCursorRecord= record
    FCursor: TCursor;
    FStartTime: integer;
  end;

  TCursorRecordArray= array of TCursorRecord;

  TCursorManager= class
  private
    FCursorService: IFMXCursorService;
    FCursorRecordStack: TCursorRecordArray;
    FCursorRecordCount: integer;
  protected
    function GetCursorTickCount: integer;
  public
    constructor Create;
    destructor Destroy; override;
    function GetCursor: TCursor;
      {Returns currently set cursor}
    procedure SetCursor(Cursor: TCursor);
      {Sets new cursor}
    function RestorePrevCursor: TCursor;
      {Restores cursor previously set using this object}
    property Cursor: TCursor read GetCursor write SetCursor;
    property CursorTickCount: integer read GetCursorTickCount;
  end;

implementation

constructor TCursorManager.Create;
var
  CurrCursorRecord: TCursorRecord;
begin
  {Create platform-dependent cursor service:}
  if TPlatformServices.Current.SupportsPlatformService(IFMXCursorService) then
    FCursorService:= TPlatformServices.Current.GetPlatformService(IFMXCursorService)
                                              as IFMXCursorService;
  {Create current cursor record:}
  CurrCursorRecord.FCursor:= FCursorService.GetCursor;
  CurrCursorRecord.FStartTime:= GetTickCount;
  {Put current cursor record onto CursorRecordStack:}
  SetLength(FCursorRecordStack, 8);
  FCursorRecordCount:= 1;
  FCursorRecordStack[0]:= CurrCursorRecord;
end;

function TCursorManager.RestorePrevCursor: TCursor;
var
  PrevCursorRecord: TCursorRecord;
begin
  if Assigned(FCursorService) then
    begin
      if FCursorRecordCount>0 then
        begin
          {Remove current cursor record from stack:}
          FCursorRecordCount:= FCursorRecordCount - 1;
          PrevCursorRecord:= FCursorRecordStack[FCursorRecordCount-1];
          {Reduce size of stack array if possible:}
          if FCursorRecordCount mod 8 = 0 then
            SetLength(FCursorRecordStack, FCursorRecordCount);
          {Update start time of new curr cursor:}
          PrevCursorRecord.FStartTime:= GetTickCount;
          {Set previous cursor in system:}
          FCursorService.SetCursor(PrevCursorRecord.FCursor);
          {Return prev cursor:}
          Result:= PrevCursorRecord.FCursor;
        end;
    end;
end;

procedure TCursorManager.SetCursor(Cursor: TCursor);
var
  NewCursorRecord: TCursorRecord;
begin
  if Assigned(FCursorService) then
    begin
      {Set up new CursorRecord:}
      NewCursorRecord.FCursor:= Cursor;
      NewCursorRecord.FStartTime:= GetTickCount;
      {Add new cursor record to stack:}
      if FCursorRecordCount= Length(FCursorRecordStack) then
        SetLength(FCursorRecordStack, FCursorRecordCount + 8);
      Inc(FCursorRecordCount);
      FCursorRecordStack[FCursorRecordCount-1]:= NewCursorRecord;
      {Call system procedure to set cursor:}
      FCursorService.SetCursor(Cursor);
    end;
end;

实现我要达到的目标的最简单方法是什么?

user-interface delphi firemonkey
1个回答
0
投票

该问题似乎由以下事实解释。RAD Studio帮助中描述的游标行为是通过以下方法实现的:

procedure TControl.SetCursor(const Value: TCursor);
var
  CursorService: IFMXCursorService;
begin
  if FCursor <> Value then
  begin
    FCursor := Value;
    if FCursor <> crDefault then
      RefreshInheritedCursor
    else
    begin
      if Parent <> nil then
        RefreshInheritedCursor
      else
        FInheritedCursor := crDefault;
    end;

    if IsMouseOver and not (csLoading in ComponentState) and not (csDesigning in ComponentState) and
      TPlatformServices.Current.SupportsPlatformService(IFMXCursorService, CursorService) then
      CursorService.SetCursor(FInheritedCursor);
  end;
end;

当在继承自TControl类的组件上单击鼠标时,将调用上述过程。如果IsMouseOver为True,则光标更改起作用。因此,单击按钮时选项1起作用,因为单击按钮时鼠标悬停在其上方。但是,当单击链接到该动作的菜单项时,该过程不会被调用,因为在那种情况下,鼠标不在按钮上方,而是在菜单项上方。

人们曾以为Option 0应该起作用,因为无论在表单上单击鼠标的哪个位置,表单始终位于鼠标的下方。但是TForm不能从TControl继承,而只能从TFMXObject继承。方法TCustomForm.SetCursor只是将光标值分配给一个字段,而无需调用实现帮助文件中描述的行为的代码。因此,选项0不起作用。这里的FMX实现似乎还有待改进!

关于选项2中的方法,实际上实际上无法正常工作。沙漏会短暂显示,直到显示PanelProgress。这将导致光标切换回crDefault。

鉴于这些限制,我唯一能找到的解决方案是在PanelProgress中添加一个标记为“开始”的新按钮,并将先前在ActionFindExactMatchesExecute中的大多数代码移到新按钮的OnClick事件处理程序中。 ActionFindExactMatchesExecute变为:

procedure TFormMain.ActionFindExactMatchesExecute(Sender: TObject);
begin
  PanelResults.SendToBack;
  PanelProgress.BringToFront;
end;

而ButtonStartClick代码为:

procedure TFormMain.ButtonStartClick(Sender: TObject);
var
  IterationContextHits: TIterationContextHits;
begin
  ButtonStart.Cursor:= crHourglass;
  {…}
  Try
    {…}
    ShowMessage('Number of matches found: ' + IntToStr(FHitCount));
    {Update GUI:}
    DataToControls;
    PanelResults.BringToFront;
  finally
    IterationContextHits.Free;
    ButtonStart.Cursor:= crDefault;
  end;
end;

通过这些更改,无论单击了链接到该动作的哪个组件,所发生的一切都是将PanelProgress引入视图。然后只有一种方法来启动冗长的代码,即单击ButtonStart,因此鼠标必须在ButtonStart上方,因此Control.IsMouseOver为true。因此,显示了沙漏光标,但是操作已启动。

© www.soinside.com 2019 - 2024. All rights reserved.