Определить, получает ли пользователь фокус компонента по вкладке или по клику

Как я могу определить, входит ли пользователь в компонент с помощью клавиши tab или мыши click?

ОБНОВЛЕНИЕ 1

На самом деле речь идет о TVirtualStringTree, который в зависимости от того, как он фокусируется, открывает пользовательский редактор, ориентированный на тот или иной столбец.

ОБНОВЛЕНИЕ 2

Проверьте код ниже.

procedure TForm1.Tree1Click(Sender: TObject);
var
  Tree: TVirtualStringTree;
  Click: THitInfo;
  HitNode: PVirtualNode;
  HitColumn: TColumnIndex;
  col: Integer;
begin
  Tree:= Sender as TVirtualStringTree;
  Tree.GetHitTestInfoAt(Mouse.CursorPos.X-Tree.ClientOrigin.X, Mouse.CursorPos.Y-Tree.ClientOrigin.Y, True, Click);

  HitNode:= Click.HitNode;
  if not Assigned(Click.HitNode) and Assigned(Tree.FocusedNode) then
    HitNode:= Tree.FocusedNode;

  HitColumn:= Click.HitColumn;

  //get first visible and editable column
  if (HitColumn <= NoColumn) or
     ((HitColumn > NoColumn) and
      (not (coVisible in Tree.Header.Columns.Items[HitColumn].Options) or
       not (coEditable in Tree.Header.Columns.Items[HitColumn].Options))) then
    if Tree.Header.Columns.Count > 0 then
      for col := 0 to Tree.Header.Columns.Count - 1 do
        if (coVisible in Tree.Header.Columns.Items[col].Options) and
           (coEditable in Tree.Header.Columns.Items[col].Options) then
          begin
            HitColumn:= col;
            Break;
          end;

  if Assigned(HitNode) and (HitColumn > NoColumn) then
    {if (Tree.IsEditing and (HitNode <> Tree.FocusedNode)) or
       ((not Tree.IsEditing) and (HitNode = Tree.FocusedNode)) then}
      Tree.EditNode(HitNode,HitColumn);
end;

procedure TForm1.Tree1Enter(Sender: TObject);
var
  Tree: TVirtualStringTree;
  Click: THitInfo;
  HitNode: PVirtualNode;
  HitColumn: TColumnIndex;
  col: Integer;
begin
  Tree:= Sender as TVirtualStringTree;

  HitNode:= Tree.FocusedNode;

  if not Assigned(Tree.FocusedNode) then
    HitNode:= Tree.GetFirstVisible;

  HitColumn:= NoColumn;

  //get first visible and editable column
  if Tree.Header.Columns.Count > 0 then
    for col := 0 to Tree.Header.Columns.Count - 1 do
      if (coVisible in Tree.Header.Columns.Items[col].Options) and
         (coEditable in Tree.Header.Columns.Items[col].Options) then
        begin
          HitColumn:= col;
          Break;
        end;

  if Assigned(HitNode) and (HitColumn > NoColumn) then
    Tree.EditNode(HitNode,HitColumn);
end;

Что я хотел бы сделать, так это:

  • для редактирования первого столбца сфокусированного узла, если пользователь вводит ключ Tab или
  • редактировать соответствующий выбранный столбец при вводе мышью click

Если я нажимаю на компонент, сначала запускается OnEnter, а затем OnClick, поэтому проблема в том, что узел редактирования запускается 2 раза.


person REALSOFO    schedule 24.08.2016    source источник
comment
Ожидается, что вам будет все равно. Ведь есть много других способов сделать это.   -  person David Heffernan    schedule 24.08.2016
comment
перехватывать все щелчки мыши, и если последний щелчок перед получением фокуса был на этот компонент, то, вероятно, это был он. Однако вам действительно должно быть все равно. Что, если я просто восстановлю свернутое окно — это еще один способ получить фокус. Что, если я сверну окно другого приложения - 4-й способ получить фокус. Что, если я разблокирую сеанс Windows или подключусь к работающему сеансу службы терминалов? и так далее и тому подобное.   -  person Arioch 'The    schedule 24.08.2016
comment
см. мое обновление ... Я боялся, что я слишком общий.   -  person REALSOFO    schedule 24.08.2016
comment
Мне все это кажется неправильным. Боюсь, вы совершаете ошибку. Вы полностью понимаете все различные способы, которыми пользователи могут взаимодействовать с вашей программой. Нажатие клавиши табуляции и использование мыши — не единственные способы ввода.   -  person David Heffernan    schedule 24.08.2016
comment
см. обновление 2. на самом деле редактор работает, но мне не нравится, что команда редактирования узла запускается 2 раза.   -  person REALSOFO    schedule 24.08.2016
comment
Теперь я понял, что есть еще одна проблема. Когда редактор закрывается, OnEnter дерева снова запускается...   -  person REALSOFO    schedule 24.08.2016


Ответы (2)


Поскольку фокус, достигнутый нажатием, всегда будет генерировать событие mousedown перед событием onEnter, вы можете установить событие в mousedown, говоря «gMousedown» := true, а затем в событии OnEnter вы можете проверить, было ли mousedown. Не забудьте сбросить gMousedown в false в событии onMouseUp.

person Danielle Dayna Sherstobitoff    schedule 25.08.2016

Чтобы узнать, был ли фокус получен с помощью tab или с помощью щелчка мыши, вам нужно проделать некоторую детективную работу.

  1. Вот код, который обеспечивает фокус при нажатии табуляции.
procedure TBaseVirtualTree.WMKeyUp(var Message: TWMKeyUp);

begin
  inherited;

  case Message.CharCode of
    VK_SPACE:
     .... [snip] ....
     VK_TAB:
       //This method causes a flurry of event handlers to be called.
       EnsureNodeFocused(); 
  end;
end;
  1. VTV имеет два события для смены фокуса: OnFocusChanged и OnFocusChanging в дополнение к стандартным событиям OnEnter и OnExit.
    Я понятия не имею, какое из них лучше всего соответствует вашим потребностям, вам придется поэкспериментировать.

Предположим, что фокус всегда устанавливается с помощью мыши, если мы не можем доказать, что он был получен с помощью tab. Это немного шаткое предположение, но неважно.

Сначала мы используем интерпозер для переопределения обработчика сообщений WMKeyUp.
WMKeyUp является закрытым, но, к счастью, обработчики сообщений всегда можно переопределить, даже если они частные.

Промежуточный элемент
Вы можете использовать хитрость, называемая вставкой для переопределения VST в соответствии с нашими потребностями.
Из-за правил области действия "улучшенный" TVST занимает место VST по умолчанию. Это не мешает потоковой передаче или созданию форм или чему-либо еще. Это просто работает.

type
  TVirtualStringTree = class(VirtualTrees.TVirtualStringTree)
  private
    FTabPressed: boolean;
  protected
    procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
    property TabPressed: boolean read FTabPressed;
  end;

  TForm56 = class(TForm)
    VirtualStringTree1: TVirtualStringTree;
    ....


procedure TVirtualStringTree.WMKeyUp(var Message: TWMKeyUp);
begin
  case Message.CharCode of
    VK_TAB: FTabPressed:= true;
    else FTabPressed:= false;
  end; {case}
  inherited;
  FTabPressed:= false;
end;

Обработчики событий фокуса
Используйте один из этих два три обработчика событий.

//use the standard onEnter....
procedure TForm56.VirtualStringTree1Enter(Sender: TObject);
begin
    if VirtualStringTree1.TabPressed then .....
  else ....
end;

//... or use this event, or...
procedure TForm56.VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
begin
  if VirtualStringTree1.TabPressed then .....
  else ....
end;

//.... this event if you want to mess with the focus.
procedure TForm56.VirtualStringTree1FocusChanging(Sender: TBaseVirtualTree;
  OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex;
  var Allowed: Boolean);
begin
  Allowed:= VirtualStringTree1.TabPressed; //just a silly example.
end;
person Johan    schedule 24.08.2016