公司采用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;
|