【问题标题】:Snapping mouse cursor to a line in a Delphi custom control将鼠标光标捕捉到 Delphi 自定义控件中的一行
【发布时间】:2016-01-15 12:11:17
【问题描述】:

我想知道如何实现水平(或垂直)将鼠标光标捕捉到一行。例如,在 Facebook 时间线功能上,当您将鼠标悬停在中心下方的线上时,它会将光标捕捉到中心。将鼠标靠近线条也会捕捉它。

我想将其包装在单个自定义控件中,而不是表单的控件中。将有一条垂直线或水平线,当它靠近任何地方时,它必须将鼠标光标捕捉到它。我将添加返回被点击的行的位置的事件。此控件还将有一个垂直或水平滚动条,与这条线平行,并且两个滚动条永远不会同时显示。有一个主属性是显示此行垂直还是水平

鼠标实际上不应该移动位置,而只是光标的显示应该以某种方式调整到显示它在这条线的中心,而实际的X(或Y)位置永远不会改变.我不想移动光标,如果光标靠近,我想显示光标在这条线的中心。当光标处于此捕捉位置时,它将显示另一个自定义光标,而不是标准(或默认)箭头。

我只需要知道如何在此控件中处理鼠标指针到达该行附近的事件,并将光标的显示调整到该行的中心。

【问题讨论】:

  • 听起来很愚蠢,以防万一有人想继续为我构建一个控制外壳来演示这一点,请提前告诉我,以便我知道等待它。否则,我仍然愿意接受有关如何处理此问题的任何建议。
  • 我没有对你投反对票,但我对这个问题感到困惑。标题说“捕捉鼠标光标”,但文本说“鼠标实际上不应该移动位置,但应该调整光标的显示”。你是什​​么意思光标不应该移动但它应该移动?我是唯一一个觉得这很矛盾的人吗?我刚刚看了 Facebook 时间线,我不明白。图片?
  • @WarrenP X、Y 位置(用户指向鼠标的位置)永远不会改变,但光标显示位置的视图会改变。因此,例如,您可以将其指向 60,100,但它会显示在 55,100,而将其指向 50,100 也会将其显示在 55,100。
  • 你可以说我试图在 GUI 上撒谎关于用户实际将鼠标指向的位置并更改此 + 显示的位置,而不触及实际鼠标坐标。添加到我之前的评论 - X 轴上的灵敏度范围在 50 到 60 之间 - 如果用户指向该范围内的任何位置,它看起来就像他们将其指向 55。
  • 另一个例子 - 在 FB 时间线上 - 将鼠标从左向右缓慢移动到中心线。当它靠近时,它会将焦点转移到线条上。继续缓慢水平移动,它似乎没有移动 - 直到你将鼠标移动到线的右侧很远的距离 - 然后指针跳回到它所属的右侧。

标签: delphi drawing custom-controls mouse mouse-cursor


【解决方案1】:

捕捉需要你捕捉一些东西

  • 在 AutoCAD 中,“光标”实际上是与“光标”所在位置相交的水平和垂直线
  • Photoshop 使用 Windows 鼠标,但将效果捕捉到参考线
  • Facebook 将一个 + 小符号捕捉到时间轴上的某个位置

您需要跟踪鼠标的位置(即OnMouseMove),如果光标“足够近”,您就可以决定要做什么。

这是一个小示例程序,其中我在距左侧 150 像素处有一条假想的垂直线。如果我离那条线足够近,就会出现一点 Facebook +

const
    centerLine = 150; //"line" is at 150px from the left
    tolerance = 15; //15px on either size is about good.

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
    if IsMouseNearLine(x, y) then
    begin
        //We're on the centerline-ish. React by...
        //...changing the cursor to a <->
        Self.Cursor := crSizeWE;

        //and maybe let's put a "+" there, like Facebook
        if (FPlusLabel = nil) then
        begin
            FPlusLabel := TLabel.Create(Self);
            FPlusLabel.Parent := Self;
            FPlusLabel.Caption := '+';
            FPlusLabel.Alignment := taCenter;
            FPlusLabel.Font.Color := $00996600; //Windows UX "Main Instruction" color
            FPlusLabel.Font.Style := FPlusLabel.Font.Style + [fsBold];
        end;

        FPlusLabel.Left := centerLine-(FPlusLabel.Width div 2);
        FPlusLabel.Top := Y-(FPlusLabel.Height div 2);
        FPlusLabel.Visible := True;
    end
    else
    begin
        Self.Cursor := crDefault;
        if Assigned(FPlusLabel) then
            FPlusLabel.Visible := False;
    end;
end;

function TForm1.IsMouseNearLine(X, Y: Integer): Boolean;
begin
    if (x >= (centerLine-tolerance)) and (x <= (centerLine+tolerance)) then
    begin
        //we're close-ish to the line
        Result := true;
    end
    else
        Result := False;
end;

当您拥有其他控件时,事情开始变得棘手,每个控件都需要与MouseMove 消息一起播放。但是,如果您将它们全部转发给单个处理程序,这并不是糟糕;在执行之前执行 client-to-screen-to-formClient。

注意:任何代码都会发布到公共领域。无需署名。

【讨论】:

  • 谢谢,除了我要求将其包装在自己的自定义控件中(需要完全不同的方法),而且我需要更多关于如何显示光标的帮助,而不是识别它的位置。另外,我看到你创建了FPlusLabel,但我看不到它在哪里是免费的......
  • 您的子控件将需要它所在的窗体的帮助。当鼠标实际上不在表单上时,它不会得到MouseMoveClick 事件。你不需要释放FPlusLabel;当它的 owner 被释放时它被释放。 (FPlusLabel := TLabel.Create(theOwner);)。或者您可以将其归结为示例代码,尽可能多地省略以使答案尽可能清晰。标签的文本颜色也不符合用户的颜色偏好。
【解决方案2】:

我能看到的最简单的方法是使用自己的 Cursor 属性制作一个 TPaintBox 控件,因此您隐藏了内置的 Windows 光标,并且您在其“捕捉”位置绘制了“+”符号。

鼠标指针从未真正移动过,但当真正的鼠标指针位于 TPaintBox 的控制范围内时,它会被所有者绘制的光标图像“替换”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多