Skip to content

Commit

Permalink
Merge pull request #2626 from ievgen-baida/feature/unsaved-indictator…
Browse files Browse the repository at this point in the history
…-for-document-control

Unsaved indictator for DocumentControl
  • Loading branch information
cwensley authored Mar 1, 2024
2 parents 27238c3 + 59136f2 commit da62e2c
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 34 deletions.
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);
}

0 comments on commit da62e2c

Please sign in to comment.