2
0
Эх сурвалжийг харах

Mouseover #include file tabs now also show a close button.

Martijn Laan 1 жил өмнө
parent
commit
3e32e94814

+ 83 - 23
Components/NewTabSet.pas

@@ -20,21 +20,25 @@ type
 
   TBoolList = TList<Boolean>;
 
+  TCloseButtonClickEvent = procedure(Sender: TObject; Index: Integer) of object;
+
   TNewTabSet = class(TCustomControl)
   private
     FCloseButtons: TBoolList;
     FHints: TStrings;
     FMenuThemeData: HTHEME;
-    FOnCloseButtonClick: TNotifyEvent;
+    FOnCloseButtonClick: TCloseButtonClickEvent;
     FTabs: TStrings;
     FTabIndex: Integer;
     FTabPosition: TTabPosition;
     FTabsOffset: Integer;
     FTheme: TTheme;
     FThemeDark: Boolean;
+    FHotIndex: Integer;
     function GetTabRect(Index: Integer): TRect;
     function GetCloseButtonRect(const TabRect: TRect): TRect;
     procedure InvalidateTab(Index: Integer);
+    procedure InvalidateCloseButton(Index: Integer);
     procedure CloseButtonsListChanged(Sender: TObject; const Item: Boolean;
       Action: TCollectionNotification);
     procedure TabsListChanged(Sender: TObject);
@@ -49,10 +53,13 @@ type
     procedure EnsureCurrentTabIsFullyVisible;
   protected
     procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
+    procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
     procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;
     procedure CreateParams(var Params: TCreateParams); override;
     procedure CreateWnd; override;
     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
+    procedure UpdateHotIndex(NewHotIndex: Integer);
+    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
     procedure Paint; override;
   public
     constructor Create(AOwner: TComponent); override;
@@ -69,7 +76,7 @@ type
     property TabPosition: TTabPosition read FTabPosition write SetTabPosition default tpBottom;
     property PopupMenu;
     property OnClick;
-    property OnCloseButtonClick: TNotifyEvent read FOnCloseButtonClick write FOnCloseButtonClick;
+    property OnCloseButtonClick: TCloseButtonClickEvent read FOnCloseButtonClick write FOnCloseButtonClick;
   end;
 
 procedure Register;
@@ -77,7 +84,7 @@ procedure Register;
 implementation
 
 uses
-  WinApi.UxTheme;
+  WinApi.UxTheme, Types;
 
 procedure Register;
 begin
@@ -173,6 +180,7 @@ begin
   ControlStyle := ControlStyle + [csOpaque];
   Width := 129;
   Height := 21;
+  FHotIndex := -1;
 end;
 
 procedure TNewTabSet.CreateParams(var Params: TCreateParams);
@@ -215,6 +223,24 @@ begin
   end;
 end;
 
+procedure TNewTabSet.WMMouseMove(var Message: TWMMouseMove);
+begin
+  var Pos := SmallPointToPoint(Message.Pos);
+  var NewHotIndex := -1;
+
+  for var I := 0 to FTabs.Count-1 do begin
+    if (I <> TabIndex) and (I < FCloseButtons.Count) and FCloseButtons[I] then begin
+      var R := GetTabRect(I);
+      if PtInRect(R, TPoint.Create(Pos.X, Pos.Y)) then begin
+        NewHotIndex := I;
+        Break;
+      end;
+    end;
+  end;
+
+  UpdateHotIndex(NewHotIndex);
+end;
+
 procedure TNewTabSet.WMThemeChanged(var Message: TMessage);
 begin
   { Don't Run to Cursor into this function, it will interrupt up the theme change }
@@ -267,14 +293,24 @@ begin
   end;
 end;
 
+procedure TNewTabSet.InvalidateCloseButton(Index: Integer);
+begin
+  if HandleAllocated and (Index >= 0) and (Index < FTabs.Count) then begin
+    var R := GetCloseButtonRect(GetTabRect(Index));
+    InvalidateRect(Handle, @R, False);
+  end;
+end;
+
 procedure TNewTabSet.CloseButtonsListChanged(Sender: TObject; const Item: Boolean;
   Action: TCollectionNotification);
 begin
+  FHotIndex := -1;
   Invalidate;
 end;
 
 procedure TNewTabSet.TabsListChanged(Sender: TObject);
 begin
+  FHotIndex := -1;
   Invalidate;
 end;
 
@@ -293,11 +329,11 @@ begin
     for I := 0 to FTabs.Count-1 do begin
       R := GetTabRect(I);
       if (X >= R.Left) and (X < R.Right) then begin
-        if (I = TabIndex) and (I < FCloseButtons.Count) and FCloseButtons[I] then begin
+        if ((I = TabIndex) or (I = FHotIndex)) and (I < FCloseButtons.Count) and FCloseButtons[I] then begin
           var R2 := GetCloseButtonRect(R);
           if PtInRect(R2, TPoint.Create(X, Y)) then begin
             if Assigned(OnCloseButtonClick) then
-              OnCloseButtonClick(Self);
+              OnCloseButtonClick(Self, I);
             Break;
           end;
         end;
@@ -308,10 +344,48 @@ begin
   end;
 end;
 
+procedure TNewTabSet.UpdateHotIndex(NewHotIndex: Integer);
+begin
+  var OldHotIndex := FHotIndex;
+  if NewHotIndex <> OldHotIndex then begin
+    FHotIndex := NewHotIndex;
+    if OldHotIndex <> -1 then
+      InvalidateCloseButton(OldHotIndex);
+    if NewHotIndex <> -1 then
+      InvalidateCloseButton(NewHotIndex);
+  end;
+end;
+
+procedure TNewTabSet.CMMouseLeave(var Message: TMessage);
+begin
+  UpdateHotIndex(-1);
+  inherited;
+end;
+
 procedure TNewTabSet.Paint;
 var
   HighColorMode: Boolean;
 
+  procedure DrawCloseButton(const TabRect: TRect; const TabIndex: Integer);
+  begin
+   if (TabIndex < FCloseButtons.Count) and FCloseButtons[TabIndex] then begin
+      var R := GetCloseButtonRect(TabRect);
+      if FMenuThemeData <> 0 then begin
+        var Offset := MulDiv(1, CurrentPPI, 96);
+        Inc(R.Left, Offset);
+        Inc(R.Top, Offset);
+        DrawThemeBackground(FMenuThemeData, Canvas.Handle,  MENU_SYSTEMCLOSE, MSYSC_NORMAL, R, nil);
+      end else begin
+        InflateRect(R, -MulDiv(3, CurrentPPI, 96), -MulDiv(6, CurrentPPI, 96));
+        Canvas.Pen.Color := Canvas.Font.Color;
+        Canvas.MoveTo(R.Left, R.Top);
+        Canvas.LineTo(R.Right, R.Bottom);
+        Canvas.MoveTo(R.Left, R.Bottom-1);
+        Canvas.LineTo(R.Right, R.Top-1);
+      end;
+    end;
+  end;
+
   procedure DrawTabs(const SelectedTab: Boolean);
   var
     I: Integer;
@@ -326,29 +400,13 @@ var
         else
           Canvas.Brush.Color := clBtnFace;
         Canvas.FillRect(R);
-        
+
         if FTheme <> nil then
           Canvas.Font.Color := FTheme.Colors[tcFore]
         else
           Canvas.Font.Color := clBtnText;
         Canvas.TextOut(R.Left + TabPaddingX, R.Top + TabPaddingY, FTabs[I]);
-
-       if (I < FCloseButtons.Count) and FCloseButtons[I] then begin
-          var R2 := GetCloseButtonRect(R);
-          if FMenuThemeData <> 0 then begin
-            var Offset := MulDiv(1, CurrentPPI, 96);
-            Inc(R2.Left, Offset);
-            Inc(R2.Top, Offset);
-            DrawThemeBackground(FMenuThemeData, Canvas.Handle,  MENU_SYSTEMCLOSE, MSYSC_NORMAL, R2, nil);
-          end else begin
-            InflateRect(R2, -MulDiv(3, CurrentPPI, 96), -MulDiv(6, CurrentPPI, 96));
-            Canvas.Pen.Color := Canvas.Font.Color;
-            Canvas.MoveTo(R2.Left, R2.Top);
-            Canvas.LineTo(R2.Right, R2.Bottom);
-            Canvas.MoveTo(R2.Left, R2.Bottom-1);
-            Canvas.LineTo(R2.Right, R2.Top-1);
-          end;
-        end;
+        DrawCloseButton(R, I);
         ExcludeClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
         Break;
       end;
@@ -363,6 +421,8 @@ var
           Canvas.Font.Color := clBtnHighlight;
         end;
         Canvas.TextOut(R.Left + TabPaddingX, R.Top + TabPaddingY, FTabs[I]);
+        if FHotIndex = I then
+          DrawCloseButton(R, I);
       end;
     end;
   end;

+ 4 - 4
Projects/Src/CompForm.dfm

@@ -535,10 +535,10 @@ object CompileForm: TCompileForm
         ShortCut = 24585
         OnClick = VPreviousTabClick
       end
-      object VCloseTab: TMenuItem
+      object VCloseCurrentTab: TMenuItem
         Caption = 'Close Tab'
         ShortCut = 16499
-        OnClick = VCloseTabClick
+        OnClick = VCloseCurrentTabClick
       end
       object VReopenTab: TMenuItem
         Caption = 'Reopen Tab'
@@ -4333,10 +4333,10 @@ object CompileForm: TCompileForm
     OnPopup = MemosTabSetPopup
     Left = 48
     Top = 51
-    object VCloseTab2: TMenuItem
+    object VCloseCurrentTab2: TMenuItem
       Caption = 'Close Current Tab'
       ShortCut = 16499
-      OnClick = VCloseTabClick
+      OnClick = VCloseCurrentTabClick
     end
     object VReopenTab2: TMenuItem
       Caption = 'Reopen Tab'

+ 58 - 45
Projects/Src/CompForm.pas

@@ -210,11 +210,11 @@ type
     PrintDialog: TPrintDialog;
     FSaveEncodingUTF8WithoutBOM: TMenuItem;
     TFilesDesigner: TMenuItem;
-    VCloseTab: TMenuItem;
+    VCloseCurrentTab: TMenuItem;
     VReopenTab: TMenuItem;
     VReopenTabs: TMenuItem;
     MemosTabSetPopupMenu: TPopupMenu;
-    VCloseTab2: TMenuItem;
+    VCloseCurrentTab2: TMenuItem;
     VReopenTab2: TMenuItem;
     VReopenTabs2: TMenuItem;
     N23: TMenuItem;
@@ -318,11 +318,11 @@ type
     procedure FindResultsListDblClick(Sender: TObject);
     procedure FPrintClick(Sender: TObject);
     procedure TFilesDesignerClick(Sender: TObject);
-    procedure VCloseTabClick(Sender: TObject);
+    procedure VCloseCurrentTabClick(Sender: TObject);
     procedure VReopenTabClick(Sender: TObject);
     procedure VReopenTabsClick(Sender: TObject);
     procedure MemosTabSetPopup(Sender: TObject);
-    procedure MemosTabSetOnCloseButtonClick(Sender: TObject);
+    procedure MemosTabSetOnCloseButtonClick(Sender: TObject; Index: Integer);
     procedure StatusBarClick(Sender: TObject);
   private
     { Private declarations }
@@ -412,6 +412,7 @@ type
     function AskToDetachDebugger: Boolean;
     procedure BringToForeground;
     procedure CheckIfTerminated;
+    procedure CloseTab(const TabIndex: Integer);
     procedure CompileFile(AFilename: String; const ReadFromFile: Boolean);
     procedure CompileIfNecessary;
     function ConfirmCloseFile(const PromptToSave: Boolean): Boolean;
@@ -476,6 +477,7 @@ type
     procedure StoreLastFindOptions(Sender: TObject);
     procedure SyncEditorOptions;
     procedure SyncZoom;
+    function TabIndexToMemo(const ATabIndex, AMaxTabIndex: Integer): TCompScintEdit;
     function ToCurrentPPI(const XY: Integer): Integer;
     procedure ToggleBreakPoint(Line: Integer);
     procedure UpdateAllMemosLineMarkers;
@@ -2293,7 +2295,7 @@ begin
   VStatusBar.Checked := StatusBar.Visible;
   VNextTab.Enabled := MemosTabSet.Visible and (MemosTabSet.Tabs.Count > 1);
   VPreviousTab.Enabled := VNextTab.Enabled;
-  VCloseTab.Enabled := MemosTabSet.Visible and (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
+  VCloseCurrentTab.Enabled := MemosTabSet.Visible and (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
   VReopenTab.Visible := MemosTabSet.Visible and (FHiddenFiles.Count > 0);
   if VReopenTab.Visible then
     UpdateReopenTabMenu(VReopenTab);
@@ -2325,20 +2327,32 @@ begin
   MemosTabSet.TabIndex := NewTabIndex;
 end;
 
-procedure TCompileForm.VCloseTabClick(Sender: TObject);
+procedure TCompileForm.CloseTab(const TabIndex: Integer);
 begin
-  var Index := MemoToTabIndex(FActiveMemo);
-  MemosTabSet.Tabs.Delete(Index);
-  MemosTabSet.Hints.Delete(Index);
-  MemosTabSet.CloseButtons.Delete(Index);
-  FActiveMemo.Visible := False;
-  FHiddenFiles.Add((FActiveMemo as TCompScintFileEdit).Filename);
+  var Memo := TabIndexToMemo(TabIndex, MemosTabSet.Tabs.Count-1);
+
+  MemosTabSet.Tabs.Delete(TabIndex);
+  MemosTabSet.Hints.Delete(TabIndex);
+  MemosTabSet.CloseButtons.Delete(TabIndex);
+
+  if Memo = FActiveMemo then
+    Memo.Visible := False;
+
+  FHiddenFiles.Add((Memo as TCompScintFileEdit).Filename);
   UpdateHiddenFilesPanel;
   SaveKnownIncludedAndHiddenFiles(FMainMemo.Filename);
 
-  { Select next tab, except when we're already at the end }
-  VNextTabClick(Self);
-  VPreviousTabClick(Self);
+  if TabIndex = MemosTabset.TabIndex then begin
+    { Select next tab, except when we're already at the end }
+    VNextTabClick(Self);
+    VPreviousTabClick(Self);
+  end else if TabIndex < MemosTabset.TabIndex then
+    MemosTabSet.TabIndex := MemosTabset.TabIndex-1; { Reselect old selected tab }
+end;
+
+procedure TCompileForm.VCloseCurrentTabClick(Sender: TObject);
+begin
+  CloseTab(MemosTabSet.TabIndex);
 end;
 
 procedure TCompileForm.ReopenTabOrTabs(const HiddenFileIndex: Integer;
@@ -2739,7 +2753,7 @@ end;
 procedure TCompileForm.MemosTabSetPopup(Sender: TObject);
 begin
   { Main and preprocessor memos can't be hidden }
-  VCloseTab2.Enabled := (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
+  VCloseCurrentTab2.Enabled := (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
 
   VReopenTab2.Visible := FHiddenFiles.Count > 0;
   if VReopenTab2.Visible then
@@ -2748,31 +2762,6 @@ begin
 end;
 
 procedure TCompileForm.MemosTabSetClick(Sender: TObject);
-
-  { Also see MemoToTabIndex }
-  function TabIndexToMemoIndex(const ATabIndex, AMaxTabIndex: Integer): Integer;
-  begin
-    if ATabIndex = 0 then
-      Result := 0 { First tab displays the main memo which is FMemos[0] }
-    else if FPreprocessorOutputMemo.Used and (ATabIndex = AMaxTabIndex) then
-      Result := 1 { Last tab displays the preprocessor output memo which is FMemos[1] }
-    else begin
-      { Only count memos not explicitly hidden by the user }
-      var TabIndex := 0;
-      for var MemoIndex := FirstIncludedFilesMemoIndex to FFileMemos.Count-1 do begin
-        if FHiddenFiles.IndexOf(FFileMemos[MemoIndex].Filename) = -1 then begin
-          Inc(TabIndex);
-          if TabIndex = ATabIndex then begin
-            Result := MemoIndex + 1;   { Other tabs display include files which start at second tab but at FMemos[2] }
-            Exit;
-          end;
-        end;
-      end;
-
-      raise Exception.Create('TabIndexToMemoIndex failed');
-    end;
-  end;
-
 var
   Memo: TCompScintEdit;
   TabIndex, MaxTabIndex: Integer;
@@ -2781,7 +2770,7 @@ begin
 
   MaxTabIndex := MemosTabSet.Tabs.Count-1;
   for TabIndex := 0 to MaxTabIndex do begin
-    Memo := FMemos[TabIndexToMemoIndex(TabIndex, MaxTabIndex)];
+    Memo := TabIndexToMemo(TabIndex, MaxTabIndex);
     Memo.Visible := (TabIndex = MemosTabSet.TabIndex);
     if Memo.Visible then begin
       FActiveMemo := Memo;
@@ -2795,9 +2784,9 @@ begin
   UpdateModifiedPanel;
 end;
 
-procedure TCompileForm.MemosTabSetOnCloseButtonClick(Sender: TObject);
+procedure TCompileForm.MemosTabSetOnCloseButtonClick(Sender: TObject; Index: Integer);
 begin
-  VCloseTabClick(Self);
+  CloseTab(Index);
 end;
 
 procedure TCompileForm.InitializeFindText(Dlg: TFindDialog);
@@ -3265,6 +3254,30 @@ begin
   end;
 end;
 
+{ Also see MemoToTabIndex }
+function TCompileForm.TabIndexToMemo(const ATabIndex, AMaxTabIndex: Integer): TCompScintEdit;
+begin
+  if ATabIndex = 0 then
+    Result := FMemos[0] { First tab displays the main memo which is FMemos[0] }
+  else if FPreprocessorOutputMemo.Used and (ATabIndex = AMaxTabIndex) then
+    Result := FMemos[1] { Last tab displays the preprocessor output memo which is FMemos[1] }
+  else begin
+    { Only count memos not explicitly hidden by the user }
+    var TabIndex := 0;
+    for var MemoIndex := FirstIncludedFilesMemoIndex to FFileMemos.Count-1 do begin
+      if FHiddenFiles.IndexOf(FFileMemos[MemoIndex].Filename) = -1 then begin
+        Inc(TabIndex);
+        if TabIndex = ATabIndex then begin
+          Result := FMemos[MemoIndex + 1];   { Other tabs display include files which start at second tab but at FMemos[2] }
+          Exit;
+        end;
+      end;
+    end;
+
+    raise Exception.Create('TabIndexToMemo failed');
+  end;
+end;
+
 procedure TCompileForm.MoveCaretAndActivateMemo(AMemo: TCompScintFileEdit; const LineNumber: Integer;
   const AlwaysResetColumn: Boolean);
 var
@@ -4981,7 +4994,7 @@ end;
 procedure TCompileForm.StatusBarClick(Sender: TObject);
 begin
   if MemosTabSet.Visible and (FHiddenFiles.Count > 0) then begin
-    var Point := SmallPointToPoint(TSmallPoint(DWORD(GetMessagePos)));
+    var Point := SmallPointToPoint(TSmallPoint(GetMessagePos()));
     var X := StatusBar.ScreenToClient(Point).X;
     var W := 0;
     for var I := 0 to StatusBar.Panels.Count-1 do begin

+ 1 - 1
whatsnew.htm

@@ -49,7 +49,7 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
 <ul>
   <li>The Compiler IDE now opens up to 20 #include files in tabs, instead of up to 10.</li>
   <li>Tabs for opened #include files can now be closed: Added new <i>Close Tab (Ctrl+F4)</i>, <i>Reopen Tab</i>, and  <i>Reopen All Tabs</i> menu items to the <i>View</i> menu, also available by right clicking the tab bar.</li>
-  <li>The tab for the selected #include file now shows a close button.</li>
+  <li>The selected and mouseover #include file tabs now show close buttons.</li>
   <li>Added a clickable panel to the Status Bar showing the amount of closed tabs if there are any.</li>
 </ul>
 <p><span class="head2">Support for Windows Vista and Windows Server 2008 removed</span></p>