Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsaved indicator for DocumentControl #2626

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Eto.Gtk/Forms/Controls/DocumentPageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public bool Closable
set { closeButton.Visible = value; }
}

public bool HasUnsavedChanges { get; set; }

public Image Image
{
get { return image; }
Expand Down
18 changes: 17 additions & 1 deletion src/Eto/Forms/Controls/DocumentPage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Eto.Forms;
namespace Eto.Forms;

/// <summary>
/// Control for a page in a <see cref="DocumentControl"/>
Expand Down Expand Up @@ -77,6 +77,16 @@ public bool Closable
set { Handler.Closable = value; }
}

/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:Eto.Forms.DocumentPage"/> has unsaved changes.
/// </summary>
/// <value><c>true</c> if page has unsaved changes; otherwise, <c>false</c>.</value>
public bool HasUnsavedChanges
{
get { return Handler.HasUnsavedChanges; }
set { Handler.HasUnsavedChanges = value; }
}

/// <summary>
/// Gets or sets the image of the page.
/// </summary>
Expand Down Expand Up @@ -113,6 +123,12 @@ public string Text
/// <value><c>true</c> if closable; otherwise, <c>false</c>.</value>
bool Closable { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:Eto.Forms.DocumentPage"/> has unsaved changes.
/// </summary>
/// <value><c>true</c> if page has unsaved changes; otherwise, <c>false</c>.</value>
bool HasUnsavedChanges { get; set; }

/// <summary>
/// Gets or sets the image of the page.
/// </summary>
Expand Down
93 changes: 60 additions & 33 deletions src/Eto/Forms/ThemedControls/ThemedDocumentControlHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class ThemedDocumentControlHandler : ThemedContainerHandler<TableLayout,
Color tabForegroundColor;
Color tabHighlightForegroundColor;
Color tabHoverForegroundColor;
Color unsavedBackgroundColor;

int closeCornerRadius;

Expand Down Expand Up @@ -259,6 +260,20 @@ public Color TabHoverForegroundColor
}
}

/// <summary>
/// Gets or sets the background color for the unsaved changes indicator.
/// </summary>
/// <value>The background color for the unsaved changes indicator.</value>
public Color UnsavedBackgroundColor
{
get { return unsavedBackgroundColor; }
set
{
unsavedBackgroundColor = value;
tabDrawable.Invalidate();
}
}

/// <summary>
/// Gets or sets a value indicating whether to use a fixed tab height.
/// </summary>
Expand Down Expand Up @@ -298,6 +313,7 @@ public ThemedDocumentControlHandler()
tabForegroundColor = SystemColors.ControlText;
tabHighlightForegroundColor = SystemColors.HighlightText;
tabHoverForegroundColor = SystemColors.HighlightText;
unsavedBackgroundColor = SystemColors.ControlText;

tabDrawable = new Drawable();

Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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();
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/Eto/Forms/ThemedControls/ThemedDocumentPageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class ThemedDocumentPageHandler : ThemedContainerHandler<Panel, DocumentP
bool closable;
string text;
Image image;
bool hasUnsavedChanges;

/// <summary>
/// Initializes a new instance of the <see cref="T:Eto.Forms.ThemedControls.ThemedDocumentPageHandler"/> class.
Expand Down Expand Up @@ -105,6 +106,21 @@ public string Text
}
}


/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:Eto.Forms.ThemedControls.ThemedDocumentPageHandler"/> has unsaved changes.
/// </summary>
/// <value><c>true</c> if page has unsaved changes; otherwise, <c>false</c>.</value>
public bool HasUnsavedChanges
{
get { return hasUnsavedChanges; }
set
{
hasUnsavedChanges = value;
Update();
}
}

/// <summary>
/// Gets a value indicating whether <see cref="Control.PreLoad"/>/<see cref="Control.Load"/>/<see cref="Control.LoadComplete"/>/<see cref="Control.UnLoad"/>
/// events are propagated to the inner control
Expand All @@ -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);
}
Loading