diff --git a/src/Eto.Gtk/Forms/Controls/DocumentPageHandler.cs b/src/Eto.Gtk/Forms/Controls/DocumentPageHandler.cs
index d769a608e..286427366 100644
--- a/src/Eto.Gtk/Forms/Controls/DocumentPageHandler.cs
+++ b/src/Eto.Gtk/Forms/Controls/DocumentPageHandler.cs
@@ -89,6 +89,8 @@ public bool Closable
set { closeButton.Visible = value; }
}
+ public bool HasUnsavedChanges { get; set; }
+
public Image Image
{
get { return image; }
diff --git a/src/Eto/Forms/Controls/DocumentPage.cs b/src/Eto/Forms/Controls/DocumentPage.cs
index b2699d228..0408a7cf3 100644
--- a/src/Eto/Forms/Controls/DocumentPage.cs
+++ b/src/Eto/Forms/Controls/DocumentPage.cs
@@ -1,4 +1,4 @@
-namespace Eto.Forms;
+namespace Eto.Forms;
///
/// Control for a page in a
@@ -77,6 +77,16 @@ public bool Closable
set { Handler.Closable = value; }
}
+ ///
+ /// Gets or sets a value indicating whether this has unsaved changes.
+ ///
+ /// true if page has unsaved changes; otherwise, false.
+ public bool HasUnsavedChanges
+ {
+ get { return Handler.HasUnsavedChanges; }
+ set { Handler.HasUnsavedChanges = value; }
+ }
+
///
/// Gets or sets the image of the page.
///
@@ -113,6 +123,12 @@ public string Text
/// true if closable; otherwise, false.
bool Closable { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this has unsaved changes.
+ ///
+ /// true if page has unsaved changes; otherwise, false.
+ bool HasUnsavedChanges { get; set; }
+
///
/// Gets or sets the image of the page.
///
diff --git a/src/Eto/Forms/ThemedControls/ThemedDocumentControlHandler.cs b/src/Eto/Forms/ThemedControls/ThemedDocumentControlHandler.cs
index b4ba694bd..964344010 100644
--- a/src/Eto/Forms/ThemedControls/ThemedDocumentControlHandler.cs
+++ b/src/Eto/Forms/ThemedControls/ThemedDocumentControlHandler.cs
@@ -33,6 +33,7 @@ public class ThemedDocumentControlHandler : ThemedContainerHandler
+ /// Gets or sets the background color for the unsaved changes indicator.
+ ///
+ /// The background color for the unsaved changes indicator.
+ public Color UnsavedBackgroundColor
+ {
+ get { return unsavedBackgroundColor; }
+ set
+ {
+ unsavedBackgroundColor = value;
+ tabDrawable.Invalidate();
+ }
+ }
+
///
/// Gets or sets a value indicating whether to use a fixed tab height.
///
@@ -298,6 +313,7 @@ public ThemedDocumentControlHandler()
tabForegroundColor = SystemColors.ControlText;
tabHighlightForegroundColor = SystemColors.HighlightText;
tabHoverForegroundColor = SystemColors.HighlightText;
+ unsavedBackgroundColor = SystemColors.ControlText;
tabDrawable = new Drawable();
@@ -680,33 +696,35 @@ void Drawable_Paint(object sender, PaintEventArgs e)
}
}
- void CalculateTab(ThemedDocumentPageHandler tab, int i, ref float posx)
+ void CalculateTab(ThemedDocumentPageHandler tab, int i, ref float posX)
{
- var tabPadding = TabPadding;
- var textSize = string.IsNullOrEmpty(tab.Text) ? Size.Empty : Size.Ceiling(Font.MeasureString(tab.Text));
- var size = textSize;
- var prevnextsel = mousePos.X > nextPrevWidth || i == -1;
- var textoffset = 0;
- if (tab.Image != null)
+ var tabLocation = new PointF(posX, 0);
+ if (i == selectedIndex && draggingLocation != null)
{
- textoffset = maxImageSize.Width + tabPadding.Left;
- size.Width += textoffset;
+ tabLocation.Offset(mousePos.X - draggingLocation.Value.X, 0);
}
- var closesize = (int)Math.Floor(tabDrawable.Height * 0.6);
- var tabRect = new RectangleF(posx, 0, size.Width + (tab.Closable ? closesize + tabPadding.Horizontal + tabPadding.Right : tabPadding.Horizontal), tabDrawable.Height);
+ var imageLocation = new PointF(tabLocation.X + TabPadding.Left, (tabDrawable.Height - maxImageSize.Height) / 2f);
+ var imageSize = tab.Image is null ? Size.Empty : maxImageSize;
+ tab.ImageRect = new RectangleF(imageLocation, imageSize);
- if (i == selectedIndex && draggingLocation != null)
- {
- tabRect.Offset(mousePos.X - draggingLocation.Value.X, 0);
- }
+ var textSize = string.IsNullOrEmpty(tab.Text) ? Size.Empty : Size.Ceiling(Font.MeasureString(tab.Text));
+ var textLocation = new PointF(tab.ImageRect.Right + (imageSize.IsEmpty ? 0 : TabPadding.Left), (tabDrawable.Height - textSize.Height) / 2);
+ tab.TextRect = new RectangleF(textLocation, textSize);
- tab.Rect = tabRect;
+ var unsavedSize = tab.HasUnsavedChanges ? new Size(6, 6) : Size.Empty;
+ var unsavedLocation = new PointF(tab.TextRect.Right + (tab.HasUnsavedChanges ? TabPadding.Right : 0), (tabDrawable.Height - unsavedSize.Height) / 2);
+ tab.UnsavedRect = new RectangleF(unsavedLocation, unsavedSize);
- tab.CloseRect = new RectangleF(tabRect.X + tab.Rect.Width - tabPadding.Right - closesize, (tabDrawable.Height - closesize) / 2, closesize, closesize);
- tab.TextRect = new RectangleF(tabRect.X + tabPadding.Left + textoffset, (tabDrawable.Height - size.Height) / 2, textSize.Width, textSize.Height);
+ var closeSize = tab.Closable
+ ? (int)Math.Floor(tabDrawable.Height * 0.6)
+ : 0;
+ var closeLocation = new PointF(tab.UnsavedRect.Right + (tab.Closable ? TabPadding.Right : 0), (tabDrawable.Height - closeSize) / 2);
+ tab.CloseRect = new RectangleF(closeLocation, new SizeF(closeSize, closeSize));
- posx += tab.Rect.Width;
+ tab.Rect = new RectangleF(tabLocation, new SizeF(tab.CloseRect.Right + TabPadding.Right - tabLocation.X, tabDrawable.Height));
+
+ posX += tab.Rect.Width;
}
bool IsCloseSelected(ThemedDocumentPageHandler tab)
@@ -718,11 +736,6 @@ bool IsCloseSelected(ThemedDocumentPageHandler tab)
void DrawTab(Graphics g, ThemedDocumentPageHandler tab, int i)
{
var prevnextsel = mousePos.X > nextPrevWidth || i == -1;
- var closeSelected = IsCloseSelected(tab);
- var tabRect = tab.Rect;
- var textRect = tab.TextRect;
- var closerect = tab.CloseRect;
- var closemargin = closerect.Height / 4;
var textcolor = Enabled ? TabForegroundColor : DisabledForegroundColor;
var backcolor = TabBackgroundColor;
@@ -731,33 +744,47 @@ void DrawTab(Graphics g, ThemedDocumentPageHandler tab, int i)
textcolor = Enabled ? TabHighlightForegroundColor : DisabledForegroundColor;
backcolor = TabHighlightBackgroundColor;
}
- else if (draggingLocation == null && tabRect.Contains(mousePos) && prevnextsel && Enabled)
+ else if (draggingLocation == null && tab.Rect.Contains(mousePos) && prevnextsel && Enabled)
{
textcolor = TabHoverForegroundColor;
backcolor = TabHoverBackgroundColor;
}
- g.FillRectangle(backcolor, tabRect);
- if (tab.Image != null)
+ g.FillRectangle(backcolor, tab.Rect);
+ g.DrawText(Font, textcolor, tab.TextRect.Location, tab.Text);
+
+ if (tab.Image is not null)
{
g.SaveTransform();
g.ImageInterpolation = ImageInterpolation.High;
- g.DrawImage(tab.Image, tabRect.X + TabPadding.Left, (tabDrawable.Height - maxImageSize.Height) / 2, maxImageSize.Width, maxImageSize.Height);
+ g.DrawImage(tab.Image, tab.ImageRect);
g.RestoreTransform();
}
- g.DrawText(Font, textcolor, textRect.Location, tab.Text);
+
+ if (tab.HasUnsavedChanges)
+ {
+ g.FillEllipse(UnsavedBackgroundColor, tab.UnsavedRect);
+ }
if (tab.Closable)
{
+ var closeSelected = IsCloseSelected(tab);
+
var closeBackground = closeSelected ? CloseHighlightBackgroundColor : CloseBackgroundColor;
if (closeCornerRadius > 0)
- g.FillPath(closeBackground, GraphicsPath.GetRoundRect(closerect, closeCornerRadius));
+ g.FillPath(closeBackground, GraphicsPath.GetRoundRect(tab.CloseRect, closeCornerRadius));
else
- g.FillRectangle(closeBackground, closerect);
+ g.FillRectangle(closeBackground, tab.CloseRect);
+ var closeMargin = (int)tab.CloseRect.Height / 4;
+ var closeForegroundRect = RectangleF.Inset(tab.CloseRect, new PaddingF(closeMargin));
var closeForeground = Enabled ? closeSelected ? CloseHighlightForegroundColor : CloseForegroundColor : DisabledForegroundColor;
- g.DrawLine(closeForeground, closerect.X + closemargin, closerect.Y + closemargin, closerect.X + closerect.Width - 1 - closemargin, closerect.Y + closerect.Height - 1 - closemargin);
- g.DrawLine(closeForeground, closerect.X + closemargin, closerect.Y + closerect.Height - 1 - closemargin, closerect.X + closerect.Width - 1 - closemargin, closerect.Y + closemargin);
+
+ g.SaveTransform();
+ g.PixelOffsetMode = PixelOffsetMode.Half;
+ g.DrawLine(closeForeground, closeForegroundRect.TopLeft, closeForegroundRect.BottomRight);
+ g.DrawLine(closeForeground, closeForegroundRect.TopRight, closeForegroundRect.BottomLeft);
+ g.RestoreTransform();
}
}
diff --git a/src/Eto/Forms/ThemedControls/ThemedDocumentPageHandler.cs b/src/Eto/Forms/ThemedControls/ThemedDocumentPageHandler.cs
index 117986da2..73a1c87a9 100644
--- a/src/Eto/Forms/ThemedControls/ThemedDocumentPageHandler.cs
+++ b/src/Eto/Forms/ThemedControls/ThemedDocumentPageHandler.cs
@@ -9,6 +9,7 @@ public class ThemedDocumentPageHandler : ThemedContainerHandler
/// Initializes a new instance of the class.
@@ -105,6 +106,21 @@ public string Text
}
}
+
+ ///
+ /// Gets or sets a value indicating whether this has unsaved changes.
+ ///
+ /// true if page has unsaved changes; otherwise, false.
+ public bool HasUnsavedChanges
+ {
+ get { return hasUnsavedChanges; }
+ set
+ {
+ hasUnsavedChanges = value;
+ Update();
+ }
+ }
+
///
/// Gets a value indicating whether ///
/// events are propagated to the inner control
@@ -113,9 +129,13 @@ public string Text
internal RectangleF Rect { get; set; }
+ internal RectangleF UnsavedRect { get; set; }
+
internal RectangleF CloseRect { get; set; }
internal RectangleF TextRect { get; set; }
+ internal RectangleF ImageRect { get; set; }
+
void Update() => DocControl?.Update(this);
}
\ No newline at end of file