Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi/C+的标记编辑器组件+;建设者_Delphi_Components_C++builder_Vcl - Fatal编程技术网

Delphi/C+的标记编辑器组件+;建设者

Delphi/C+的标记编辑器组件+;建设者,delphi,components,c++builder,vcl,Delphi,Components,C++builder,Vcl,我需要一个用于Delphi或C++Builder的VCL标记编辑器组件,类似于JavaScript可用的组件:例如,或StackOverflow自己的标记编辑器 有这样的产品吗?还是我需要从头开始 我需要的一些具体的东西是: 如果出现的标签超过编辑器允许的宽度,编辑器应允许滚动或变为多行。如果是多行,应该有一个选项来定义一些最大高度,但是,防止它变得太高 选择在按空格键或逗号键时是否创建标记的选项 编辑器中未聚焦的提示文本(例如“添加新标记”) 理想情况下,您应该能够使用键盘箭头在标记之间移动

我需要一个用于Delphi或C++Builder的VCL标记编辑器组件,类似于JavaScript可用的组件:例如,或StackOverflow自己的标记编辑器

有这样的产品吗?还是我需要从头开始

我需要的一些具体的东西是:

  • 如果出现的标签超过编辑器允许的宽度,编辑器应允许滚动或变为多行。如果是多行,应该有一个选项来定义一些最大高度,但是,防止它变得太高
  • 选择在按空格键或逗号键时是否创建标记的选项
  • 编辑器中未聚焦的提示文本(例如“添加新标记”)
  • 理想情况下,您应该能够使用键盘箭头在标记之间移动(突出显示它们),以便仅使用键盘删除任何标记

    • 当然,你想自己做这件事!编写GUI控件既有趣又有益

      你可以这样做

      unit TagEditor;
      
      interface
      
      uses
        Windows, Messages, SysUtils, Classes, Controls, StdCtrls, Forms, Graphics,
        Types, Menus;
      
      type
        TClickInfo = cardinal;
        GetTagIndex = word;
      
      const TAG_LOW = 0;
      const TAG_HIGH = MAXWORD - 2;
      const EDITOR = MAXWORD - 1;
      const NOWHERE = MAXWORD;
      
      const PART_BODY = $00000000;
      const PART_REMOVE_BUTTON = $00010000;
      
      function GetTagPart(ClickInfo: TClickInfo): cardinal;
      
      type
        TTagClickEvent = procedure(Sender: TObject; TagIndex: integer;
          const TagCaption: string) of object;
        TRemoveConfirmEvent = procedure(Sender: TObject; TagIndex: integer;
          const TagCaption: string; var CanRemove: boolean) of object;
        TTagEditor = class(TCustomControl)
        private
          { Private declarations }
          FTags: TStringList;
          FEdit: TEdit;
          FBgColor: TColor;
          FBorderColor: TColor;
          FTagBgColor: TColor;
          FTagBorderColor: TColor;
          FSpacing: integer;
          FTextColor: TColor;
          FLefts, FRights, FWidths,
          FTops, FBottoms: array of integer;
          FCloseBtnLefts, FCloseBtnTops: array of integer;
          FCloseBtnWidth: integer;
          FSpaceAccepts: boolean;
          FCommaAccepts: boolean;
          FSemicolonAccepts: boolean;
          FTrimInput: boolean;
          FNoLeadingSpaceInput: boolean;
          FTagClickEvent: TTagClickEvent;
          FAllowDuplicates: boolean;
          FPopupMenu: TPopupMenu;
          FMultiLine: boolean;
          FTagHeight: integer;
          FEditPos: TPoint;
          FActualTagHeight: integer;
          FShrunk: boolean;
          FEditorColor: TColor;
          FTagAdded: TNotifyEvent;
          FTagRemoved: TNotifyEvent;
          FOnChange: TNotifyEvent;
          FOnRemoveConfirm: TRemoveConfirmEvent;
          FMouseDownClickInfo: TClickInfo;
          FCaretVisible: boolean;
          FDragging: boolean;
          FAutoHeight: boolean;
          FNumRows: integer;
          procedure SetBorderColor(const Value: TColor);
          procedure SetTagBgColor(const Value: TColor);
          procedure SetTagBorderColor(const Value: TColor);
          procedure SetSpacing(const Value: integer);
          procedure TagChange(Sender: TObject);
          procedure SetTags(const Value: TStringList);
          procedure SetTextColor(const Value: TColor);
          procedure ShowEditor;
          procedure HideEditor;
          procedure EditKeyPress(Sender: TObject; var Key: Char);
          procedure mnuDeleteItemClick(Sender: TObject);
          procedure SetMultiLine(const Value: boolean);
          procedure SetTagHeight(const Value: integer);
          procedure EditExit(Sender: TObject);
          function Accept: boolean;
          procedure SetBgColor(const Value: TColor);
          function GetClickInfoAt(X, Y: integer): TClickInfo;
          function GetSeparatorIndexAt(X, Y: integer): integer;
          procedure CreateCaret;
          procedure DestroyCaret;
          function IsFirstOnRow(TagIndex: integer): boolean; inline;
          function IsLastOnRow(TagIndex: integer): boolean;
          procedure SetAutoHeight(const Value: boolean);
        protected
          { Protected declarations }
          procedure Paint; override;
          procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
            Y: Integer); override;
          procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
      
          procedure KeyPress(var Key: Char); override;
          procedure WndProc(var Message: TMessage); override;
          procedure KeyDown(var Key: Word; Shift: TShiftState); override;
          procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
            Y: Integer); override;
        public
          { Public declarations }
          constructor Create(AOwner: TComponent); override;
          destructor Destroy; override;
        published
          { Published declarations }
          property TabOrder;
          property TabStop;
          property Color;
          property Anchors;
          property Align;
          property Tag;
          property Cursor;
          property BgColor: TColor read FBgColor write SetBgColor;
          property BorderColor: TColor read FBorderColor write SetBorderColor;
          property TagBgColor: TColor read FTagBgColor write SetTagBgColor;
          property TagBorderColor: TColor read FTagBorderColor
            write SetTagBorderColor;
          property Spacing: integer read FSpacing write SetSpacing;
          property Tags: TStringList read FTags write SetTags;
          property TextColor: TColor read FTextColor write SetTextColor;
          property SpaceAccepts: boolean read FSpaceAccepts write FSpaceAccepts
            default true;
          property CommaAccepts: boolean read FCommaAccepts write FCommaAccepts
            default true;
          property SemicolonAccepts: boolean read FSemicolonAccepts
            write FSemicolonAccepts default true;
          property TrimInput: boolean read FTrimInput write FTrimInput default true;
          property NoLeadingSpaceInput: boolean read FNoLeadingSpaceInput
            write FNoLeadingSpaceInput default true;
          property AllowDuplicates: boolean read FAllowDuplicates write FAllowDuplicates
            default false;
          property MultiLine: boolean read FMultiLine write SetMultiLine default false;
          property TagHeight: integer read FTagHeight write SetTagHeight default 32;
          property EditorColor: TColor read FEditorColor write FEditorColor
            default clWindow;
          property AutoHeight: boolean read FAutoHeight write SetAutoHeight;
          property OnTagClick: TTagClickEvent read FTagClickEvent write FTagClickEvent;
          property OnTagAdded: TNotifyEvent read FTagAdded write FTagAdded;
          property OnTagRemoved: TNotifyEvent read FTagRemoved write FTagRemoved;
          property OnChange: TNotifyEvent read FOnChange write FOnChange;
          property OnRemoveConfirm: TRemoveConfirmEvent read FOnRemoveConfirm
            write FOnRemoveConfirm;
        end;
      
      procedure Register;
      
      implementation
      
      uses Math, Clipbrd;
      
      procedure Register;
      begin
        RegisterComponents('Rejbrand 2009', [TTagEditor]);
      end;
      
      function IsKeyDown(const VK: integer): boolean;
      begin
        IsKeyDown := GetKeyState(VK) and $8000 <> 0;
      end;
      
      function GetTagPart(ClickInfo: TClickInfo): cardinal;
      begin
        result := ClickInfo and $FFFF0000;
      end;
      
      { TTagEditor }
      
      constructor TTagEditor.Create(AOwner: TComponent);
      var
        mnuItem: TMenuItem;
      begin
        inherited;
        FEdit := TEdit.Create(Self);
        FEdit.Parent := Self;
        FEdit.BorderStyle := bsNone;
        FEdit.Visible := false;
        FEdit.OnKeyPress := EditKeyPress;
        FEdit.OnExit := EditExit;
      
        FTags := TStringList.Create;
        FTags.OnChange := TagChange;
      
        FBgColor := clWindow;
        FBorderColor := clWindowFrame;
        FTagBgColor := clSkyBlue;
        FTagBorderColor := clNavy;
        FSpacing := 8;
        FTextColor := clWhite;
        FSpaceAccepts := true;
        FCommaAccepts := true;
        FSemicolonAccepts := true;
        FTrimInput := true;
        FNoLeadingSpaceInput := true;
        FAllowDuplicates := false;
        FMultiLine := false;
        FTagHeight := 32;
        FShrunk := false;
        FEditorColor := clWindow;
        FCaretVisible := false;
        FDragging := false;
      
        FPopupMenu := TPopupMenu.Create(Self);
        mnuItem := TMenuItem.Create(PopupMenu);
        mnuItem.Caption := 'Delete';
        mnuItem.OnClick := mnuDeleteItemClick;
        mnuItem.Hint := 'Deletes the selected tag.';
        FPopupMenu.Items.Add(mnuItem);
      
        TabStop := true;
      end;
      
      procedure TTagEditor.EditExit(Sender: TObject);
      begin
        if FEdit.Text <> '' then
          Accept
        else
          HideEditor;
      end;
      
      procedure TTagEditor.mnuDeleteItemClick(Sender: TObject);
      begin
        if Sender is TMenuItem then
        begin
          FTags.Delete(TMenuItem(Sender).Tag);
          if Assigned(FTagRemoved) then
            FTagRemoved(Self);
        end;
      end;
      
      procedure TTagEditor.TagChange(Sender: TObject);
      begin
        Invalidate;
        if Assigned(FOnChange) then
          FOnChange(Self);
      end;
      
      procedure TTagEditor.WndProc(var Message: TMessage);
      begin
        inherited;
        case Message.Msg of
          WM_SETFOCUS:
            Invalidate;
          WM_KILLFOCUS:
            begin
              if FCaretVisible then DestroyCaret;
              FDragging := false;
              Invalidate;
            end;
          WM_COPY:
            Clipboard.AsText := FTags.DelimitedText;
          WM_CLEAR:
            FTags.Clear;
          WM_CUT:
            begin
              Clipboard.AsText := FTags.DelimitedText;
              FTags.Clear;
            end;
          WM_PASTE:
            begin
              if Clipboard.HasFormat(CF_TEXT) then
                if FTags.Count = 0 then
                  FTags.DelimitedText := Clipboard.AsText
                else
                  FTags.DelimitedText := FTags.DelimitedText + ',' + Clipboard.AsText;
            end;
        end;
      end;
      
      function TTagEditor.Accept: boolean;
      begin
        Assert(FEdit.Visible);
        result := false;
        if FTrimInput then
          FEdit.Text := Trim(FEdit.Text);
        if (FEdit.Text = '') or
          ((not AllowDuplicates) and (FTags.IndexOf(FEdit.Text) <> -1))  then
        begin
          beep;
          Exit;
        end;
        FTags.Add(FEdit.Text);
        result := true;
        HideEditor;
        if Assigned(FTagAdded) then
          FTagAdded(Self);
        Invalidate;
      end;
      
      procedure TTagEditor.EditKeyPress(Sender: TObject; var Key: Char);
      begin
      
        if (Key = chr(VK_SPACE)) and (FEdit.Text = '') and FNoLeadingSpaceInput then
        begin
          Key := #0;
          Exit;
        end;
      
        if ((Key = chr(VK_SPACE)) and FSpaceAccepts) or
          ((Key = ',') and FCommaAccepts) or
          ((Key = ';') and FSemicolonAccepts) then
          Key := chr(VK_RETURN);
      
        case ord(Key) of
          VK_RETURN:
            begin
              Accept;
              ShowEditor;
              Key := #0;
            end;
          VK_BACK:
            begin
              if (FEdit.Text = '') and (FTags.Count > 0) then
              begin
                FTags.Delete(FTags.Count - 1);
                if Assigned(FTagRemoved) then
                  FTagRemoved(Sender);
              end;
            end;
          VK_ESCAPE:
            begin
              HideEditor;
              Self.SetFocus;
              Key := #0;
            end;
        end;
      
      end;
      
      destructor TTagEditor.Destroy;
      begin
        FPopupMenu.Free;
        FTags.Free;
        FEdit.Free;
        inherited;
      end;
      
      procedure TTagEditor.HideEditor;
      begin
        FEdit.Text := '';
        FEdit.Hide;
      //  SetFocus;
      end;
      
      
      procedure TTagEditor.KeyDown(var Key: Word; Shift: TShiftState);
      begin
        inherited;
        case Key of
          VK_END:
            ShowEditor;
          VK_DELETE:
            Perform(WM_CLEAR, 0, 0);
          VK_INSERT:
            Perform(WM_PASTE, 0, 0);
        end;
      end;
      
      procedure TTagEditor.KeyPress(var Key: Char);
      begin
        inherited;
      
        case Key of
          ^C:
            begin
              Perform(WM_COPY, 0, 0);
              Key := #0;
              Exit;
            end;
          ^X:
            begin
              Perform(WM_CUT, 0, 0);
              Key := #0;
              Exit;
            end;
          ^V:
            begin
              Perform(WM_PASTE, 0, 0);
              Key := #0;
              Exit;
            end;
        end;
      
        ShowEditor;
        FEdit.Perform(WM_CHAR, ord(Key), 0);
      end;
      
      function TTagEditor.GetClickInfoAt(X, Y: integer): TClickInfo;
      var
        i: integer;
      begin
        result := NOWHERE;
        if (X >= FEditPos.X) and (Y >= FEditPos.Y) then
          Exit(EDITOR);
      
        for i := 0 to FTags.Count - 1 do
          if InRange(X, FLefts[i], FRights[i]) and InRange(Y, FTops[i], FBottoms[i]) then
          begin
            result := i;
            if InRange(X, FCloseBtnLefts[i], FCloseBtnLefts[i] + FCloseBtnWidth) and
              InRange(Y, FCloseBtnTops[i], FCloseBtnTops[i] + FActualTagHeight) and
              not FShrunk then
              result := result or PART_REMOVE_BUTTON;
            break;
          end;
      end;
      
      function TTagEditor.IsFirstOnRow(TagIndex: integer): boolean;
      begin
        result := (TagIndex = 0) or (FTops[TagIndex] > FTops[TagIndex-1]);
      end;
      
      function TTagEditor.IsLastOnRow(TagIndex: integer): boolean;
      begin
        result := (TagIndex = FTags.Count - 1) or (FTops[TagIndex] < FTops[TagIndex+1]);
      end;
      
      function TTagEditor.GetSeparatorIndexAt(X, Y: integer): integer;
      var
        i: Integer;
      begin
        result := FTags.Count;
        Y := Max(Y, FSpacing + 1);
        for i := FTags.Count - 1 downto 0 do
        begin
          if Y < FTops[i] then Continue;
          if (IsLastOnRow(i) and (X >= FRights[i])) or
            ((X < FRights[i]) and (IsFirstOnRow(i) or (FRights[i-1] < X))) then
          begin
            result := i;
            if (IsLastOnRow(i) and (X >= FRights[i])) then inc(result);
            Exit;
          end;
        end;
      end;
      
      procedure TTagEditor.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
        Y: Integer);
      begin
        FMouseDownClickInfo := GetClickInfoAt(X, Y);
        if GetTagIndex(FMouseDownClickInfo) <> EDITOR then
          SetFocus;
      end;
      
      procedure TTagEditor.CreateCaret;
      begin
        if not FCaretVisible then
          FCaretVisible := Windows.CreateCaret(Handle, 0, 0, FActualTagHeight);
      end;
      
      procedure TTagEditor.DestroyCaret;
      begin
        if not FCaretVisible then Exit;
        Windows.DestroyCaret;
        FCaretVisible := false;
      end;
      
      procedure TTagEditor.MouseMove(Shift: TShiftState; X, Y: Integer);
      var
        SepIndex: integer;
      begin
        inherited;
      
        if IsKeyDown(VK_LBUTTON) and
          InRange(GetTagIndex(FMouseDownClickInfo), TAG_LOW, TAG_HIGH) then
        begin
          FDragging := true;
          Screen.Cursor := crDrag;
          SepIndex := GetSeparatorIndexAt(X, Y);
          TForm(Parent).Caption := IntToStr(SepIndex);
          CreateCaret;
          if SepIndex = FTags.Count then
            SetCaretPos(FLefts[SepIndex - 1] + FWidths[SepIndex - 1] + FSpacing div 2,
              FTops[SepIndex - 1])
          else
            SetCaretPos(FLefts[SepIndex] - FSpacing div 2, FTops[SepIndex]);
          ShowCaret(Handle);
          Exit;
        end;
      
        case GetTagIndex(GetClickInfoAt(X,Y)) of
          NOWHERE: Cursor := crArrow;
          EDITOR: Cursor := crIBeam;
          TAG_LOW..TAG_HIGH: Cursor := crHandPoint;
        end;
      
      end;
      
      procedure TTagEditor.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
        Y: Integer);
      var
        pnt: TPoint;
        CanRemove: boolean;
        ClickInfo: TClickInfo;
        i: word;
        p: cardinal;
        SepIndex: integer;
      begin
        inherited;
      
        if FDragging then
        begin
          DestroyCaret;
          FDragging := false;
          Screen.Cursor := crDefault;
          SepIndex := GetSeparatorIndexAt(X, Y);
          if not InRange(SepIndex, GetTagIndex(FMouseDownClickInfo),
            GetTagIndex(FMouseDownClickInfo) + 1) then
            FTags.Move(GetTagIndex(FMouseDownClickInfo), SepIndex -
              IfThen(SepIndex > GetTagIndex(FMouseDownClickInfo), 1, 0));
          Exit;
        end;
      
        ClickInfo := GetClickInfoAt(X, Y);
      
        if ClickInfo <> FMouseDownClickInfo then Exit;
      
        i := GetTagIndex(ClickInfo);
        p := GetTagPart(ClickInfo);
      
        case i of
          EDITOR:
            ShowEditor;
          NOWHERE: ;
        else
          case Button of
            mbLeft:
              begin
                case p of
                  PART_BODY:
                    if Assigned(FTagClickEvent) then
                      FTagClickEvent(Self, i, FTags[i]);
                  PART_REMOVE_BUTTON:
                    begin
                      if Assigned(FOnRemoveConfirm) then
                      begin
                        CanRemove := false;
                        FOnRemoveConfirm(Self, i, FTags[i], CanRemove);
                        if not CanRemove then Exit;
                      end;
                      FTags.Delete(i);
                      if Assigned(FTagRemoved) then
                        FTagRemoved(Self);
                    end;
                end;
              end;
            mbRight:
              begin
                FPopupMenu.Items[0].Tag := i;
                pnt := ClientToScreen(Point(X,Y));
                FPopupMenu.Items[0].Caption := 'Delete tag "' + FTags[i] + '"';
                FPopupMenu.Popup(pnt.X, pnt.Y);
              end;
          end;
        end;
      
      end;
      
      procedure TTagEditor.Paint;
      var
        i: integer;
        w: integer;
        x, y: integer;
        R: TRect;
        MeanWidth: integer;
        S: string;
        DesiredHeight: integer;
      begin
        inherited;
        Canvas.Brush.Color := FBgColor;
        Canvas.Pen.Color := FBorderColor;
        Canvas.Rectangle(ClientRect);
        Canvas.Font.Assign(Self.Font);
        SetLength(FLefts, FTags.Count);
        SetLength(FRights, FTags.Count);
        SetLength(FTops, FTags.Count);
        SetLength(FBottoms, FTags.Count);
        SetLength(FWidths, FTags.Count);
        SetLength(FCloseBtnLefts, FTags.Count);
        SetLength(FCloseBtnTops, FTags.Count);
        FCloseBtnWidth := Canvas.TextWidth('×');
        FShrunk := false;
      
        // Do metrics
        FNumRows := 1;
        if FMultiLine then
        begin
          FActualTagHeight := FTagHeight;
          x := FSpacing;
          y := FSpacing;
          for i := 0 to FTags.Count - 1 do
          begin
            FWidths[i] := Canvas.TextWidth(FTags[i] + ' ×') + 2*FSpacing;
            FLefts[i] := x;
            FRights[i] := x + FWidths[i];
            FTops[i] := y;
            FBottoms[i] := y + FTagHeight;
      
            if x + FWidths[i] + FSpacing > ClientWidth then
         { no need to make room for the editor, since it can reside on the next row! }
            begin
              x := FSpacing;
              inc(y, FTagHeight + FSpacing);
              inc(FNumRows);
              FLefts[i] := x;
              FRights[i] := x + FWidths[i];
              FTops[i] := y;
              FBottoms[i] := y + FTagHeight;
            end;
      
            FCloseBtnLefts[i] := x + FWidths[i] - FCloseBtnWidth - FSpacing;
            FCloseBtnTops[i] := y;
      
            inc(x, FWidths[i] + FSpacing);
          end;
        end
        else // i.e., not FMultiLine
        begin
          FActualTagHeight := ClientHeight - 2*FSpacing;
          x := FSpacing;
          y := FSpacing;
          for i := 0 to FTags.Count - 1 do
          begin
            FWidths[i] := Canvas.TextWidth(FTags[i] + ' ×') + 2*FSpacing;
            FLefts[i] := x;
            FRights[i] := x + FWidths[i];
            FTops[i] := y;
            FBottoms[i] := y + FActualTagHeight;
            inc(x, FWidths[i] + FSpacing);
            FCloseBtnLefts[i] := FRights[i] - FCloseBtnWidth - FSpacing;
            FCloseBtnTops[i] := y;
          end;
          FShrunk := x + 64 {FEdit} > ClientWidth;
          if FShrunk then
          begin
      
            // Enough to remove close buttons?
            x := FSpacing;
            y := FSpacing;
            for i := 0 to FTags.Count - 1 do
            begin
              FWidths[i] := Canvas.TextWidth(FTags[i]) + 2*FSpacing;
              FLefts[i] := x;
              FRights[i] := x + FWidths[i];
              FTops[i] := y;
              FBottoms[i] := y + FActualTagHeight;
              inc(x, FWidths[i] + FSpacing);
              FCloseBtnLefts[i] := FRights[i] - FCloseBtnWidth - FSpacing;
              FCloseBtnTops[i] := y;
            end;
      
            if x + 64 {FEdit} > ClientWidth then // apparently no
            begin
              MeanWidth := (ClientWidth - 2*FSpacing - 64 {FEdit}) div FTags.Count - FSpacing;
              x := FSpacing;
              for i := 0 to FTags.Count - 1 do
              begin
                FWidths[i] := Min(FWidths[i], MeanWidth);
                FLefts[i] := x;
                FRights[i] := x  + FWidths[i];
                inc(x, FWidths[i] + FSpacing);
              end;
            end;
          end;
        end;
      
        FEditPos := Point(FSpacing, FSpacing + (FActualTagHeight - FEdit.Height) div 2);
        if FTags.Count > 0 then
          FEditPos := Point(FRights[FTags.Count - 1] + FSpacing,
            FTops[FTags.Count - 1] + (FActualTagHeight - FEdit.Height) div 2);
        if FMultiLine and (FEditPos.X + 64 > ClientWidth) and (FTags.Count > 0) then
        begin
          FEditPos := Point(FSpacing,
            FTops[FTags.Count - 1] + FTagHeight + FSpacing +
            (FActualTagHeight - FEdit.Height) div 2);
          inc(FNumRows);
        end;
      
        DesiredHeight := FSpacing + FNumRows*(FTagHeight+FSpacing);
        if FMultiLine and FAutoHeight and (ClientHeight <> DesiredHeight) then
        begin
          ClientHeight := DesiredHeight;
          Invalidate;
          Exit;
        end;
      
        // Draw
        for i := 0 to FTags.Count - 1 do
        begin
          x := FLefts[i];
          y := FTops[i];
          w := FWidths[i];
          R := Rect(x, y, x + w, y + FActualTagHeight);
          Canvas.Brush.Color := FTagBgColor;
          Canvas.Pen.Color := FTagBorderColor;
          Canvas.Rectangle(R);
          Canvas.Font.Color := FTextColor;
          Canvas.Brush.Style := bsClear;
          R.Left := R.Left + FSpacing;
          S := FTags[i];
          if not FShrunk then
            S := S + ' ×';
          DrawText(Canvas.Handle, PChar(S), -1, R, DT_SINGLELINE or DT_VCENTER or
            DT_LEFT or DT_END_ELLIPSIS or DT_NOPREFIX);
          Canvas.Brush.Style := bsSolid;
        end;
      
        if FEdit.Visible then
        begin
          FEdit.Left := FEditPos.X;
          FEdit.Top := FEditPos.Y;
          FEdit.Width := ClientWidth - FEdit.Left - FSpacing;
        end;
        if Focused then
        begin
          R := Rect(2, 2, ClientWidth - 2, ClientHeight - 2);
          SetBkColor(Canvas.Handle, clWhite);
          SetTextColor(clBlack);
          Canvas.DrawFocusRect(R);
        end;
      end;
      
      procedure TTagEditor.SetAutoHeight(const Value: boolean);
      begin
        if FAutoHeight <> Value then
        begin
          FAutoHeight := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.SetBgColor(const Value: TColor);
      begin
        if FBgColor <> Value then
        begin
          FBgColor := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.SetBorderColor(const Value: TColor);
      begin
        if FBorderColor <> Value then
        begin
          FBorderColor := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.SetMultiLine(const Value: boolean);
      begin
        if FMultiLine <> Value then
        begin
          FMultiLine := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.SetTagBgColor(const Value: TColor);
      begin
        if FTagBgColor <> Value then
        begin
          FTagBgColor := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.SetTagBorderColor(const Value: TColor);
      begin
        if FTagBorderColor <> Value then
        begin
          FTagBorderColor := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.SetTagHeight(const Value: integer);
      begin
        if FTagHeight <> Value then
        begin
          FTagHeight := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.SetTags(const Value: TStringList);
      begin
        FTags.Assign(Value);
        Invalidate;
      end;
      
      procedure TTagEditor.SetTextColor(const Value: TColor);
      begin
        if FTextColor <> Value then
        begin
          FTextColor := Value;
          Invalidate;
        end;
      end;
      
      procedure TTagEditor.ShowEditor;
      begin
        FEdit.Left := FEditPos.X;
        FEdit.Top := FEditPos.Y;
        FEdit.Width := ClientWidth - FEdit.Left - FSpacing;
        FEdit.Color := FEditorColor;
        FEdit.Text := '';
        FEdit.Show;
        FEdit.SetFocus;
      end;
      
      procedure TTagEditor.SetSpacing(const Value: integer);
      begin
        if FSpacing <> Value then
        begin
          FSpacing := Value;
          Invalidate;
        end;
      end;
      
      initialization
        Screen.Cursors[crHandPoint] := LoadCursor(0, IDC_HAND); // Get the normal hand cursor
      
      end.
      
      单元标记编辑器;
      接口
      使用
      窗口、消息、系统、类、控件、stdctrl、窗体、图形、,
      类型、菜单;
      类型
      TClickInfo=基数;
      GetTagIndex=单词;
      const TAG_LOW=0;
      const TAG_HIGH=MAXWORD-2;
      const EDITOR=MAXWORD-1;
      const NOWHERE=MAXWORD;
      const PART_BODY=$00000000;
      const PART_REMOVE_按钮=$00010000;
      函数GetTagPart(单击信息:TClickInfo):基数;
      类型
      TTagClickEvent=过程(发送方:ToObject;标记索引:integer;
      对象的常量标记(标题:字符串);
      TRemoveConfirmEvent=过程(发送方:TObject;标记索引:integer;
      对象的const TagCaption:string;var CanRemove:boolean);
      TTagEditor=class(TCustomControl)
      私有的
      {私有声明}
      ftag:TStringList;
      FEdit:TEdit;
      FBgColor:t彩色;
      FBorderColor:TColor;
      FTagBgColor:TColor;
      FTagBorderColor:TColor;
      FSpacing:整数;
      FTextColor:TColor;
      弗莱夫特,恐惧,恐惧,
      FTops,FBottoms:整数数组;
      FCloseBtnLefts,FCloseBtnTops:整数数组;
      FCloseBtnWidth:整数;
      fspace:布尔型;
      FCommaAccepts:布尔值;
      fsemicology:布尔型;
      输入:布尔;
      FNoLeadingSpaceInput:布尔值;
      FTagClickEvent:TTagClickEvent;
      FAllowDuplicates:布尔值;
      FPopupMenu:TPopupMenu;
      F多行:布尔值;
      FTagHeight:整数;
      FEditPos:TPoint;
      FActualTagHeight:整数;
      FShrunk:布尔型;
      FEditorColor:t彩色;
      FTAGADED:TNotifyEvent;
      FTAGREMOVE:TNotifyEvent;
      FOnChange:TNotifyEvent;
      FOnRemoveConfirm:TremoveConfirm;
      fmousedownclickininfo:TClickInfo;
      FCaretVisible:布尔值;
      FDragging:布尔值;
      FAutoHeight:布尔值;
      FNumRows:整数;
      程序设置颜色(常量值:TColor);
      程序SetTagBgColor(常量值:TColor);
      程序SetTagBorderColor(常量值:TColor);
      程序设置间隔(常量值:整数);
      程序标记更改(发送方:ToObject);
      程序设置标签(常量值:TStringList);
      程序SetTextColor(常量值:TColor);
      程序显示编辑器;
      程序隐藏器;
      程序EditKeyPress(发送方:TObject;变量键:Char);
      过程mnuDeleteItemClick(发送方:TObject);
      过程SetMultiLine(常量值:布尔值);
      过程SetTagHeight(常量值:整数);
      程序EditExit(发送方:TObject);
      函数Accept:boolean;
      程序设置颜色(常量值:TColor);
      函数GetClickInfoAt(X,Y:integer):TClickInfo;
      函数GetSeparatorIndexAt(X,Y:integer):整数;
      程序插入符号;
      程序插入符号;
      函数IsFirstOnRow(TagIndex:integer):布尔值;内联;
      函数IsLastOnRow(TagIndex:integer):布尔型;
      过程SetAutoHeight(常量值:布尔值);
      受保护的
      {受保护的声明}
      程序漆;推翻
      过程鼠标向下移动(按钮:TMouseButton;Shift:tShift状态;X:整数;
      Y:整数);推翻
      过程MouseMove(Shift:tshift状态;X:Integer;Y:Integer);推翻
      程序按键(变量键:Char);推翻
      程序WndProc(变量消息:TMessage);推翻
      程序键控(var键:字;移位:TShift状态);推翻
      过程鼠标(按钮:TMouseButton;Shift:tShift状态;X:整数;
      Y:整数);推翻
      公众的
      {公开声明}
      构造函数创建(AOwner:TComponent);推翻
      毁灭者毁灭;推翻
      出版
      {已发布声明}
      财产秩序;
      财产禁令;
      属性颜色;
      地产锚;
      属性对齐;
      属性标签;
      属性游标;
      属性BgColor:t颜色读取FBgColor写入SetBgColor;
      属性BorderColor:TColor读取FBorderColor写入SetBorderColor;
      属性TagBgColor:t颜色读取FTagBgColor写入设置TagBgColor;
      属性标记BorderColor:TColor读取FTagBorderColor
      写SetTagBorderColor;
      属性间距:整数读fspacking写setspace;
      属性标签:TStringList读取FTags写入设置标签;
      属性TextColor:TColor读取FTextColor写入SetTextColor;
      属性SpaceAccepts:boolean read fspacecopts write fspacecopts
      默认为true;
      属性commaaaccepts:布尔读取FCommaAccepts写入FCommaAccepts
      默认为true;
      属性分号接受:布尔读取FSemicolonAccepts
      写入fsemicolon接受默认值true;
      属性TrimInput:布尔读取FTrimInput写入FTrimInput默认值为true;
      属性NoLeadingSpaceInput:布尔读取FNoLeadingSpaceInput
      写入FNoLeadingSpaceInput default true;
      属性AllowDuplicates:布尔读取FAllowDuplicates写入FAllowDuplicates
      默认为false;
      属性多行:布尔读FMultiLine写SetMultiLine默认值false;
      属性标记高度:整数读取FTagHeight写入设置标记高度默认值32;
      属性EditorColor:t颜色读取FEditorColor写入FEditorColor
      德福