登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> CLQ工作室开源代码 >> 主题: [macos/cocoa/appkit/lazarus]原生 objectc appkit 如何实现 TMemo - 通过 lazarus 学习原生 macos 开发     [回主站]     [分站链接]
标题
[macos/cocoa/appkit/lazarus]原生 objectc appkit 如何实现 TMemo - 通过 lazarus 学习原生 macos 开发
clq
浏览(105) + 2023-12-02 15:14:36 发表 编辑

关键字:

[2023-12-02 15:29:19 最后更新]
[macos/cocoa/appkit/lazarus]原生 objectc appkit 如何实现 TMemo - 通过 lazarus 学习原生 macos 开发

类似的方法我们已经学习过打开对话框的实现了。这次是更啰嗦的 TMemo 的实现。
早就听说了 macos 的appkit 相比 ios 的 uikit 原始很多,这次在实现 TMemo 时狠狠的体验到了。

话说带滚动条的多行文本控件在 macos 原生 ui 库中如何实现? 实际情况让人大跌眼镜,居然是要两种控件组合起来才行。而且在 xcode 设计器中加入一个多行文本控件时就是如何实现的。
我唯一见过这样代码生成与设计器生成对就不了的情况是 android 的界面,它的设计器中的控件,要代码生成的话很多时候也是多种控件组成的。

其中一种方法是 NSScrollView 加 NSTextView 。

其实 lazarus 中就有完善的 TMemo 实现,那么它是怎么做的呢? 其实有了上次的经验,本来也是不难找到,但找到相应代码只花了 1 小时,配置环境却经历了多次失败,推倒重来,花了近一周。

最后的环境是两个:vscode - 这个比较简单;另外是可调试的 lazarus 环境,这就难了,因为现在 lazarus 还没有 arm 的,而我是 m1 芯片,最后配置方法大家可以找下本站相关贴子。

配置好 lazarus 环境后要找到它的实现其实就比较容易了,因为我已经知道其是 NSTextView 实现的,而它的自动换行是要调用原生 setWidthTracksTextView 方法的,所以以这两个为关键字就可以很容易的找到控件实现的代码在哪里。

最简单的其实是利用 lazarus 的单步调试,因为 lazarus 的调试有一个特点 -- 它可以一直步进到功能实现的最低层代码(本身 delphi 也可以,但要求你先要将源码路径加入调试路径,如果你本来就不知道是在哪实现的,那就不行了)。所以 lazarus 这一特点目前还真找不到替代的环境来实现。

我是通过
memo1.WordWrap := false;
来调试到最底层代码的。不过如果属性本身就是这个是跳转不到的,所以可以考虑用两行
memo1.WordWrap := false;
memo1.WordWrap := true;

这样总有一行能跳转到。

实际情况我就是因为属性相同没能跳转到,所以我是用 vscode 的查找来实现的。
TMemo 的源码理论在两个地方都有,分别是
/usr/local/share/fpcsrc
/Applications/Lazarus/lcl

我本来以为是两个地方都要查看的,不过实际上我在 /Applications/Lazarus/lcl 就直接找到了。
方法是,在 vscode 中打开目录 /Applications/Lazarus/lcl 。这其实也不太容易,我的方法是,在 Finder 中的 “转到” 功能中打开后将它拖放到左边的访问位置列表中,这样才能在 vscode 中打开。

然后用 vscode 的查找功能,看看哪个地方调用了 setWidthTracksTextView 。
很快就可以找到文件 cocoawsstdctrls.pas 中的 TCocoaWSCustomMemo 控件。但是 lazarus 中并没有找到 TMemo 的继承链是怎么关联起来的。
这其实也好办,再搜索 TCocoaWSCustomMemo 看是哪里调用它就行了。目前的是 cocoawsfactory.pas 中通过一个类替换函数来实现的,源码如下

function RegisterCustomMemo: Boolean; alias : 'WSRegisterCustomMemo';
begin
RegisterWSComponent(TCustomMemo, TCocoaWSCustomMemo);
Result := True;
end;

关键就是这个 RegisterWSComponent 了,它的功能比较奇怪,lazarus 为什么要这样做?以后再研究吧。
以上代码的全路径为
/Applications/Lazarus/lcl/interfaces/cocoa/cocoawsfactory.pas

所以最后我们只要研究 cocoawsstdctrls.pas 中的以下函数,就知道 TMemo 是如何实现的了
class function TCocoaWSCustomMemo.CreateHandle

具体代码如下,基本上可以照抄。我们也看到了它也是用 NSScrollView 混合实现的。

--------------------------------------------------------

class function TCocoaWSCustomMemo.CreateHandle(const AWinControl: TWinControl;
const AParams: TCreateParams):TLCLIntfHandle;
var
txt: TCocoaTextView;
ns: NSString;
scr: TCocoaScrollView;
nr:NSRect;
r:TRect;
layoutSize: NSSize;
lcl: TLCLCommonCallback;
begin
scr := TCocoaScrollView(NSView(TCocoaScrollView.alloc).lclInitWithCreateParams(AParams));

nr.origin.x:=0;
nr.origin.y:=0;
nr.size.height:=0;
nr.size.width:=AParams.Width;

txt := TCocoaTextView.alloc.initwithframe(nr);
txt.setAllowsUndo(true);
// setting up a default system font (to be consistent with other widgetsets)
txt.setFont( NSFont.systemFontOfSize( NSFont.systemFontSizeForControlSize(NSRegularControlSize) ));
txt.setRichText(false);
txt.setImportsGraphics(false);
txt.setUsesRuler(false);

// this is necessary for Ward Wrap disabled, so NSViewText
// doesn't have a constraint to resize
// Apple default maxsize is InitialWidth, 10000000
// (MaxSize is also changed automatically, if NSViewText size is changed)
txt.setMaxSize(NSMakeSize(10000000, 10000000));
scr.setDocumentView(txt);

scr.setHasVerticalScroller(VerticalScrollerVisible[TMemo(AWinControl).ScrollBars]);
scr.setHasHorizontalScroller(HorizontalScrollerVisible[TMemo(AWinControl).ScrollBars]);
scr.setAutohidesScrollers(ScrollerAutoHide[TMemo(AWinControl).ScrollBars]);
scr.setDrawsBackground(false);

ScrollViewSetBorderStyle(scr, TCustomMemo(AWinControl).BorderStyle);
UpdateFocusRing(txt, TCustomMemo(AWinControl).BorderStyle);

nr:=scr.documentVisibleRect;
txt.setFrame(nr);
txt.lclSetEnabled(True);

// ToDo: This should be made selectable in the LCL
txt.setAutomaticQuoteSubstitutionEnabled(False);
txt.setAutomaticLinkDetectionEnabled(False);
// macOS 10.6 version
if txt.respondsToSelector(objcselector('setAutomaticDataDetectionEnabled:')) then
txt.setAutomaticDataDetectionEnabled(false);
if txt.respondsToSelector(objcselector('setAutomaticTextReplacementEnabled:')) then
txt.setAutomaticTextReplacementEnabled(False);
if txt.respondsToSelector(ObjCSelector('setAutomaticDashSubstitutionEnabled:')) then
txt.setAutomaticDashSubstitutionEnabled(False);
if txt.respondsToSelector(ObjCSelector('setAutomaticSpellingCorrectionEnabled:')) then
txt.setAutomaticSpellingCorrectionEnabled(False);

// defaulting to System colors
// This makes NSTextView to be responsive to theme color change (Mojave 10.14)
txt.setTextColor(NSColor.textColor);
txt.setBackgroundColor(NSColor.textBackgroundColor);
scr.setFocusRingType(NSFocusRingTypeExterior);

lcl := TLCLCommonCallback.Create(txt, AWinControl);
lcl.ForceReturnKeyDown := true;
txt.callback := lcl;
txt.setDelegate(txt);

SetNSText(txt, AParams.Caption);

scr.callback := txt.callback;

TextViewSetWordWrap(txt, scr, TCustomMemo(AWinControl).WordWrap);
TextViewSetAllignment(txt, TCustomMemo(AWinControl).Alignment);
txt.wantReturns := TCustomMemo(AWinControl).WantReturns;
txt.callback.SetTabSuppress(not TCustomMemo(AWinControl).WantTabs);
Result := TLCLIntfHandle(scr);
end;






总数:0 页次:1/0 首页 尾页  
总数:0 页次:1/0 首页 尾页  


所在合集/目录
macos和ios混合编程 更多
appkit 更多
lazarus调试 更多



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1