研究心得------->Seskin控件包中SeskinEdit汉字问题的解决办法
2006-02-04 13:41:33 来源:WEB开发网核心提示:公司采用Seskin控件包来开发,却发现SeskinEdit在使用汉字是有问题,研究心得------->Seskin控件包中SeskinEdit汉字问题的解决办法,主要是由汉字时光标定位不准,鼠标选字也选不准,其实TCanvas提供了一个TextLength方法,在去文本长度时汉字没有问题,于是看了其代码
公司采用Seskin控件包来开发。却发现SeskinEdit在使用汉字是有问题。主要是由汉字时光标定位不准。鼠标选字也选不准。
于是看了其代码。发现它在计算文本长度时采用的函数TextLength有问题。
其实TCanvas提供了一个TextLength方法,在去文本长度时汉字没有问题。
所以把这里替换下来就行了。
替换后的se_controls单元中的TSeCustomEdit的代码如下
TSeCustomEdit = class(TSeCustomControl)
PRivate
FText: WideString;
FLMouseSelecting: boolean;
FCaretPosition: integer;
FSelStart: integer;
FSelLength: integer;
FFirstVisibleChar: integer;
FPopupMenu: TSeCustomPopupMenu;
FAutoSelect: boolean;
FCharCase: TEditCharCase;
FHideSelection: Boolean;
FMaxLength: Integer;
FReadOnly: Boolean;
FOnChange: TNotifyEvent;
FPassWordChar: WideChar;
FPasswordKind: TPasswordKind;
FTextAlignment: TAlignment;
FActionStack: TEditActionStack;
FPopupMenuDropShadow: boolean;
FPopupMenuShowAnimationTime: integer;
FPopupMenuBlendValue: integer;
FPopupMenuShadowWidth: integer;
FPopupMenuShowAnimation: TSeAnimationRec;
FPopupMenuBlend: boolean;
FContextMenuOptions: TSePopupMenuOptions;
procedure UpdateFirstVisibleChar;
procedure UpdateCaretePosition;
procedure UpdateCarete;
procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMCopy(var Message: TMessage); message WM_COPY;
procedure WMPaste(var Message: TMessage); message WM_PASTE;
procedure WMCut(var Message: TMessage); message WM_CUT;
procedure WMUnDo(var Message: TMessage); message WM_UNDO;
procedure WMContexMenu(var Message: TMessage); message WM_CONTEXTMENU;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message
WM_LBUTTONDBLCLK;
{ unicode }
procedure WMImeStartComposition(var Message: TMessage); message
WM_IME_STARTCOMPOSITION;
procedure WMImeComposition(var Msg: TMessage); message WM_IME_COMPOSITION;
{ VCL messages }
procedure CMEnabledChanged(var Msg: TMessage); message CM_ENABLEDCHANGED;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure CMTextChanged(var Msg: TMessage); message CM_TEXTCHANGED;
function GetSelText: WideString;
function GetVisibleSelText: WideString;
function GetNextWordBeging(StartPosition: integer): integer;
function GetPrivWordBeging(StartPosition: integer): integer;
function GetSelStart: integer;
function GetSelLength: integer;
function GetText: WideString;
procedure SetText(const Value: WideString);
procedure SetFont(Value: TFont);
procedure SetCaretPosition(const Value: integer);
procedure SetSelLength(const Value: integer);
procedure SetSelStart(const Value: integer);
procedure SetAutoSelect(const Value: boolean);
procedure SetCharCase(const Value: TEditCharCase);
procedure SetHideSelection(const Value: Boolean);
procedure SetMaxLength(const Value: Integer);
procedure SetPasswordChar(const Value: WideChar);
procedure SetCursor(const Value: TCursor);
procedure SetTextAlignment(const Value: TAlignment);
procedure SetPasswordKind(const Value: TPasswordKind);
procedure SetPopupMenuBlendValue(const Value: integer);
procedure SetPopupMenuDropShadow(const Value: boolean);
procedure SetPopupMenuShadowWidth(const Value: integer);
procedure SetPopupMenuShowAnimation(const Value: TSeAnimationRec);
procedure SetPopupMenuShowAnimationTime(const Value: integer);
procedure SetPopupMenuBlend(const Value: boolean);
procedure SetContextMenuOptions(const Value: TSePopupMenuOptions);
protected
function GetEditRect: TRect; virtual;
function GetPasswordCharWidth: integer; virtual;
function GetCharX(A: integer): integer;
function GetCoordinatePosition(x: integer): integer;
function GetSelRect: TRect; virtual;
function GetAlignmentFlags: integer;
procedure PaintBuffer; override;
procedure PaintText; virtual;
procedure PaintBackground(Rect: TRect; Canvas: TCanvas); virtual;
procedure PaintSelectedText; virtual;
procedure DrawPasswordChar(SymbolRect: TRect; Selected: boolean); virtual;
function ValidText(NewText: WideString): boolean; virtual;
function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;
procedure BorderChanged; override;
procedure HasFocus; override;
procedure KillFocus; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; x, y:
integer);
override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; x, y: integer);
override;
procedure MouseMove(Shift: TShiftState; x, y: integer); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
procedure SelectWord;
procedure Change; dynamic;
function CreatePopupMenu(AOwner: TComponent): TSeCustomPopupMenu; virtual;
function CreatePopupMenuItem(AOwner: TComponent): TSeCustomItem; virtual;
procedure BuildPopupMenu;
procedure UpdatePopupMenuItems; virtual;
procedure DoUndo(Sender: TObject);
procedure DoCut(Sender: TObject);
procedure DoCopy(Sender: TObject);
procedure DoPaste(Sender: TObject);
procedure DoDelete(Sender: TObject);
procedure DoSelectAll(Sender: TObject);
property CaretPosition: integer read FCaretPosition write SetCaretPosition;
property PopupMenu: TSeCustomPopupMenu read FPopupMenu;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Loaded; override;
procedure ShowCaret; virtual;
procedure HideCaret; virtual;
procedure CopyToClipboard;
procedure PasteFromClipboard;
procedure CutToClipboard;
procedure ClearSelection;
procedure SelectAll;
procedure Clear;
procedure UnDo;
procedure InsertChar(Ch: WideChar);
procedure InsertText(AText: WideString);
procedure InsertAfter(Position: integer; S: WideString; Selected: boolean);
procedure DeleteFrom(Position, Length: integer; MoveCaret: boolean);
property SelStart: integer read GetSelStart write SetSelStart;
property SelLength: integer read GetSelLength write SetSelLength;
property SelText: WideString read GetSelText;
published
property Anchors;
property AutoSelect: boolean read FAutoSelect write SetAutoSelect default
true;
property AutoSize;
property Blending;
property BevelSides;
property BevelInner;
property BevelOuter;
property BevelKind;
property BevelWidth;
property BorderWidth;
property CharCase: TEditCharCase read FCharCase write SetCharCase default
ecNormal;
property Constraints;
property Color;
property Cursor write SetCursor;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property ImeMode;
property ImeName;
property Font write SetFont;
property HideSelection: Boolean read FHideSelection write SetHideSelection
default True;
property MaxLength: Integer read FMaxLength write SetMaxLength default 0;
property Performance;
property ParentFont;
property ParentShowHint;
property PasswordKind: TPasswordKind read FPasswordKind write
SetPasswordKind;
property PasswordWideChar: WideChar read FPasswordChar write SetPasswordChar
default WideChar(#0);
property ContextMenuOptions: TSePopupMenuOptions read FContextMenuOptions
write SetContextMenuOptions;
property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
property ShowHint;
property TabOrder;
property TabStop default true;
property Text: WideString read GetText write SetText;
property TextAlignment: TAlignment read FTextAlignment write SetTextAlignment
default taLeftJustify;
property Visible;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
end;
{ TSeCustomEdit ===============================================================}
constructor TSeCustomEdit.Create(AOwner: TComponent);
begin
inherited;
FActionStack := TEditActionStack.Create(Self);
FContextMenuOptions := TSePopupMenuOptions.Create;
Performance := kspDoubleBuffer;
BevelKind := kbkSingle;
BevelWidth := 1;
BorderWidth := 3;
TabStop := true;
Width := 121;
Height := 21;
Color := clWindow;
FTextAlignment := taLeftJustify;
FAutoSelect := true;
AutoSize := true;
FCharCase := ecNormal;
FHideSelection := true;
FMaxLength := 0;
FReadOnly := false;
FPasswordChar := WideChar(#0);
FLMouseSelecting := false;
FCaretPosition := 0;
FSelStart := 0;
FSelLength := 0;
FFirstVisibleChar := 1;
ControlStyle := ControlStyle + [csCaptureMouse];
FPopupMenuBlend := false;
FPopupMenuBlendValue := 150;
FPopupMenuDropShadow := false;
FPopupMenuShadowWidth := 4;
FPopupMenuShowAnimationTime := 300;
Cursor := Cursor;
end;
destructor TSeCustomEdit.Destroy;
begin
if FPopupMenu <> nil then
FPopupMenu.Free;
FContextMenuOptions.Free;
FActionStack.Free;
inherited;
end;
procedure TSeCustomEdit.Loaded;
begin
inherited;
AdjustSize;
end;
procedure TSeCustomEdit.HasFocus;
begin
inherited;
UpdateCarete;
CaretPosition := 0;
if AutoSelect then
SelectAll;
end;
procedure TSeCustomEdit.KillFocus;
begin
inherited;
DestroyCaret;
Invalidate;
end;
function TSeCustomEdit.GetCharX(a: integer): integer;
var
WholeTextWidth : integer;
EditRectWidth : integer;
begin
Result := GetEditRect.Left;
if PasswordKind <> pkNone then
WholeTextWidth := Length(Text) * GetPasswordCharWidth
else
{WholeTextWidth := TextWidth(Canvas, Copy(Text, 1, Length(Text)),
DT_NOPREFIX); }
WholeTextWidth := Canvas.TextWidth(Copy(Text, 1, Length(Text)));
if a > 0 then
begin
Canvas.Font.Assign(ControlFont);
if PasswordKind <> pkNone then
begin
if a <= Length(Text) then
Result := Result + (a - FFirstVisibleChar + 1) * GetPasswordCharWidth
else
Result := Result + (Length(Text) - FFirstVisibleChar + 1) *
GetPasswordCharWidth;
end
else
begin
if a <= Length(Text) then
Result := Result + Canvas.TextWidth(Copy(Text, FFirstVisibleChar, a -
FFirstVisibleChar + 1))
//Result := Result + TextWidth(Canvas, Copy(Text, FFirstVisibleChar, a - FFirstVisibleChar + 1), DT_NOPREFIX)
else
Result := Result + Canvas.TextWidth(Copy(Text, FFirstVisibleChar,
Length(Text) - FFirstVisibleChar + 1));
//Result := Result + TextWidth(Canvas, Copy(Text, FFirstVisibleChar, Length(Text) - FFirstVisibleChar + 1), DT_NOPREFIX);
end;
end;
EditRectWidth := GetEditRect.Right - GetEditRect.Left;
if WholeTextWidth < EditRectWidth then
case TextAlignment of
taRightJustify: Result := Result + (EditRectWidth - WholeTextWidth);
taCenter: Result := Result + ((EditRectWidth - WholeTextWidth) div 2);
end;
end;
function TSeCustomEdit.GetCoordinatePosition(x: integer): integer;
var
CurX : double;
TmpX,
WholeTextWidth,
EditRectWidth : integer;
begin
Result := FFirstVisibleChar - 1;
if Length(Text) = 0 then
Exit;
if PasswordKind <> pkNone then
WholeTextWidth := Length(Text) * GetPasswordCharWidth
else
WholeTextWidth := Canvas.TextWidth(Copy(Text, 1, Length(Text)));
//WholeTextWidth :=TextWidth(Canvas, Copy(Text, 1, Length(Text)), DT_NOPREFIX);
EditRectWidth := GetEditRect.Right - GetEditRect.Left;
TmpX := x;
if WholeTextWidth < EditRectWidth then
case TextAlignment of
taRightJustify: TmpX := x - (EditRectWidth - WholeTextWidth);
taCenter: TmpX := x - ((EditRectWidth - WholeTextWidth) div 2);
end;
if PasswordKind <> pkNone then
begin
Result := Result + (TmpX - GetEditRect.Left) div GetPasswordCharWidth;
if Result < 0 then
Result := 0
else
if Result > Length(Text) then
Result := Length(Text);
end
else
begin
Canvas.Font.Assign(ControlFont);
{CurX := GetEditRect.Left + TextWidth(Canvas, Text[FFirstVisibleChar],
DT_NOPREFIX) / 2; }
CurX := GetEditRect.Left + Canvas.TextWidth(Text[FFirstVisibleChar]) / 2;
while (CurX < TmpX) and (Result + 1 <= Length(Text)) and (CurX <
GetEditRect.Right) do
begin
//CurX := CurX + TextWidth(Canvas, Text[Result + 1], DT_NOPREFIX) / 2;
CurX := CurX + Canvas.TextWidth(Text[Result + 1]) / 2;
if Result + 1 + 1 <= Length(Text) then
//CurX := CurX + TextWidth(Canvas, Text[Result + 1 + 1], DT_NOPREFIX) / 2;
CurX := CurX + Canvas.TextWidth(Text[Result + 1 + 1]) / 2;
Result := Result + 1;
end;
end;
end;
function TSeCustomEdit.GetEditRect: TRect;
begin
with Result do
begin
Result := GetBorderRect;
Canvas.Font.Assign(ControlFont);
Result.Bottom := Result.Top + Canvas.TextHeight('Pq');
end;
end;
function TSeCustomEdit.GetAlignmentFlags: integer;
begin
case FTextAlignment of
taCenter: Result := DT_CENTER;
taRightJustify: Result := DT_RIGHT;
else
Result := DT_LEFT;
end;
end;
procedure TSeCustomEdit.KeyDown(var Key: word; Shift: TShiftState);
var
TmpS : WideString;
OldCaretPosition : integer;
begin
inherited KeyDown(Key, Shift);
OldCaretPosition := CaretPosition;
case Key of
VK_END: CaretPosition := Length(Text);
VK_HOME: CaretPosition := 0;
VK_LEFT:
if ssCtrl in Shift then
CaretPosition := GetPrivWordBeging(CaretPosition)
else
CaretPosition := CaretPosition - 1;
VK_RIGHT:
if ssCtrl in Shift then
CaretPosition := GetNextWordBeging(CaretPosition)
else
CaretPosition := CaretPosition + 1;
VK_DELETE, 8: {Delete or BackSpace key was pressed}
if not ReadOnly then
begin
if SelLength <> 0 then
begin
if Shift = [ssShift] then
CutToClipboard
else
ClearSelection;
end
else
begin
TmpS := Text;
if TmpS <> '' then
if Key = VK_DELETE then
begin
FActionStack.FragmentDeleted(CaretPosition + 1, TmpS[CaretPosition
+ 1]);
Delete(TmpS, CaretPosition + 1, 1);
end
else
begin {BackSpace key was pressed}
if CaretPosition > 0 then
FActionStack.FragmentDeleted(CaretPosition,
TmpS[CaretPosition]);
Delete(TmpS, CaretPosition, 1);
CaretPosition := CaretPosition - 1;
end;
Text := TmpS;
end;
end;
VK_INSERT:
if Shift = [ssCtrl] then
CopyToClipboard
else
if Shift = [ssShift] then
PasteFromClipboard;
Ord('c'),
Ord('C'):
if Shift = [ssCtrl] then
CopyToClipboard;
Ord('v'),
Ord('V'):
if Shift = [ssCtrl] then
PasteFromClipboard;
Ord('x'),
Ord('X'):
if Shift = [ssCtrl] then
CutToClipboard;
Ord('z'), Ord('Z'):
if Shift = [ssCtrl] then
UnDo;
end;
if Key in [VK_END, VK_HOME, VK_LEFT, VK_RIGHT] then
begin
if ssShift in Shift then
begin
if SelLength = 0 then
FSelStart := OldCaretPosition;
FSelStart := CaretPosition;
FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
end
else
FSelLength := 0;
Invalidate;
end;
UpdateCaretePosition;
end;
procedure TSeCustomEdit.KeyPress(var Key: Char);
begin
inherited KeyPress(Key);
if (Ord(Key) >= 32) and not ReadOnly then
InsertChar(charToWideChar(Key));
end;
procedure TSeCustomEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
x, y: integer);
begin
inherited;
if Button = mbLeft then
FLMouseSelecting := true;
SetFocus;
if Button = mbLeft then
begin
CaretPosition := GetCoordinatePosition(x);
SelLength := 0;
end;
end;
procedure TSeCustomEdit.PaintBuffer;
var
R : TRect;
begin
R := GetEditRect;
R.Bottom := FHeight - R.Top;
PaintBackground(R, Canvas);
if (Self is TSeCustomComboBox) and (TSeCustomComboBox(Self).ComboStyle =
kcsDropDownList) then
Exit;
if Focused or not HideSelection then
FillRect(Canvas, GetSelRect, clHighlight);
PaintText;
if Focused or not HideSelection then
PaintSelectedText;
end;
procedure TSeCustomEdit.PaintBackground(Rect: TRect; Canvas: TCanvas);
begin
FillRect(Canvas, Rect, Color);
end;
procedure TSeCustomEdit.PaintText;
var
TmpRect : TRect;
CurChar : integer;
LPWCharWidth : integer;
begin
TmpRect := GetEditRect;
if PasswordKind <> pkNone then
begin
LPWCharWidth := GetPasswordCharWidth;
for CurChar := 0 to Length(Text) - FFirstVisibleChar + 1 - 1 do
DrawPasswordChar(Rect(CurChar * LPWCharWidth + GetCharX(0),
TmpRect.Top,
(CurChar + 1) * LPWCharWidth + GetCharX(0),
TmpRect.Bottom), false);
end
else
begin
Canvas.Font.Assign(ControlFont);
DrawText(Canvas, Copy(Text, FFirstVisibleChar, Length(Text) -
FFirstVisibleChar + 1),
TmpRect, GetAlignmentFlags or DT_NOPREFIX);
end;
end;
procedure TSeCustomEdit.UpdateFirstVisibleChar;
var
LEditRect : TRect;
begin
if FFirstVisibleChar >= (FCaretPosition + 1) then
begin
FFirstVisibleChar := FCaretPosition;
if FFirstVisibleChar < 1 then
FFirstVisibleChar := 1;
end
else
begin
LEditRect := GetEditRect;
if PasswordKind <> pkNone then
while ((FCaretPosition - FFirstVisibleChar + 1) * GetPasswordCharWidth >
LEditRect.Right - LEditRect.Left)
and (FFirstVisibleChar < Length(Text)) do
Inc(FFirstVisibleChar)
else
begin
Canvas.Font.Assign(ControlFont);
{while (TextWidth(Canvas, Copy(Text, FFirstVisibleChar, FCaretPosition -
FFirstVisibleChar + 1), DT_NOPREFIX) > LEditRect.Right - LEditRect.Left)
and (FFirstVisibleChar < Length(Text)) do
Inc(FFirstVisibleChar); }
while (Canvas.TextWidth(Copy(Text, FFirstVisibleChar, FCaretPosition -
FFirstVisibleChar + 1)) > LEditRect.Right - LEditRect.Left)
and (FFirstVisibleChar < Length(Text)) do
Inc(FFirstVisibleChar);
end;
end;
Invalidate;
end;
procedure TSeCustomEdit.MouseMove(Shift: TShiftState; x, y: integer);
var
OldCaretPosition : integer;
TmpNewPosition : integer;
begin
inherited;
if FLMouseSelecting then
begin
TmpNewPosition := GetCoordinatePosition(x);
OldCaretPosition := CaretPosition;
if (x > GetEditRect.Right) then
CaretPosition := TmpNewPosition + 1
else
CaretPosition := TmpNewPosition;
if SelLength = 0 then
FSelStart := OldCaretPosition;
FSelStart := CaretPosition;
FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
end;
end;
procedure TSeCustomEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
x, y: integer);
begin
inherited;
FLMouseSelecting := false;
end;
procedure TSeCustomEdit.CopyToClipboard;
var
Data : THandle;
DataPtr : Pointer;
Size : Cardinal;
S : WideString;
begin
if PasswordKind = pkNone then
if Length(SelText) > 0 then
begin
S := SelText;
if not IsWinNT then
begin
Clipboard.AsText := S;
end
else
begin
Size := Length(S);
Data := GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, 2 * Size + 2);
try
DataPtr := GlobalLock(Data);
try
Move(PWideChar(S)^, DataPtr^, 2 * Size + 2);
Clipboard.SetAsHandle(CF_UNICODETEXT, Data);
finally
GlobalUnlock(Data);
end;
except
GlobalFree(Data);
raise;
end;
end;
end;
end;
procedure TSeCustomEdit.PasteFromClipboard;
var
Data : THandle;
Insertion : WideString;
begin
if ReadOnly then
Exit;
if Clipboard.HasFormat(CF_UNICODETEXT) then
begin
Data := Clipboard.GetAsHandle(CF_UNICODETEXT);
try
if Data <> 0 then
Insertion := PWideChar(GlobalLock(Data));
finally
if Data <> 0 then
GlobalUnlock(Data);
end;
end
else
Insertion := Clipboard.AsText;
InsertText(Insertion);
end;
procedure TSeCustomEdit.PaintSelectedText;
var
TmpRect : TRect;
CurChar : integer;
LPWCharWidth : integer;
begin
TmpRect := GetSelRect;
if PasswordKind <> pkNone then
begin
LPWCharWidth := GetPasswordCharWidth;
for CurChar := 0 to Length(GetVisibleSelText) - 1 do
DrawPasswordChar(Rect(CurChar * LPWCharWidth + TmpRect.Left,
TmpRect.Top,
(CurChar + 1) * LPWCharWidth + TmpRect.Left,
TmpRect.Bottom),
true);
end
else
begin
Canvas.Font.Assign(ControlFont);
Canvas.Font.Color := clHighlightText;
DrawText(Canvas, GetVisibleSelText, TmpRect, GetAlignmentFlags or
DT_NOPREFIX)
end;
end;
function TSeCustomEdit.GetVisibleSelText: WideString;
begin
if SelStart + 1 >= FFirstVisibleChar then
Result := SelText
else
Result := Copy(SelText, FFirstVisibleChar - SelStart, Length(SelText) -
(FFirstVisibleChar - SelStart) + 1);
end;
procedure TSeCustomEdit.BuildPopupMenu;
var
TmpItem : TSeCustomItem;
begin
FPopupMenu := CreatePopupMenu(Self);
if FPopupMenu = nil then
Exit;
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditUndo;
OnClick := DoUndo;
end;
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
TmpItem.Caption := '-';
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditCut;
OnClick := DoCut;
end;
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditCopy;
OnClick := DoCopy;
end;
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditPaste;
OnClick := DoPaste;
end;
于是看了其代码。发现它在计算文本长度时采用的函数TextLength有问题。
其实TCanvas提供了一个TextLength方法,在去文本长度时汉字没有问题。
所以把这里替换下来就行了。
替换后的se_controls单元中的TSeCustomEdit的代码如下
TSeCustomEdit = class(TSeCustomControl)
PRivate
FText: WideString;
FLMouseSelecting: boolean;
FCaretPosition: integer;
FSelStart: integer;
FSelLength: integer;
FFirstVisibleChar: integer;
FPopupMenu: TSeCustomPopupMenu;
FAutoSelect: boolean;
FCharCase: TEditCharCase;
FHideSelection: Boolean;
FMaxLength: Integer;
FReadOnly: Boolean;
FOnChange: TNotifyEvent;
FPassWordChar: WideChar;
FPasswordKind: TPasswordKind;
FTextAlignment: TAlignment;
FActionStack: TEditActionStack;
FPopupMenuDropShadow: boolean;
FPopupMenuShowAnimationTime: integer;
FPopupMenuBlendValue: integer;
FPopupMenuShadowWidth: integer;
FPopupMenuShowAnimation: TSeAnimationRec;
FPopupMenuBlend: boolean;
FContextMenuOptions: TSePopupMenuOptions;
procedure UpdateFirstVisibleChar;
procedure UpdateCaretePosition;
procedure UpdateCarete;
procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMCopy(var Message: TMessage); message WM_COPY;
procedure WMPaste(var Message: TMessage); message WM_PASTE;
procedure WMCut(var Message: TMessage); message WM_CUT;
procedure WMUnDo(var Message: TMessage); message WM_UNDO;
procedure WMContexMenu(var Message: TMessage); message WM_CONTEXTMENU;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message
WM_LBUTTONDBLCLK;
{ unicode }
procedure WMImeStartComposition(var Message: TMessage); message
WM_IME_STARTCOMPOSITION;
procedure WMImeComposition(var Msg: TMessage); message WM_IME_COMPOSITION;
{ VCL messages }
procedure CMEnabledChanged(var Msg: TMessage); message CM_ENABLEDCHANGED;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure CMTextChanged(var Msg: TMessage); message CM_TEXTCHANGED;
function GetSelText: WideString;
function GetVisibleSelText: WideString;
function GetNextWordBeging(StartPosition: integer): integer;
function GetPrivWordBeging(StartPosition: integer): integer;
function GetSelStart: integer;
function GetSelLength: integer;
function GetText: WideString;
procedure SetText(const Value: WideString);
procedure SetFont(Value: TFont);
procedure SetCaretPosition(const Value: integer);
procedure SetSelLength(const Value: integer);
procedure SetSelStart(const Value: integer);
procedure SetAutoSelect(const Value: boolean);
procedure SetCharCase(const Value: TEditCharCase);
procedure SetHideSelection(const Value: Boolean);
procedure SetMaxLength(const Value: Integer);
procedure SetPasswordChar(const Value: WideChar);
procedure SetCursor(const Value: TCursor);
procedure SetTextAlignment(const Value: TAlignment);
procedure SetPasswordKind(const Value: TPasswordKind);
procedure SetPopupMenuBlendValue(const Value: integer);
procedure SetPopupMenuDropShadow(const Value: boolean);
procedure SetPopupMenuShadowWidth(const Value: integer);
procedure SetPopupMenuShowAnimation(const Value: TSeAnimationRec);
procedure SetPopupMenuShowAnimationTime(const Value: integer);
procedure SetPopupMenuBlend(const Value: boolean);
procedure SetContextMenuOptions(const Value: TSePopupMenuOptions);
protected
function GetEditRect: TRect; virtual;
function GetPasswordCharWidth: integer; virtual;
function GetCharX(A: integer): integer;
function GetCoordinatePosition(x: integer): integer;
function GetSelRect: TRect; virtual;
function GetAlignmentFlags: integer;
procedure PaintBuffer; override;
procedure PaintText; virtual;
procedure PaintBackground(Rect: TRect; Canvas: TCanvas); virtual;
procedure PaintSelectedText; virtual;
procedure DrawPasswordChar(SymbolRect: TRect; Selected: boolean); virtual;
function ValidText(NewText: WideString): boolean; virtual;
function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;
procedure BorderChanged; override;
procedure HasFocus; override;
procedure KillFocus; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; x, y:
integer);
override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; x, y: integer);
override;
procedure MouseMove(Shift: TShiftState; x, y: integer); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
procedure SelectWord;
procedure Change; dynamic;
function CreatePopupMenu(AOwner: TComponent): TSeCustomPopupMenu; virtual;
function CreatePopupMenuItem(AOwner: TComponent): TSeCustomItem; virtual;
procedure BuildPopupMenu;
procedure UpdatePopupMenuItems; virtual;
procedure DoUndo(Sender: TObject);
procedure DoCut(Sender: TObject);
procedure DoCopy(Sender: TObject);
procedure DoPaste(Sender: TObject);
procedure DoDelete(Sender: TObject);
procedure DoSelectAll(Sender: TObject);
property CaretPosition: integer read FCaretPosition write SetCaretPosition;
property PopupMenu: TSeCustomPopupMenu read FPopupMenu;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Loaded; override;
procedure ShowCaret; virtual;
procedure HideCaret; virtual;
procedure CopyToClipboard;
procedure PasteFromClipboard;
procedure CutToClipboard;
procedure ClearSelection;
procedure SelectAll;
procedure Clear;
procedure UnDo;
procedure InsertChar(Ch: WideChar);
procedure InsertText(AText: WideString);
procedure InsertAfter(Position: integer; S: WideString; Selected: boolean);
procedure DeleteFrom(Position, Length: integer; MoveCaret: boolean);
property SelStart: integer read GetSelStart write SetSelStart;
property SelLength: integer read GetSelLength write SetSelLength;
property SelText: WideString read GetSelText;
published
property Anchors;
property AutoSelect: boolean read FAutoSelect write SetAutoSelect default
true;
property AutoSize;
property Blending;
property BevelSides;
property BevelInner;
property BevelOuter;
property BevelKind;
property BevelWidth;
property BorderWidth;
property CharCase: TEditCharCase read FCharCase write SetCharCase default
ecNormal;
property Constraints;
property Color;
property Cursor write SetCursor;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property ImeMode;
property ImeName;
property Font write SetFont;
property HideSelection: Boolean read FHideSelection write SetHideSelection
default True;
property MaxLength: Integer read FMaxLength write SetMaxLength default 0;
property Performance;
property ParentFont;
property ParentShowHint;
property PasswordKind: TPasswordKind read FPasswordKind write
SetPasswordKind;
property PasswordWideChar: WideChar read FPasswordChar write SetPasswordChar
default WideChar(#0);
property ContextMenuOptions: TSePopupMenuOptions read FContextMenuOptions
write SetContextMenuOptions;
property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
property ShowHint;
property TabOrder;
property TabStop default true;
property Text: WideString read GetText write SetText;
property TextAlignment: TAlignment read FTextAlignment write SetTextAlignment
default taLeftJustify;
property Visible;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
end;
{ TSeCustomEdit ===============================================================}
constructor TSeCustomEdit.Create(AOwner: TComponent);
begin
inherited;
FActionStack := TEditActionStack.Create(Self);
FContextMenuOptions := TSePopupMenuOptions.Create;
Performance := kspDoubleBuffer;
BevelKind := kbkSingle;
BevelWidth := 1;
BorderWidth := 3;
TabStop := true;
Width := 121;
Height := 21;
Color := clWindow;
FTextAlignment := taLeftJustify;
FAutoSelect := true;
AutoSize := true;
FCharCase := ecNormal;
FHideSelection := true;
FMaxLength := 0;
FReadOnly := false;
FPasswordChar := WideChar(#0);
FLMouseSelecting := false;
FCaretPosition := 0;
FSelStart := 0;
FSelLength := 0;
FFirstVisibleChar := 1;
ControlStyle := ControlStyle + [csCaptureMouse];
FPopupMenuBlend := false;
FPopupMenuBlendValue := 150;
FPopupMenuDropShadow := false;
FPopupMenuShadowWidth := 4;
FPopupMenuShowAnimationTime := 300;
Cursor := Cursor;
end;
destructor TSeCustomEdit.Destroy;
begin
if FPopupMenu <> nil then
FPopupMenu.Free;
FContextMenuOptions.Free;
FActionStack.Free;
inherited;
end;
procedure TSeCustomEdit.Loaded;
begin
inherited;
AdjustSize;
end;
procedure TSeCustomEdit.HasFocus;
begin
inherited;
UpdateCarete;
CaretPosition := 0;
if AutoSelect then
SelectAll;
end;
procedure TSeCustomEdit.KillFocus;
begin
inherited;
DestroyCaret;
Invalidate;
end;
function TSeCustomEdit.GetCharX(a: integer): integer;
var
WholeTextWidth : integer;
EditRectWidth : integer;
begin
Result := GetEditRect.Left;
if PasswordKind <> pkNone then
WholeTextWidth := Length(Text) * GetPasswordCharWidth
else
{WholeTextWidth := TextWidth(Canvas, Copy(Text, 1, Length(Text)),
DT_NOPREFIX); }
WholeTextWidth := Canvas.TextWidth(Copy(Text, 1, Length(Text)));
if a > 0 then
begin
Canvas.Font.Assign(ControlFont);
if PasswordKind <> pkNone then
begin
if a <= Length(Text) then
Result := Result + (a - FFirstVisibleChar + 1) * GetPasswordCharWidth
else
Result := Result + (Length(Text) - FFirstVisibleChar + 1) *
GetPasswordCharWidth;
end
else
begin
if a <= Length(Text) then
Result := Result + Canvas.TextWidth(Copy(Text, FFirstVisibleChar, a -
FFirstVisibleChar + 1))
//Result := Result + TextWidth(Canvas, Copy(Text, FFirstVisibleChar, a - FFirstVisibleChar + 1), DT_NOPREFIX)
else
Result := Result + Canvas.TextWidth(Copy(Text, FFirstVisibleChar,
Length(Text) - FFirstVisibleChar + 1));
//Result := Result + TextWidth(Canvas, Copy(Text, FFirstVisibleChar, Length(Text) - FFirstVisibleChar + 1), DT_NOPREFIX);
end;
end;
EditRectWidth := GetEditRect.Right - GetEditRect.Left;
if WholeTextWidth < EditRectWidth then
case TextAlignment of
taRightJustify: Result := Result + (EditRectWidth - WholeTextWidth);
taCenter: Result := Result + ((EditRectWidth - WholeTextWidth) div 2);
end;
end;
function TSeCustomEdit.GetCoordinatePosition(x: integer): integer;
var
CurX : double;
TmpX,
WholeTextWidth,
EditRectWidth : integer;
begin
Result := FFirstVisibleChar - 1;
if Length(Text) = 0 then
Exit;
if PasswordKind <> pkNone then
WholeTextWidth := Length(Text) * GetPasswordCharWidth
else
WholeTextWidth := Canvas.TextWidth(Copy(Text, 1, Length(Text)));
//WholeTextWidth :=TextWidth(Canvas, Copy(Text, 1, Length(Text)), DT_NOPREFIX);
EditRectWidth := GetEditRect.Right - GetEditRect.Left;
TmpX := x;
if WholeTextWidth < EditRectWidth then
case TextAlignment of
taRightJustify: TmpX := x - (EditRectWidth - WholeTextWidth);
taCenter: TmpX := x - ((EditRectWidth - WholeTextWidth) div 2);
end;
if PasswordKind <> pkNone then
begin
Result := Result + (TmpX - GetEditRect.Left) div GetPasswordCharWidth;
if Result < 0 then
Result := 0
else
if Result > Length(Text) then
Result := Length(Text);
end
else
begin
Canvas.Font.Assign(ControlFont);
{CurX := GetEditRect.Left + TextWidth(Canvas, Text[FFirstVisibleChar],
DT_NOPREFIX) / 2; }
CurX := GetEditRect.Left + Canvas.TextWidth(Text[FFirstVisibleChar]) / 2;
while (CurX < TmpX) and (Result + 1 <= Length(Text)) and (CurX <
GetEditRect.Right) do
begin
//CurX := CurX + TextWidth(Canvas, Text[Result + 1], DT_NOPREFIX) / 2;
CurX := CurX + Canvas.TextWidth(Text[Result + 1]) / 2;
if Result + 1 + 1 <= Length(Text) then
//CurX := CurX + TextWidth(Canvas, Text[Result + 1 + 1], DT_NOPREFIX) / 2;
CurX := CurX + Canvas.TextWidth(Text[Result + 1 + 1]) / 2;
Result := Result + 1;
end;
end;
end;
function TSeCustomEdit.GetEditRect: TRect;
begin
with Result do
begin
Result := GetBorderRect;
Canvas.Font.Assign(ControlFont);
Result.Bottom := Result.Top + Canvas.TextHeight('Pq');
end;
end;
function TSeCustomEdit.GetAlignmentFlags: integer;
begin
case FTextAlignment of
taCenter: Result := DT_CENTER;
taRightJustify: Result := DT_RIGHT;
else
Result := DT_LEFT;
end;
end;
procedure TSeCustomEdit.KeyDown(var Key: word; Shift: TShiftState);
var
TmpS : WideString;
OldCaretPosition : integer;
begin
inherited KeyDown(Key, Shift);
OldCaretPosition := CaretPosition;
case Key of
VK_END: CaretPosition := Length(Text);
VK_HOME: CaretPosition := 0;
VK_LEFT:
if ssCtrl in Shift then
CaretPosition := GetPrivWordBeging(CaretPosition)
else
CaretPosition := CaretPosition - 1;
VK_RIGHT:
if ssCtrl in Shift then
CaretPosition := GetNextWordBeging(CaretPosition)
else
CaretPosition := CaretPosition + 1;
VK_DELETE, 8: {Delete or BackSpace key was pressed}
if not ReadOnly then
begin
if SelLength <> 0 then
begin
if Shift = [ssShift] then
CutToClipboard
else
ClearSelection;
end
else
begin
TmpS := Text;
if TmpS <> '' then
if Key = VK_DELETE then
begin
FActionStack.FragmentDeleted(CaretPosition + 1, TmpS[CaretPosition
+ 1]);
Delete(TmpS, CaretPosition + 1, 1);
end
else
begin {BackSpace key was pressed}
if CaretPosition > 0 then
FActionStack.FragmentDeleted(CaretPosition,
TmpS[CaretPosition]);
Delete(TmpS, CaretPosition, 1);
CaretPosition := CaretPosition - 1;
end;
Text := TmpS;
end;
end;
VK_INSERT:
if Shift = [ssCtrl] then
CopyToClipboard
else
if Shift = [ssShift] then
PasteFromClipboard;
Ord('c'),
Ord('C'):
if Shift = [ssCtrl] then
CopyToClipboard;
Ord('v'),
Ord('V'):
if Shift = [ssCtrl] then
PasteFromClipboard;
Ord('x'),
Ord('X'):
if Shift = [ssCtrl] then
CutToClipboard;
Ord('z'), Ord('Z'):
if Shift = [ssCtrl] then
UnDo;
end;
if Key in [VK_END, VK_HOME, VK_LEFT, VK_RIGHT] then
begin
if ssShift in Shift then
begin
if SelLength = 0 then
FSelStart := OldCaretPosition;
FSelStart := CaretPosition;
FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
end
else
FSelLength := 0;
Invalidate;
end;
UpdateCaretePosition;
end;
procedure TSeCustomEdit.KeyPress(var Key: Char);
begin
inherited KeyPress(Key);
if (Ord(Key) >= 32) and not ReadOnly then
InsertChar(charToWideChar(Key));
end;
procedure TSeCustomEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
x, y: integer);
begin
inherited;
if Button = mbLeft then
FLMouseSelecting := true;
SetFocus;
if Button = mbLeft then
begin
CaretPosition := GetCoordinatePosition(x);
SelLength := 0;
end;
end;
procedure TSeCustomEdit.PaintBuffer;
var
R : TRect;
begin
R := GetEditRect;
R.Bottom := FHeight - R.Top;
PaintBackground(R, Canvas);
if (Self is TSeCustomComboBox) and (TSeCustomComboBox(Self).ComboStyle =
kcsDropDownList) then
Exit;
if Focused or not HideSelection then
FillRect(Canvas, GetSelRect, clHighlight);
PaintText;
if Focused or not HideSelection then
PaintSelectedText;
end;
procedure TSeCustomEdit.PaintBackground(Rect: TRect; Canvas: TCanvas);
begin
FillRect(Canvas, Rect, Color);
end;
procedure TSeCustomEdit.PaintText;
var
TmpRect : TRect;
CurChar : integer;
LPWCharWidth : integer;
begin
TmpRect := GetEditRect;
if PasswordKind <> pkNone then
begin
LPWCharWidth := GetPasswordCharWidth;
for CurChar := 0 to Length(Text) - FFirstVisibleChar + 1 - 1 do
DrawPasswordChar(Rect(CurChar * LPWCharWidth + GetCharX(0),
TmpRect.Top,
(CurChar + 1) * LPWCharWidth + GetCharX(0),
TmpRect.Bottom), false);
end
else
begin
Canvas.Font.Assign(ControlFont);
DrawText(Canvas, Copy(Text, FFirstVisibleChar, Length(Text) -
FFirstVisibleChar + 1),
TmpRect, GetAlignmentFlags or DT_NOPREFIX);
end;
end;
procedure TSeCustomEdit.UpdateFirstVisibleChar;
var
LEditRect : TRect;
begin
if FFirstVisibleChar >= (FCaretPosition + 1) then
begin
FFirstVisibleChar := FCaretPosition;
if FFirstVisibleChar < 1 then
FFirstVisibleChar := 1;
end
else
begin
LEditRect := GetEditRect;
if PasswordKind <> pkNone then
while ((FCaretPosition - FFirstVisibleChar + 1) * GetPasswordCharWidth >
LEditRect.Right - LEditRect.Left)
and (FFirstVisibleChar < Length(Text)) do
Inc(FFirstVisibleChar)
else
begin
Canvas.Font.Assign(ControlFont);
{while (TextWidth(Canvas, Copy(Text, FFirstVisibleChar, FCaretPosition -
FFirstVisibleChar + 1), DT_NOPREFIX) > LEditRect.Right - LEditRect.Left)
and (FFirstVisibleChar < Length(Text)) do
Inc(FFirstVisibleChar); }
while (Canvas.TextWidth(Copy(Text, FFirstVisibleChar, FCaretPosition -
FFirstVisibleChar + 1)) > LEditRect.Right - LEditRect.Left)
and (FFirstVisibleChar < Length(Text)) do
Inc(FFirstVisibleChar);
end;
end;
Invalidate;
end;
procedure TSeCustomEdit.MouseMove(Shift: TShiftState; x, y: integer);
var
OldCaretPosition : integer;
TmpNewPosition : integer;
begin
inherited;
if FLMouseSelecting then
begin
TmpNewPosition := GetCoordinatePosition(x);
OldCaretPosition := CaretPosition;
if (x > GetEditRect.Right) then
CaretPosition := TmpNewPosition + 1
else
CaretPosition := TmpNewPosition;
if SelLength = 0 then
FSelStart := OldCaretPosition;
FSelStart := CaretPosition;
FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
end;
end;
procedure TSeCustomEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
x, y: integer);
begin
inherited;
FLMouseSelecting := false;
end;
procedure TSeCustomEdit.CopyToClipboard;
var
Data : THandle;
DataPtr : Pointer;
Size : Cardinal;
S : WideString;
begin
if PasswordKind = pkNone then
if Length(SelText) > 0 then
begin
S := SelText;
if not IsWinNT then
begin
Clipboard.AsText := S;
end
else
begin
Size := Length(S);
Data := GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, 2 * Size + 2);
try
DataPtr := GlobalLock(Data);
try
Move(PWideChar(S)^, DataPtr^, 2 * Size + 2);
Clipboard.SetAsHandle(CF_UNICODETEXT, Data);
finally
GlobalUnlock(Data);
end;
except
GlobalFree(Data);
raise;
end;
end;
end;
end;
procedure TSeCustomEdit.PasteFromClipboard;
var
Data : THandle;
Insertion : WideString;
begin
if ReadOnly then
Exit;
if Clipboard.HasFormat(CF_UNICODETEXT) then
begin
Data := Clipboard.GetAsHandle(CF_UNICODETEXT);
try
if Data <> 0 then
Insertion := PWideChar(GlobalLock(Data));
finally
if Data <> 0 then
GlobalUnlock(Data);
end;
end
else
Insertion := Clipboard.AsText;
InsertText(Insertion);
end;
procedure TSeCustomEdit.PaintSelectedText;
var
TmpRect : TRect;
CurChar : integer;
LPWCharWidth : integer;
begin
TmpRect := GetSelRect;
if PasswordKind <> pkNone then
begin
LPWCharWidth := GetPasswordCharWidth;
for CurChar := 0 to Length(GetVisibleSelText) - 1 do
DrawPasswordChar(Rect(CurChar * LPWCharWidth + TmpRect.Left,
TmpRect.Top,
(CurChar + 1) * LPWCharWidth + TmpRect.Left,
TmpRect.Bottom),
true);
end
else
begin
Canvas.Font.Assign(ControlFont);
Canvas.Font.Color := clHighlightText;
DrawText(Canvas, GetVisibleSelText, TmpRect, GetAlignmentFlags or
DT_NOPREFIX)
end;
end;
function TSeCustomEdit.GetVisibleSelText: WideString;
begin
if SelStart + 1 >= FFirstVisibleChar then
Result := SelText
else
Result := Copy(SelText, FFirstVisibleChar - SelStart, Length(SelText) -
(FFirstVisibleChar - SelStart) + 1);
end;
procedure TSeCustomEdit.BuildPopupMenu;
var
TmpItem : TSeCustomItem;
begin
FPopupMenu := CreatePopupMenu(Self);
if FPopupMenu = nil then
Exit;
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditUndo;
OnClick := DoUndo;
end;
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
TmpItem.Caption := '-';
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditCut;
OnClick := DoCut;
end;
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditCopy;
OnClick := DoCopy;
end;
FPopupMenu.Items.Add(TmpItem);
TmpItem := CreatePopupMenuItem(FPopupMenu);
with TmpItem do
begin
Caption := SEditPaste;
OnClick := DoPaste;
end;
更多精彩
赞助商链接