Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Medrano <jose.medrano@microsoft.com>2019-05-10 05:27:18 +0300
committerJose Medrano <josmed@microsoft.com>2019-07-29 13:00:40 +0300
commitf4cf5afcb68c141d666b0c20bb495d04fc8ae36b (patch)
tree481fd956b0a8197b1cdf619b5c1c7587d0bd0eb3 /main/src/core/MonoDevelop.Ide
parent5a8dda4e6daabe1182f28562b0ae894a137a24f4 (diff)
[Ide] Adds feature in MonoDevelop to pin opened tabs
This feature allows to lock some of your most used windows (tabs) to easily detect and open like VS2013 added. Fixes VSTS #890509 - Add support for pinned tabs - Adds Pin/Unpin tab feature clicking right button (contextual menu) in tab or Shortcut key -Don't call ping events if isPinned value has no change -Reuses current CloseAllHandler logic and make its more flexible -Fixes changes to use a ImmutableArray<ViewContent> instead a List -Adds a new Tab and Windows options panel and usage of ConfigurationProperty -Removes nested lambda with capture. -Removes an extra array conversion and makes the code clearer -Removes extra allocations using Min/MaxValue instead OrderBy -Uses a for loop instead a copied collection with a ForEach
Diffstat (limited to 'main/src/core/MonoDevelop.Ide')
-rw-r--r--main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml12
-rw-r--r--main/src/core/MonoDevelop.Ide/ExtensionModel/GlobalOptionsDialog.addin.xml1
-rw-r--r--main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs56
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs19
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs90
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs1
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs104
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/TabsWindowOptionsPanel.cs104
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj9
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs1
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-pinned-9.pngbin0 -> 204 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-pinned-9@2x.pngbin0 -> 329 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark.pngbin0 -> 204 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark@2x.pngbin0 -> 329 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9.pngbin0 -> 206 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9@2x.pngbin0 -> 295 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark.pngbin0 -> 199 bytes
-rw-r--r--main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark@2x.pngbin0 -> 295 bytes
19 files changed, 354 insertions, 45 deletions
diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml
index 70c27e8047..2e696441b6 100644
--- a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml
+++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml
@@ -543,6 +543,18 @@
defaultHandler = "MonoDevelop.Ide.Commands.ReopenClosedTabHandler"
_label = "Reopen Closed Tab"
_description = "Opens the last tab that has been closed"/>
+ <Command id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllExceptPinned"
+ defaultHandler = "MonoDevelop.Ide.Commands.CloseAllExceptPinnedHandler"
+ _label = "Close All Except _Pinned"
+ _description = "Close all files except pinned"
+ shortcut = "Control|Shift|P"
+ macShortcut = "Meta|Shift|P" />
+ <Command id = "MonoDevelop.Ide.Commands.FileTabCommands.PinTab"
+ defaultHandler = "MonoDevelop.Ide.Commands.PinTabHandler"
+ _label = "_Pin Tab"
+ _description = "Pin/Unpin current Tab selected"
+ shortcut = "Control|Alt|P"
+ macShortcut = "Meta|Alt|P" />
</Category>
<!-- ViewCommands -->
diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/GlobalOptionsDialog.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/GlobalOptionsDialog.addin.xml
index f89f61d487..352a1b8314 100644
--- a/main/src/core/MonoDevelop.Ide/ExtensionModel/GlobalOptionsDialog.addin.xml
+++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/GlobalOptionsDialog.addin.xml
@@ -13,6 +13,7 @@
<Section id = "Fonts" _label = "Fonts" fill="true" class = "MonoDevelop.Ide.Fonts.FontChooserPanel" icon = "md-prefs-fonts" />
<Section id = "Updates" _label = "Updates" class = "MonoDevelop.Ide.Gui.OptionPanels.AddInsOptionsPanel" icon="md-prefs-updates" />
<Section id = "TaskList" _label = "Tasks" fill="true" class = "MonoDevelop.Ide.Gui.OptionPanels.TasksOptionsPanel" icon="md-prefs-task-list" />
+ <Section id = "TabsWindows" _label = "Tabs and Windows" fill="true" class = "MonoDevelop.Ide.Projects.OptionPanels.TabsWindowOptionsPanel" icon="md-prefs-metadata" />
<Section id = "ExternalTools" _label = "External Tools" fill="true" class = "MonoDevelop.Ide.ExternalTools.ExternalToolPane" icon="md-prefs-external-tools" />
</Section>
diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml
index 70690194fb..c25b4e1b7c 100644
--- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml
+++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml
@@ -348,8 +348,10 @@
<Extension path = "/MonoDevelop/Ide/ContextMenu/DocumentTab">
<CommandItem id = "MonoDevelop.Ide.Commands.FileCommands.CloseFile" />
<CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAll" />
+ <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllExceptPinned" />
<CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllButThis" />
<CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllToTheRight" />
+ <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.PinTab" />
<CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.ReopenClosedTab" />
<SeparatorItem id = "CloseSeparator" />
<CommandItem id = "MonoDevelop.Ide.Commands.FileCommands.Save" />
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs
index b12ecad3fb..bd13649cf9 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs
@@ -36,6 +36,7 @@ using MonoDevelop.Ide;
using MonoDevelop.Components.AtkCocoaHelper;
using MonoDevelop.Core;
using MonoDevelop.Ide.Gui.Shell;
+using System.Linq;
namespace MonoDevelop.Components.DockNotebook
{
@@ -111,6 +112,23 @@ namespace MonoDevelop.Components.DockNotebook
};
allNotebooks.Add (this);
+
+ tabStrip.IsPinEnabled = IdeApp.Preferences.EnablePinnedTabs.Value;
+ RefreshCurrentTabStrip ();
+
+ IdeApp.Preferences.EnablePinnedTabs.Changed += EnablePinnedTabs_Changed;
+ }
+
+ void EnablePinnedTabs_Changed (object sender, EventArgs e) => RefreshCurrentTabStrip ();
+
+ void RefreshCurrentTabStrip ()
+ {
+ tabStrip.IsPinEnabled = IdeApp.Preferences.EnablePinnedTabs.Value;
+ if (!tabStrip.IsPinEnabled) {
+ for (int i = 0; i < pages.Count; i++) {
+ pages[i].IsPinned = false;
+ }
+ }
}
public static DockNotebook ActiveNotebook {
@@ -136,6 +154,7 @@ namespace MonoDevelop.Components.DockNotebook
public event TabsReorderedHandler TabsReordered;
public event EventHandler<TabEventArgs> TabClosed;
+ public event EventHandler<TabEventArgs> TabPinned;
public event EventHandler<TabEventArgs> TabActivated;
public event EventHandler<TabEventArgs> PageAdded;
@@ -349,6 +368,8 @@ namespace MonoDevelop.Components.DockNotebook
PageAdded?.Invoke (this, new TabEventArgs { Tab = tab, });
+ tab.OnChangingPinned = OnTabPinned;
+
NotebookChanged?.Invoke (this, EventArgs.Empty);
return tab;
@@ -360,6 +381,24 @@ namespace MonoDevelop.Components.DockNotebook
((DockNotebookTab)pages [n]).Index = n;
}
+ void OnTabPinned (DockNotebookTab sender, bool value)
+ {
+ if (pages.Count == 1)
+ return;
+
+ var stickedPages = pages.Where (p => p.IsPinned);
+ var normalPages = pages.Where (p => !p.IsPinned);
+
+ if (value) {
+ if (stickedPages.Any ())
+ ReorderTab (sender, normalPages.MinValueOrDefault (s => s.Index) ?? stickedPages.MaxValueOrDefault (s => s.Index), false);
+ else
+ ReorderTab (sender, pages.FirstOrDefault (), false);
+ } else {
+ ReorderTab (sender, stickedPages.MaxValueOrDefault (s => s.Index) ?? normalPages.MinValueOrDefault (s => s.Index), false);
+ }
+ }
+
public DockNotebookTab GetTab (int n)
{
if (n < 0 || n >= pages.Count)
@@ -388,8 +427,11 @@ namespace MonoDevelop.Components.DockNotebook
NotebookChanged?.Invoke (this, EventArgs.Empty);
}
- internal void ReorderTab (DockNotebookTab tab, DockNotebookTab targetTab)
+ internal void ReorderTab (DockNotebookTab tab, DockNotebookTab targetTab, bool pinCheck = true)
{
+ if (pinCheck && tab.IsPinned != targetTab.IsPinned)
+ return;
+
if (tab == targetTab)
return;
int targetPos = targetTab.Index;
@@ -412,6 +454,12 @@ namespace MonoDevelop.Components.DockNotebook
TabClosed (this, new TabEventArgs () { Tab = tab });
}
+ internal void OnPinTab (DockNotebookTab tab)
+ {
+ if (TabPinned != null)
+ TabPinned (this, new TabEventArgs () { Tab = tab });
+ }
+
internal void OnActivateTab (DockNotebookTab tab)
{
if (TabActivated != null)
@@ -442,6 +490,12 @@ namespace MonoDevelop.Components.DockNotebook
}
base.OnDestroyed ();
}
+
+ public override void Dispose ()
+ {
+ IdeApp.Preferences.EnablePinnedTabs.Changed -= EnablePinnedTabs_Changed;
+ base.Dispose ();
+ }
}
class DockNotebookChangedArgs : EventArgs
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs
index fbd6a1ed22..731d75ad41 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs
@@ -35,6 +35,9 @@ namespace MonoDevelop.Components.DockNotebook
{
class DockNotebookTab: IAnimatable, IDisposable
{
+ public System.Action<DockNotebookTab, bool> OnChangingPinned;
+ public System.Action<DockNotebookTab, bool> OnChangedPinned;
+
DockNotebook notebook;
readonly TabStrip strip;
@@ -47,6 +50,8 @@ namespace MonoDevelop.Components.DockNotebook
Xwt.Drawing.Image icon;
Widget content;
+ internal Cairo.Rectangle PinButtonActiveArea;
+
Gdk.Rectangle allocation;
internal Gdk.Rectangle Allocation {
get {
@@ -113,6 +118,20 @@ namespace MonoDevelop.Components.DockNotebook
public double DirtyStrength { get; set; }
+ bool isPinned;
+ public bool IsPinned {
+ get { return isPinned; }
+ set {
+ if (isPinned == value)
+ return;
+ if (OnChangingPinned != null)
+ OnChangingPinned (this, value);
+ isPinned = value;
+ if (OnChangedPinned != null)
+ OnChangedPinned (this, value);
+ }
+ }
+
void IAnimatable.BatchBegin () { }
void IAnimatable.BatchCommit () { QueueDraw (); }
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs
index 7f7743b443..e8df4e9be6 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs
@@ -37,6 +37,7 @@ using MonoDevelop.Components.Docking;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Ide;
using System.Runtime.InteropServices;
+using MonoDevelop.Ide.Editor;
namespace MonoDevelop.Components.DockNotebook
{
@@ -48,6 +49,8 @@ namespace MonoDevelop.Components.DockNotebook
static Xwt.Drawing.Image tabBackImage = Xwt.Drawing.Image.FromResource ("tabbar-inactive.9.png");
static Xwt.Drawing.Image tabbarBackImage = Xwt.Drawing.Image.FromResource ("tabbar-back.9.png");
static Xwt.Drawing.Image tabCloseImage = Xwt.Drawing.Image.FromResource ("tab-close-9.png");
+ static Xwt.Drawing.Image tabPinnedImage = Xwt.Drawing.Image.FromResource ("tab-pinned-9.png");
+ static Xwt.Drawing.Image tabUnPinnedImage = Xwt.Drawing.Image.FromResource ("tab-unpinned-9.png");
static Xwt.Drawing.Image tabDirtyImage = Xwt.Drawing.Image.FromResource ("tab-dirty-9.png");
HBox innerBox;
@@ -84,8 +87,11 @@ namespace MonoDevelop.Components.DockNotebook
static readonly int VerticalTextSize = 11;
const int TabSpacing = 0;
const int LeanWidth = 12;
+ const int ButtonSize = 14;
const double CloseButtonMarginRight = 0;
const double CloseButtonMarginBottom = -1.0;
+ const double PinButtonMarginRight = 0;
+ const double PinButtonMarginBottom = -1.0;
const int TextOffset = 1;
@@ -109,6 +115,8 @@ namespace MonoDevelop.Components.DockNotebook
}
}
+ public bool IsPinEnabled { get; set; }
+
public bool NavigationButtonsVisible {
get { return NextButton.Visible; }
set {
@@ -235,7 +243,7 @@ namespace MonoDevelop.Components.DockNotebook
tab.AccessibilityPressCloseButton += OnAccessibilityPressCloseButton;
tab.AccessibilityShowMenu += OnAccessibilityShowMenu;
}
- }
+ }
UpdateAccessibilityTabs ();
notebook.PageAdded += PageAddedHandler;
notebook.PageRemoved += PageRemovedHandler;
@@ -244,6 +252,11 @@ namespace MonoDevelop.Components.DockNotebook
closingTabs = new Dictionary<int, DockNotebookTab> ();
}
+ void EnablePinnedTabs_Changed (object sender, EventArgs e)
+ {
+ IsPinEnabled = IdeApp.Preferences.EnablePinnedTabs;
+ }
+
protected override void OnDestroyed ()
{
this.AbortAnimation ("TabWidth");
@@ -254,14 +267,14 @@ namespace MonoDevelop.Components.DockNotebook
void PageAddedHandler (object sender, TabEventArgs args)
{
- var tab = args.Tab;
+ var tab = args.Tab;
- if (tab.Accessible != null) {
+ if (tab.Accessible != null) {
Accessible.AddAccessibleElement (tab.Accessible);
- Accessible.AddAccessibleElement (tab.CloseButtonAccessible);
-
- tab.AccessibilityPressTab += OnAccessibilityPressTab;
- tab.AccessibilityPressCloseButton += OnAccessibilityPressCloseButton;
+ Accessible.AddAccessibleElement (tab.CloseButtonAccessible);
+
+ tab.AccessibilityPressTab += OnAccessibilityPressTab;
+ tab.AccessibilityPressCloseButton += OnAccessibilityPressCloseButton;
tab.AccessibilityShowMenu += OnAccessibilityShowMenu;
}
@@ -272,13 +285,13 @@ namespace MonoDevelop.Components.DockNotebook
void PageRemovedHandler (object sender, TabEventArgs args)
{
- var tab = args.Tab;
+ var tab = args.Tab;
+
+ if (tab.Accessible != null) {
+ tab.AccessibilityPressTab -= OnAccessibilityPressTab;
+ tab.AccessibilityPressCloseButton -= OnAccessibilityPressCloseButton;
+ tab.AccessibilityShowMenu -= OnAccessibilityShowMenu;
- if (tab.Accessible != null) {
- tab.AccessibilityPressTab -= OnAccessibilityPressTab;
- tab.AccessibilityPressCloseButton -= OnAccessibilityPressCloseButton;
- tab.AccessibilityShowMenu -= OnAccessibilityShowMenu;
-
Accessible.RemoveAccessibleElement (tab.Accessible);
Accessible.RemoveAccessibleElement (tab.CloseButtonAccessible);
}
@@ -290,11 +303,11 @@ namespace MonoDevelop.Components.DockNotebook
UpdateAccessibilityTabs ();
}
- void PageReorderedHandler (DockNotebookTab tab, int oldPlacement, int newPlacement)
+ void PageReorderedHandler (DockNotebookTab tab, int oldPlacement, int newPlacement)
{
QueueResize ();
- UpdateAccessibilityTabs ();
+ UpdateAccessibilityTabs ();
}
void UpdateAccessibilityTabs ()
@@ -553,6 +566,7 @@ namespace MonoDevelop.Components.DockNotebook
return base.OnMotionNotifyEvent (evnt);
}
+ bool overPinOnPress;
bool overCloseOnPress;
bool allowDoubleClick;
@@ -573,6 +587,13 @@ namespace MonoDevelop.Components.DockNotebook
}
overCloseOnPress = false;
+ // Don't select the tab if we are clicking the pin button
+ if (IsOverPinButton (t, (int)evnt.X, (int)evnt.Y)) {
+ overPinOnPress = true;
+ return true;
+ }
+ overPinOnPress = false;
+
if (evnt.Type == EventType.TwoButtonPress) {
if (allowDoubleClick) {
notebook.OnActivateTab (t);
@@ -604,15 +625,22 @@ namespace MonoDevelop.Components.DockNotebook
return base.OnButtonReleaseEvent (evnt);
}
- if (!draggingTab && overCloseOnPress) {
+ if (!draggingTab) {
var t = FindTab ((int)evnt.X, (int)evnt.Y);
- if (t != null && IsOverCloseButton (t, (int)evnt.X, (int)evnt.Y)) {
+ if (t != null && overCloseOnPress && IsOverCloseButton (t, (int)evnt.X, (int)evnt.Y)) {
notebook.OnCloseTab (t);
allowDoubleClick = false;
return true;
+ } else if (IsPinEnabled && t != null && overPinOnPress && IsOverPinButton (t, (int)evnt.X, (int)evnt.Y)) {
+ t.IsPinned = !t.IsPinned;
+ notebook.OnPinTab (t);
+ allowDoubleClick = false;
+ QueueDraw ();
+ return true;
}
}
overCloseOnPress = false;
+ overPinOnPress = false;
allowDoubleClick = true;
if (dragX != 0)
this.Animate ("EndDrag",
@@ -935,6 +963,7 @@ namespace MonoDevelop.Components.DockNotebook
// Cancel drag operations and animations
buttonPressedOnTab = false;
overCloseOnPress = false;
+ overPinOnPress = false;
allowDoubleClick = true;
draggingTab = false;
dragX = 0;
@@ -966,6 +995,11 @@ namespace MonoDevelop.Components.DockNotebook
return tab != null && tab.CloseButtonActiveArea.Contains (x, y);
}
+ static bool IsOverPinButton (DockNotebookTab tab, int x, int y)
+ {
+ return tab != null && tab.PinButtonActiveArea.Contains (x, y);
+ }
+
public void Update ()
{
if (!tracker.Hovered) {
@@ -1145,7 +1179,7 @@ namespace MonoDevelop.Components.DockNotebook
leftPadding = (leftPadding * Math.Min (1.0, Math.Max (0.5, (tabBounds.Width - 30) / 70.0)));
double bottomPadding = active ? TabActivePadding.Bottom : TabPadding.Bottom;
- DrawTabBackground (this, ctx, allocation, tabBounds.Width, tabBounds.X, active);
+ DrawTabBackground (this, ctx, allocation, tabBounds.Width, tabBounds.X, active, IsPinEnabled ? tab.IsPinned : false);
ctx.LineWidth = 1;
ctx.NewPath ();
@@ -1158,9 +1192,17 @@ namespace MonoDevelop.Components.DockNotebook
tab.CloseButtonActiveArea = closeButtonAlloation.Inflate (2, 2);
+ var spinButtonAllocation = new Cairo.Rectangle (closeButtonAlloation.X - rightPadding - PinButtonMarginRight,
+ closeButtonAlloation.Y,
+ tabPinnedImage.Width, tabPinnedImage.Height);
+
+ tab.PinButtonActiveArea = spinButtonAllocation.Inflate (2, 2);
+
bool closeButtonHovered = tracker.Hovered && tab.CloseButtonActiveArea.Contains (tracker.MousePosition);
+ bool pinButtonHovered = tracker.Hovered && tab.PinButtonActiveArea.Contains (tracker.MousePosition);
bool tabHovered = tracker.Hovered && tab.Allocation.Contains (tracker.MousePosition);
- bool drawCloseButton = active || tabHovered || focused;
+ bool drawCloseButton = (IsPinEnabled && tab.IsPinned) || (active || tabHovered || focused);
+ bool drawPinButton = IsPinEnabled && (tab.IsPinned || tabHovered);
if (!closeButtonHovered && tab.DirtyStrength > 0.5) {
ctx.DrawImage (this, tabDirtyImage, closeButtonAlloation.X, closeButtonAlloation.Y);
@@ -1170,11 +1212,17 @@ namespace MonoDevelop.Components.DockNotebook
if (drawCloseButton)
ctx.DrawImage (this, tabCloseImage.WithAlpha ((closeButtonHovered ? 1.0 : 0.5) * tab.Opacity), closeButtonAlloation.X, closeButtonAlloation.Y);
+ if (drawPinButton)
+ ctx.DrawImage (this, (tab.IsPinned ? tabPinnedImage : tabUnPinnedImage).WithAlpha ((pinButtonHovered ? 1.0 : 0.5) * tab.Opacity), spinButtonAllocation.X, spinButtonAllocation.Y);
+
// Render Text
double tw = tabBounds.Width - (leftPadding + rightPadding);
if (drawCloseButton || tab.DirtyStrength > 0.5)
tw -= closeButtonAlloation.Width / 2;
+ if (drawPinButton || tab.DirtyStrength > 0.5)
+ tw -= spinButtonAllocation.Width / 2 + rightPadding;
+
double tx = tabBounds.X + leftPadding;
var baseline = la.GetLine (0).Layout.GetPixelBaseline ();
double ty = tabBounds.Height - bottomPadding - baseline;
@@ -1199,11 +1247,11 @@ namespace MonoDevelop.Components.DockNotebook
ctx.SetSource (lg);
Pango.CairoHelper.ShowLayout (ctx, la.GetLine (0).Layout);
}
- }
+ }
la.Dispose ();
}
- static void DrawTabBackground (Widget widget, Context ctx, Gdk.Rectangle allocation, int contentWidth, int px, bool active = true)
+ static void DrawTabBackground (Widget widget, Context ctx, Gdk.Rectangle allocation, int contentWidth, int px, bool active = true, bool isPinned = false)
{
int lean = Math.Min (LeanWidth, contentWidth / 2);
int halfLean = lean / 2;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs
index c2b43f1433..bc209f4124 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs
@@ -444,4 +444,5 @@ namespace MonoDevelop.Ide.Commands
// MonoDevelop.Ide.Commands.CopyPathNameHandler Implemented in FileTabCommands.cs
// MonoDevelop.Ide.Commands.FileTabCommands.ToggleMaximize Implemented in FileTabCommands.cs
// MonoDevelop.Ide.Commands.FileTabCommands.ReopenClosedTab Implemented in FileTabCommands.cs
+ // MonoDevelop.Ide.Commands.FileTabCommands.CloseAllExceptPinned Implemented in FileTabCommands.cs
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs
index 65ade3f49b..468c7b5850 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs
@@ -39,6 +39,10 @@ using MonoDevelop.Ide.Gui.Dialogs;
using MonoDevelop.Ide.Gui.Documents;
using MonoDevelop.Ide.Gui.Shell;
+using MonoDevelop.Components.DockNotebook;
+using System.Collections.Immutable;
+using MonoDevelop.Core;
+
namespace MonoDevelop.Ide.Commands
{
public enum FileTabCommands
@@ -48,14 +52,26 @@ namespace MonoDevelop.Ide.Commands
CopyPathName,
ToggleMaximize,
ReopenClosedTab,
- CloseAllToTheRight
+ CloseAllToTheRight,
+ CloseAllExceptPinned,
+ PinTab,
}
- class CloseAllHandler : CommandHandler
+ class CloseAllHandler : TabCommandHandler
{
- protected virtual Document GetDocumentException ()
+ protected virtual ImmutableArray<Document> GetDocumentExceptions ()
+ {
+ return ImmutableArray<Document>.Empty;
+ }
+
+ bool HasDistinctViewContent (ImmutableArray<Document> viewContents, Document document)
{
- return null;
+ for (int i = 0; i < viewContents.Length; i++) {
+ if (document.Window.Document == viewContents[i]) {
+ return false;
+ }
+ }
+ return true;
}
protected virtual bool StartAfterException => false;
@@ -67,15 +83,15 @@ namespace MonoDevelop.Ide.Commands
return;
var activeNotebook = ((SdiWorkspaceWindow)active.Window).TabControl;
- var except = GetDocumentException ();
+ var except = GetDocumentExceptions ();
var docs = new List<Document> ();
var dirtyDialogShown = false;
- var startRemoving = except == null || !StartAfterException;
+ var startRemoving = !except.Any () || !StartAfterException;
foreach (var doc in IdeApp.Workbench.Documents) {
if (((SdiWorkspaceWindow)doc.Window).TabControl == activeNotebook) {
- if (except != null && doc == except) {
+ if (except.Any () && !HasDistinctViewContent (except, doc)) {
startRemoving = true;
continue;
}
@@ -102,34 +118,76 @@ namespace MonoDevelop.Ide.Commands
}
}
- class CloseAllButThisHandler : CloseAllHandler
+ abstract class TabCommandHandler : CommandHandler
{
- protected override Document GetDocumentException ()
+ protected DockNotebookTab GetTabFromDocument (Document document)
{
- return IdeApp.Workbench.ActiveDocument;
+ var activeWindow = (SdiWorkspaceWindow)document.Window;
+ var tabControl = activeWindow.TabControl;
+ return tabControl.Tabs.FirstOrDefault (item => (item.Content as SdiWorkspaceWindow).Equals (activeWindow));
+ }
+
+ protected DockNotebookTab GetTabFromActiveDocument ()
+ {
+ var active = IdeApp.Workbench.ActiveDocument;
+ if (active == null)
+ return null;
+ return GetTabFromDocument (active);
}
+ }
+ class CloseAllExceptPinnedHandler : CloseAllHandler
+ {
protected override void Update (CommandInfo info)
{
- var documents = IdeApp.Workbench.Documents;
- var activeDoc = IdeApp.Workbench.ActiveDocument;
+ info.Visible = info.Enabled = IdeApp.Preferences.EnablePinnedTabs && IdeApp.Workbench.Documents.Count != 0;
+ }
- if (activeDoc == null) {
- info.Enabled = false;
+ protected override ImmutableArray<Document> GetDocumentExceptions ()
+ {
+ var active = IdeApp.Workbench.ActiveDocument;
+ if (active == null)
+ return ImmutableArray<Document>.Empty;
+ var activeNotebook = ((SdiWorkspaceWindow)active.Window).TabControl;
+
+ var contents = IdeApp.Workbench.Documents.Where (doc => ((SdiWorkspaceWindow)doc.Window).TabControl == activeNotebook && GetTabFromDocument (doc).IsPinned)
+ .Select (s => s.Window.Document);
+
+ return contents.ToImmutableArray ();
+ }
+ }
+
+ class PinTabHandler : TabCommandHandler
+ {
+ protected override void Update (CommandInfo info)
+ {
+ info.Visible = info.Enabled = IdeApp.Preferences.EnablePinnedTabs && IdeApp.Workbench.ActiveDocument != null;
+ if (!info.Visible)
return;
+
+ var selectedTab = GetTabFromActiveDocument ();
+ if (selectedTab != null) {
+ info.Text = (selectedTab.IsPinned) ? GettextCatalog.GetString ("Un_pin Tab") : GettextCatalog.GetString ("_Pin Tab");
}
+ }
- var activeNotebook = ((SdiWorkspaceWindow)activeDoc.Window).TabControl;
+ protected override void Run ()
+ {
+ var selectedTab = GetTabFromActiveDocument ();
+ if (selectedTab != null)
+ selectedTab.IsPinned = !selectedTab.IsPinned;
+ }
+ }
- // Disable if only document in tab strip
- foreach (var doc in documents) {
- if (doc != activeDoc && ((SdiWorkspaceWindow)doc.Window).TabControl == activeNotebook) {
- info.Enabled = true;
- return;
- }
+ class CloseAllButThisHandler : CloseAllHandler
+ {
+ protected override ImmutableArray<Document> GetDocumentExceptions ()
+ {
+ var active = IdeApp.Workbench.ActiveDocument;
+ if (active == null) {
+ return ImmutableArray<Document>.Empty;
}
-
- info.Enabled = false;
+ return ImmutableArray.Create (active.Window.Document);
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/TabsWindowOptionsPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/TabsWindowOptionsPanel.cs
new file mode 100644
index 0000000000..270addfce6
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/TabsWindowOptionsPanel.cs
@@ -0,0 +1,104 @@
+//
+// TabsWindowOptionsPanel.cs
+//
+// Author:
+// jmedrano <josmed@microsoft.com>
+//
+// Copyright (c) 2019
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Xwt;
+using MonoDevelop.Components;
+using MonoDevelop.Ide.Gui.Dialogs;
+using MonoDevelop.Projects;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Ide.Projects.OptionPanels
+{
+ public class TabsWindowOptionsPanel : ItemOptionsPanel
+ {
+ TabsWindowOptionsWidget widget;
+
+ public TabsWindowOptionsPanel ()
+ {
+ }
+
+ public override void ApplyChanges ()
+ {
+ widget.Store ();
+ }
+
+ public override Control CreatePanelWidget ()
+ {
+ widget = new TabsWindowOptionsWidget (ConfiguredProject, ParentDialog);
+ return new XwtControl (widget);
+ }
+
+ }
+
+ class TabsWindowOptionsWidget : Widget
+ {
+ private readonly Project configuredProject;
+ private readonly OptionsDialog parentDialog;
+ const int margin = 12;
+
+ CheckBox enablePinnedTabsCheckbox;
+
+ public TabsWindowOptionsWidget (Project configuredProject, OptionsDialog parentDialog)
+ {
+ this.configuredProject = configuredProject;
+ this.parentDialog = parentDialog;
+
+ var mainContainer = new VBox ();
+ mainContainer.PackStart (new Label { Markup = string.Format ("<b>{0}</b>", GettextCatalog.GetString ("Pinned Tabs")) });
+
+ enablePinnedTabsCheckbox = new CheckBox () { AllowMixed = false };
+
+ var enableTabsContainer = new HBox ();
+ mainContainer.PackStart (enableTabsContainer);
+ enableTabsContainer.PackStart (enablePinnedTabsCheckbox);
+ enableTabsContainer.PackStart (new Label { Text = GettextCatalog.GetString ("Enable pin a tab in document bar") });
+
+ Content = mainContainer;
+
+ enablePinnedTabsCheckbox.State = IdeApp.Preferences.EnablePinnedTabs.Value ? CheckBoxState.On : CheckBoxState.Off;
+ enablePinnedTabsCheckbox.Toggled += EnablePinnedTabsCheckbox_Toggled;
+
+ Show ();
+ }
+
+ void EnablePinnedTabsCheckbox_Toggled (object sender, EventArgs e)
+ {
+ Store ();
+ }
+
+ internal void Store ()
+ {
+ IdeApp.Preferences.EnablePinnedTabs.Value = enablePinnedTabsCheckbox.State == CheckBoxState.On;
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ enablePinnedTabsCheckbox.Clicked -= EnablePinnedTabsCheckbox_Toggled;
+ base.Dispose (disposing);
+ }
+ }
+
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
index 85a485f1db..192389802d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
@@ -142,6 +142,14 @@
<EmbeddedResource Include="templates\EmptyStruct.xft.xml" />
<EmbeddedResource Include="templates\EmptyTextFile.xft.xml" />
<EmbeddedResource Include="templates\EmptyXMLFile.xft.xml" />
+ <EmbeddedResource Include="icons\tab-pinned-9.png" />
+ <EmbeddedResource Include="icons\tab-pinned-9%402x.png" />
+ <EmbeddedResource Include="icons\tab-pinned-9~dark.png" />
+ <EmbeddedResource Include="icons\tab-pinned-9~dark%402x.png" />
+ <EmbeddedResource Include="icons\tab-unpinned-9.png" />
+ <EmbeddedResource Include="icons\tab-unpinned-9%402x.png" />
+ <EmbeddedResource Include="icons\tab-unpinned-9~dark.png" />
+ <EmbeddedResource Include="icons\tab-unpinned-9~dark%402x.png" />
<EmbeddedResource Include="icons\reference-assembly-16.png" />
<EmbeddedResource Include="icons\reference-assembly-16%402x.png" />
<EmbeddedResource Include="icons\reference-assembly-16~dark.png" />
@@ -4259,6 +4267,7 @@
<Compile Include="MonoDevelop.Components\Mac\NSStackViewExtensions.cs" />
<Compile Include="MonoDevelop.Components\Mac\NSLabel.cs" />
<Compile Include="MonoDevelop.Components\Mac\NSLine.cs" />
+ <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\TabsWindowOptionsPanel.cs" />
</ItemGroup>
<ItemGroup>
<Data Include="options\DefaultEditingLayout.xml" />
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs
index 5984eb5d87..fb663de6b1 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs
@@ -103,6 +103,7 @@ namespace MonoDevelop.Ide
public ConfigurationProperty<TargetRuntime> DefaultTargetRuntime => RootWorkspace.DefaultTargetRuntime;
+ public readonly ConfigurationProperty<bool> EnablePinnedTabs = ConfigurationProperty.Create ("MonoDevelop.TabsAndWindow.EnablePinnedTabs", false);
public readonly ConfigurationProperty<string> UserInterfaceLanguage = Runtime.Preferences.UserInterfaceLanguage;
public readonly ConfigurationProperty<string> UserInterfaceThemeName = ConfigurationProperty.Create ("MonoDevelop.Ide.UserInterfaceTheme", Platform.IsLinux ? "" : "Light");
public readonly ConfigurationProperty<WorkbenchCompactness> WorkbenchCompactness = ConfigurationProperty.Create ("MonoDevelop.Ide.WorkbenchCompactness", MonoDevelop.Ide.WorkbenchCompactness.Normal);
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9.png
new file mode 100644
index 0000000000..243d34e4ba
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9@2x.png
new file mode 100644
index 0000000000..e5322c9a55
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9@2x.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark.png
new file mode 100644
index 0000000000..f8e83d730d
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark@2x.png
new file mode 100644
index 0000000000..de2aa003b7
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark@2x.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9.png
new file mode 100644
index 0000000000..cb7b84b601
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9@2x.png
new file mode 100644
index 0000000000..fe5fe0bb7c
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9@2x.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark.png
new file mode 100644
index 0000000000..3d451b44a2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark.png
Binary files differ
diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark@2x.png
new file mode 100644
index 0000000000..fa4093a8d0
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark@2x.png
Binary files differ