☆メモ上でマウス下のテキスト取得で使った GetWordAndPhrase名前空間を使い、エディットコントロールでカーソル下のテキストを選択させます。
実行させるとこんな感じです。
ここで問題になるのが、TEdit、TMemoは「ランタイムテーマの影響を受けてしまう」ことです。
TRichEditは、ランタイムテーマの影響を受けず、常にバイト数で扱いますが、TEdit、TMemoは
ランタイムテーマが有効時-文字数、無効時-バイト数となります。
今回、ランタイムテーマが有効、無効にかかわらず、バイト数で扱う処理を考えてみます。
uses
GetWordAndPhrase, AdjustedEdit;
procedure TForm1.Edit1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
CursolPos, sp, ep: Integer;
S: String;
begin
Exit;
CursolPos := LoWord(Edit1.Perform(EM_CHARFROMPOS, 0, MakeLParam(X, Y)));
CursolPos := GetSelStart_Byte(Edit1.Text, CursolPos);
S := GetWordAndPhraseUnderMouse(CursolPos, Edit1.Text, sp, ep);
if S <> ''then
begin
if GetSelStart(Edit1) <> sp then
begin
SetSelStart(Edit1, sp);
SetSelLength(Edit1, ep-sp+1);
end;
end;
if ActiveControl <> Edit1 then
ActiveControl := Edit1;
end;
procedure TForm1.Memo1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
CursolPos, sp, ep: Integer;
S: String;
begin
CursolPos := LoWord(Memo1.Perform(EM_CHARFROMPOS, 0, MakeLParam(X, Y)));
CursolPos := GetSelStart_Byte(Memo1.Text, CursolPos);
S := GetWordAndPhraseUnderMouse(CursolPos, Memo1.Text, sp, ep);
if S <> ''then
begin
if GetSelStart(Memo1) <> sp then
begin
SetSelStart(Memo1, sp);
SetSelLength(Memo1, ep-sp+1);
end;
end;
if ActiveControl <> Memo1 then
ActiveControl := Memo1;
end;
procedure TForm1.RichEdit1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
CursolPos, sp, ep: Integer;
S: String;
Pt:TPoint;
begin
Pt := Point(X,Y);
CursolPos := RichEdit1.Perform(EM_CHARFROMPOS, 0, Integer(@Pt));
S := GetWordAndPhraseUnderMouse(CursolPos, RichEdit1.Text, sp, ep);
if S <> ''then
begin
if RichEdit1.SelStart <> sp then
begin
RichEdit1.SelStart := sp;
RichEdit1.SelLength := ep-sp+1;
end;
end;
if ActiveControl <> RichEdit1 then
ActiveControl := RichEdit1;
end;
ランタイムテーマの設定にかかわらず、バイト単位で扱うための処理を集めた名前空間 AdjustedEdit
(名前が変なのは無視して~)
unit AdjustedEdit;
{ ランタイムテーマの設定にかかわらず、バイト単位で扱います。 }
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
StdCtrls, Buttons, ExtCtrls, Themes;
// SelTextを取得します。
function GetSelText(CustomEdit: TCustomEdit): String;
// SelStartをバイト数で設定します。
procedure SetSelStart(Edit: TCustomEdit; Position_Byte: Integer);
// SelStartをバイト数で取得します。
function GetSelStart(Edit: TCustomEdit): Integer;
// SelLengthをバイト数で設定します。
procedure SetSelLength(Edit: TCustomEdit; Length_Byte: Integer);
// SelLengthをバイト数で取得します。
function GetSelLength(Edit: TCustomEdit): Integer;
// 文字数で指定された位置のバイト数を返します。
function GetSelStart_Byte(S: WideString; Position_Moji: Integer): Integer;
implementation
// 指定位置のバイト数分の文字数を返します。
function GetMoji(S: String; Position: Integer): Integer;
var
P: PChar;
I: Integer;
Check: String;
begin
if not ThemeServices.ThemesEnabled then
begin
Result := Position;
Exit;
end;
Check := Copy(S, 1, Position);
P := PChar(Check+#0);
I := 0;
while (P^ <> #0) do
begin
if IsDBCSLeadByte(Byte(P^)) then
Inc(P,2)
else
Inc(P);
Inc(I);
end;
Result := I;
end;
// SelTextを取得します。
function GetSelText(CustomEdit: TCustomEdit): String;
var
S, Str: WideString;
I, Start, Len: Integer;
begin
if not ThemeServices.ThemesEnabled then
begin
Result := CustomEdit.SelText;
Exit;
end;
S := WideString(CustomEdit.Text);
Start := CustomEdit.SelStart + 1;
Len := Start + CustomEdit.SelLength -1;
Str := '';
for I := Start to Len do
Str := Str + S[I];
Result := Str;
end;
// SelStartをバイト数で設定します。
procedure SetSelStart(Edit: TCustomEdit; Position_Byte: Integer);
begin
Edit.SelStart := GetMoji(Edit.Text, Position_Byte);
end;
// SelStartをバイト数で取得します。
function GetSelStart(Edit: TCustomEdit): Integer;
begin
Result := GetSelStart_Byte(Edit.Text, Edit.SelStart);
end;
// SelLengthをバイト数で設定します。
procedure SetSelLength(Edit: TCustomEdit; Length_Byte: Integer);
var
I,J: Integer;
begin
I := GetSelStart(Edit);
if I = 0 then I := 1;
J := GetMoji(Edit.Text, I + Length_Byte);
Edit.SelLength := J-GetMoji(Edit.Text,I);
end;
// SelLengthをバイト数で取得します。
function GetSelLength(Edit: TCustomEdit): Integer;
begin
Result := Length(GetSelText(Edit));
end;
// 文字数で指定された位置のバイト数を返します。
function GetSelStart_Byte(S: WideString; Position_Moji: Integer): Integer;
var
Check: String;
begin
if not ThemeServices.ThemesEnabled then
Result := Position_Moji
else
begin
Check := Copy(S, 1, Position_Moji);
Result := Length(Check);
end;
end;
end.
上記、名前空間にSetSelTextという処理がないのは、次の処理がランタイムの設定に関係なく正常に動作するからです。
Edit1.Seltext := 'ややこしい!';
SelTextを表示させたら、でたらめなのに、この処理が正常に動作する理由がわかりません。
こんなややこしい処理で悩むのなら、TMemoだけでなく、TEditもTRichEditを次の設定にして代用した方が簡単なのかも知れませんね。
RichEdit1.WantReturns := False;
RichEdit1.WordWrap := False;