Skip to content
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: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

name: build
env:
DWAppVersion: v1.7.0r
DWAppVersion: v1.7.1r
permissions:
contents: write

Expand Down
2 changes: 1 addition & 1 deletion DynamicWin/Main/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
public static MMDevice? defaultDevice;
public static MMDevice? defaultMicrophone;

public static string Version => "v1.7.0r";
public static string Version => "v1.7.1r";
public static Channel ReleaseStream => Channel.Release;
public static Architecture ProcessArchitecture => RuntimeInformation.ProcessArchitecture;

Expand Down Expand Up @@ -156,7 +156,7 @@
try
{
SystemEvents.PowerModeChanged -= OnPowerModeChanged;
SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged;

Check warning on line 159 in DynamicWin/Main/App.xaml.cs

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in type of parameter 'sender' of 'void DynamicWinMain.OnDisplaySettingsChanged(object sender, EventArgs e)' doesn't match the target delegate 'EventHandler' (possibly because of nullability attributes).
}
catch { }
}
Expand Down
1,158 changes: 583 additions & 575 deletions DynamicWin/UI/Menu/Menus/SettingsMenu.cs

Large diffs are not rendered by default.

20 changes: 12 additions & 8 deletions DynamicWin/UI/Menu/Menus/SettingsMenuObjects/AddNew.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using DynamicWin.Resources;
using DynamicWin.Resources;
using DynamicWin.UI.UIElements;
using DynamicWin.Utils;
using SkiaSharp;
Expand All @@ -7,26 +7,29 @@ namespace DynamicWin.UI.Menu.Menus.SettingsMenuObjects
{
internal class AddNew : UIObject
{
public AddNew(UIObject? parent, Vec2 position, Vec2 size, UIAlignment alignment = UIAlignment.TopCenter) : base(parent, position, size, alignment)
private static readonly float[] DashIntervals = { 10f, 10f };

public AddNew(UIObject? parent, Vec2 position, Vec2 size, UIAlignment alignment = UIAlignment.TopCenter)
: base(parent, position, size, alignment)
{
UseGpuCaching = false;
Color = Theme.IconColor.Override(a: 0.4f);

AddLocalObject(new DWImage(this, Res.Add, Vec2.zero, new Vec2(15, 15), UIAlignment.Center)
{
Color = Theme.IconColor
});

Color = Theme.IconColor.Override(a: 0.4f);
}

public override void Draw(SKCanvas canvas)
{
var paint = GetPaint();
using var paint = GetPaint();
using var dash = SKPathEffect.CreateDash(DashIntervals, 0f);

var placeRect = new SKRoundRect(SKRect.Create(Position.X, Position.Y, Size.X, Size.Y), 25);
placeRect.Deflate(5, 5);

float[] intervals = { 10, 10 };
paint.PathEffect = SKPathEffect.CreateDash(intervals, 0f);

paint.PathEffect = dash;
paint.IsStroke = true;
paint.StrokeCap = SKStrokeCap.Round;
paint.StrokeJoin = SKStrokeJoin.Round;
Expand All @@ -35,6 +38,7 @@ public override void Draw(SKCanvas canvas)
canvas.DrawRoundRect(placeRect, paint);

placeRect.Deflate(5f, 5f);
paint.PathEffect = null;
paint.Color = Color.Override(a: 0.05f).Value();
paint.IsStroke = false;

Expand Down
227 changes: 151 additions & 76 deletions DynamicWin/UI/Menu/Menus/SettingsMenuObjects/BigWidgetAdder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using DynamicWin.Main;
using DynamicWin.Main;
using DynamicWin.Resources;
using DynamicWin.UI.Widgets;
using DynamicWin.Utils;
Expand All @@ -8,134 +8,209 @@ namespace DynamicWin.UI.Menu.Menus.SettingsMenuObjects
{
internal class BigWidgetAdder : UIObject
{
AddNew addNew;

public BigWidgetAdder(UIObject? parent, Vec2 position, Vec2 size, UIAlignment alignment = UIAlignment.TopCenter) : base(parent, position, size, alignment)
private const float RowHeight = 45f;
private const int Columns = 2;
private const float AnimationSpeed = 15f;
private const float Epsilon = 0.05f;

private readonly AddNew addNew;
private readonly List<BigWidgetAdderDisplay> displays = new List<BigWidgetAdderDisplay>();

private float targetHeight;
private float targetAddX;
private float targetAddY;
private float targetAddWidth;
private bool layoutDirty = true;

public BigWidgetAdder(UIObject? parent, Vec2 position, Vec2 size, UIAlignment alignment = UIAlignment.TopCenter)
: base(parent, position, size, alignment)
{
UseGpuCaching = false;
Color = Theme.WidgetBackground.Override(a: 0.1f);
roundRadius = 20;

Anchor.Y = 0;

addNew = new AddNew(this, Vec2.zero, new Vec2(size.X, 45), UIAlignment.BottomLeft);
addNew = new AddNew(this, Vec2.zero, new Vec2(size.X, RowHeight), UIAlignment.BottomLeft);
addNew.Anchor.Y = 0;
AddLocalObject(addNew);

UpdateWidgetDisplay();
RecalculateLayout();
ApplyLayoutTargets(immediate: true);
}

public override bool WantsRealtimeUpdate
{
get
{
return Math.Abs(addNew.LocalPosition.X - targetAddX) > Epsilon
|| Math.Abs(addNew.LocalPosition.Y - targetAddY) > Epsilon
|| Math.Abs(addNew.Size.X - targetAddWidth) > Epsilon
|| Math.Abs(Size.Y - targetHeight) > Epsilon;
}
}

public override void Update(float deltaTime)
{
base.Update(deltaTime);

int line = (int)(Math.Floor(displays.Count / maxE));
if (layoutDirty)
RecalculateLayout();

addNew.LocalPosition.Y = Mathf.Lerp(addNew.LocalPosition.Y, -line * 45 - 45, 15f * deltaTime);
addNew.LocalPosition.X = Mathf.Lerp(addNew.LocalPosition.X, isDisplayEven() ? Size.X / 2f : Size.X / 1.3333333f, 15f * deltaTime);
addNew.Size.X = Mathf.Lerp(addNew.Size.X, isDisplayEven() ? Size.X : Size.X / 2, 15f * deltaTime);

var lines2 = (int)Math.Max(1, (displays.Count / maxE + 1));
Size.Y = Mathf.Lerp(Size.Y, lines2 * 45, 15f * RendererMain.Instance.DeltaTime);
ApplyLayoutTargets(immediate: false, deltaTime);
}

bool isDisplayEven()
public override ContextMenu? GetContextMenu()
{
return displays.Count % 2 == 0;
}

List<BigWidgetAdderDisplay> displays = new List<BigWidgetAdderDisplay>();
float maxE = 2;
var ctx = new ContextMenu();
bool anyWidgetsLeft = false;

void UpdateWidgetDisplay()
{
displays.ForEach((x) => DestroyLocalObject(x));
displays.Clear();
foreach (var availableWidget in Res.availableBigWidgets)
{
string? fullName = availableWidget.GetType().FullName;
if (string.IsNullOrEmpty(fullName) || Settings.bigWidgets.Contains(fullName))
continue;

Dictionary<string, IRegisterableWidget> bigWidgets = new Dictionary<string, IRegisterableWidget>();
anyWidgetsLeft = true;

var item = new MenuItem() { Header = $"{GetWidgetSourceName(availableWidget)}: {availableWidget.WidgetName}" };
item.Click += (x, y) =>
{
Settings.bigWidgets.Add(fullName);
UpdateWidgetDisplay();
};

foreach (var widget in Res.availableBigWidgets)
{
if (bigWidgets.ContainsKey(widget.GetType().FullName)) continue;
bigWidgets.Add(widget.GetType().FullName, widget);
System.Diagnostics.Debug.WriteLine(widget.GetType().FullName);
ctx.Items.Add(item);
}

int c = 0;
foreach (var bigWidget in Settings.bigWidgets)
if (anyWidgetsLeft)
return ctx;

var empty = new ContextMenu();
empty.Items.Add(new MenuItem()
{
if (!bigWidgets.ContainsKey(bigWidget)) continue;
Header = "No widgets available.",
IsEnabled = false
});
return empty;
}

var widget = bigWidgets[bigWidget.ToString()];
private void UpdateWidgetDisplay()
{
for (int i = displays.Count - 1; i >= 0; i--)
DestroyLocalObject(displays[i]);

displays.Clear();

var bigWidgets = Res.availableBigWidgets
.Where(widget => !string.IsNullOrEmpty(widget.GetType().FullName))
.GroupBy(widget => widget.GetType().FullName!)
.ToDictionary(group => group.Key, group => group.First());

for (int i = 0; i < Settings.bigWidgets.Count; i++)
{
string bigWidget = Settings.bigWidgets[i];
if (!bigWidgets.TryGetValue(bigWidget, out var widget))
continue;

string capturedWidget = bigWidget;
var display = new BigWidgetAdderDisplay(this, widget.WidgetName, UIAlignment.BottomLeft);

display.onEditRemoveWidget += () => {
Settings.bigWidgets.Remove(bigWidget);
display.onEditRemoveWidget += () =>
{
Settings.bigWidgets.Remove(capturedWidget);
UpdateWidgetDisplay();
};

display.onEditMoveWidgetRight += () => {
int index = Math.Clamp(Settings.bigWidgets.IndexOf(bigWidget) + 1, 0, Settings.bigWidgets.Count - 1);
Settings.bigWidgets.Remove(bigWidget);

Settings.bigWidgets.Insert(index, bigWidget);
display.onEditMoveWidgetRight += () =>
{
MoveWidget(capturedWidget, 1);
UpdateWidgetDisplay();
};

display.onEditMoveWidgetLeft += () => {
int index = Math.Clamp(Settings.bigWidgets.IndexOf(bigWidget) - 1, 0, Settings.bigWidgets.Count - 1);
Settings.bigWidgets.Remove(bigWidget);

Settings.bigWidgets.Insert(index, bigWidget);
display.onEditMoveWidgetLeft += () =>
{
MoveWidget(capturedWidget, -1);
UpdateWidgetDisplay();
};

int line = (int)(c / maxE);

display.LocalPosition.X = (c % 2) * Size.X / 2;
display.LocalPosition.Y -= 45 + line * 45;

displays.Add(display);
AddLocalObject(display);

c++;
}

layoutDirty = true;
}

public override ContextMenu? GetContextMenu()
private void RecalculateLayout()
{
var ctx = new System.Windows.Controls.ContextMenu();
bool anyWidgetsLeft = false;

foreach (var availableWidget in Res.availableBigWidgets)
for (int i = 0; i < displays.Count; i++)
{
if (Settings.bigWidgets.Contains(availableWidget.GetType().FullName)) continue;
int row = i / Columns;
int column = i % Columns;

anyWidgetsLeft = true;
var display = displays[i];
display.Size = new Vec2(Size.X / Columns, RowHeight);
display.LocalPosition.X = column * Size.X / Columns;
display.LocalPosition.Y = -RowHeight - row * RowHeight;
}

var item = new MenuItem() { Header = availableWidget.GetType().Namespace.Split('.')[0] + ": " + availableWidget.WidgetName };
item.Click += (x, y) =>
{
Settings.bigWidgets.Add(availableWidget.GetType().FullName);
UpdateWidgetDisplay();
};
int addRow = displays.Count / Columns;
bool evenDisplayCount = displays.Count % Columns == 0;

ctx.Items.Add(item);
}
targetAddY = -RowHeight - addRow * RowHeight;
targetAddX = evenDisplayCount ? Size.X / 2f : Size.X * 0.75f;
targetAddWidth = evenDisplayCount ? Size.X : Size.X / Columns;
targetHeight = Math.Max(RowHeight, (addRow + 1) * RowHeight);

if (!anyWidgetsLeft)
layoutDirty = false;
}

private void ApplyLayoutTargets(bool immediate, float deltaTime = 0f)
{
if (immediate)
{
var ctx2 = new ContextMenu();
ctx2.Items.Add(new MenuItem()
{
Header = "No widgets available.",
IsEnabled = false
});
return ctx2;
addNew.LocalPosition.X = targetAddX;
addNew.LocalPosition.Y = targetAddY;
addNew.Size = new Vec2(targetAddWidth, RowHeight);
Size = new Vec2(Size.X, targetHeight);
return;
}

return ctx;
addNew.LocalPosition.X = Smooth(addNew.LocalPosition.X, targetAddX, AnimationSpeed, deltaTime, Epsilon);
addNew.LocalPosition.Y = Smooth(addNew.LocalPosition.Y, targetAddY, AnimationSpeed, deltaTime, Epsilon);
addNew.Size = new Vec2(Smooth(addNew.Size.X, targetAddWidth, AnimationSpeed, deltaTime, Epsilon), RowHeight);
Size = new Vec2(Size.X, Smooth(Size.Y, targetHeight, AnimationSpeed, deltaTime, Epsilon));
}

private static void MoveWidget(string widget, int direction)
{
int currentIndex = Settings.bigWidgets.IndexOf(widget);
if (currentIndex < 0)
return;

int nextIndex = Math.Clamp(currentIndex + direction, 0, Settings.bigWidgets.Count - 1);
if (nextIndex == currentIndex)
return;

Settings.bigWidgets.RemoveAt(currentIndex);
Settings.bigWidgets.Insert(nextIndex, widget);
}

private static string GetWidgetSourceName(IRegisterableWidget widget)
{
string? widgetNamespace = widget.GetType().Namespace;
if (string.IsNullOrEmpty(widgetNamespace))
return "DynamicWin";

return widgetNamespace.Split('.')[0];
}

private static float Smooth(float current, float target, float speed, float deltaTime, float epsilon)
{
if (Math.Abs(current - target) <= epsilon)
return target;

return Mathf.Lerp(current, target, speed * deltaTime);
}
}
}
Loading
Loading