mirror of
https://github.com/Leonmmcoset/CMLeonOS.git
synced 2026-04-21 19:24:00 +00:00
377 lines
11 KiB
C#
377 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using CMLeonOS.UILib.Animations;
|
|
|
|
namespace CMLeonOS.Gui.UILib
|
|
{
|
|
internal class Dropdown : Control
|
|
{
|
|
internal Dropdown(Window parent, int x, int y, int width, int height) : base(parent, x, y, width, height)
|
|
{
|
|
hostWindow = parent;
|
|
OnClick = (_, _) => ToggleExpanded();
|
|
OnUnfocused = Collapse;
|
|
}
|
|
|
|
private readonly Window hostWindow;
|
|
private Window popupWindow;
|
|
private Table popupTable;
|
|
private bool closeAnimationRunning = false;
|
|
|
|
internal Action<int, string> SelectionChanged;
|
|
|
|
internal List<string> Items { get; } = new List<string>();
|
|
|
|
private string _placeholderText = "Select";
|
|
internal string PlaceholderText
|
|
{
|
|
get
|
|
{
|
|
return _placeholderText;
|
|
}
|
|
set
|
|
{
|
|
_placeholderText = value ?? string.Empty;
|
|
Render();
|
|
}
|
|
}
|
|
|
|
private Color _background = UITheme.Surface;
|
|
internal Color Background
|
|
{
|
|
get
|
|
{
|
|
return _background;
|
|
}
|
|
set
|
|
{
|
|
_background = value;
|
|
Render();
|
|
}
|
|
}
|
|
|
|
private Color _foreground = UITheme.TextPrimary;
|
|
internal Color Foreground
|
|
{
|
|
get
|
|
{
|
|
return _foreground;
|
|
}
|
|
set
|
|
{
|
|
_foreground = value;
|
|
Render();
|
|
}
|
|
}
|
|
|
|
private Color _border = UITheme.SurfaceBorder;
|
|
internal Color Border
|
|
{
|
|
get
|
|
{
|
|
return _border;
|
|
}
|
|
set
|
|
{
|
|
_border = value;
|
|
Render();
|
|
}
|
|
}
|
|
|
|
private Color _highlight = UITheme.Accent;
|
|
internal Color Highlight
|
|
{
|
|
get
|
|
{
|
|
return _highlight;
|
|
}
|
|
set
|
|
{
|
|
_highlight = value;
|
|
Render();
|
|
}
|
|
}
|
|
|
|
private int _selectedIndex = -1;
|
|
internal int SelectedIndex
|
|
{
|
|
get
|
|
{
|
|
return _selectedIndex;
|
|
}
|
|
set
|
|
{
|
|
int newIndex = value;
|
|
if (newIndex < -1)
|
|
{
|
|
newIndex = -1;
|
|
}
|
|
if (newIndex >= Items.Count)
|
|
{
|
|
newIndex = Items.Count - 1;
|
|
}
|
|
|
|
if (_selectedIndex != newIndex)
|
|
{
|
|
_selectedIndex = newIndex;
|
|
Render();
|
|
SyncPopupSelection();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal string SelectedText
|
|
{
|
|
get
|
|
{
|
|
if (_selectedIndex < 0 || _selectedIndex >= Items.Count)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return Items[_selectedIndex];
|
|
}
|
|
}
|
|
|
|
internal bool Expanded { get; private set; } = false;
|
|
|
|
internal int MaxVisibleItems { get; set; } = 6;
|
|
|
|
internal void RefreshItems()
|
|
{
|
|
if (_selectedIndex >= Items.Count)
|
|
{
|
|
_selectedIndex = Items.Count - 1;
|
|
}
|
|
|
|
RebuildPopup();
|
|
Render();
|
|
}
|
|
|
|
private void ToggleExpanded()
|
|
{
|
|
if (Expanded)
|
|
{
|
|
Collapse();
|
|
}
|
|
else
|
|
{
|
|
Expand();
|
|
}
|
|
}
|
|
|
|
private void Expand()
|
|
{
|
|
if (Expanded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Expanded = true;
|
|
RebuildPopup();
|
|
Render();
|
|
}
|
|
|
|
internal void Collapse()
|
|
{
|
|
if (!Expanded || closeAnimationRunning)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (popupWindow == null)
|
|
{
|
|
Expanded = false;
|
|
Render();
|
|
return;
|
|
}
|
|
|
|
closeAnimationRunning = true;
|
|
|
|
int currentHeight = popupWindow.Height;
|
|
MovementAnimation windowAnimation = new MovementAnimation(popupWindow)
|
|
{
|
|
From = new Rectangle(popupWindow.X, popupWindow.Y, popupWindow.Width, currentHeight),
|
|
To = new Rectangle(popupWindow.X, popupWindow.Y, popupWindow.Width, 1),
|
|
Duration = 8,
|
|
EasingType = EasingType.Sine,
|
|
EasingDirection = EasingDirection.In
|
|
};
|
|
MovementAnimation tableAnimation = null;
|
|
if (popupTable != null)
|
|
{
|
|
tableAnimation = new MovementAnimation(popupTable)
|
|
{
|
|
From = new Rectangle(popupTable.X, popupTable.Y, popupTable.Width, currentHeight),
|
|
To = new Rectangle(popupTable.X, popupTable.Y, popupTable.Width, 1),
|
|
Duration = 8,
|
|
EasingType = EasingType.Sine,
|
|
EasingDirection = EasingDirection.In
|
|
};
|
|
}
|
|
|
|
windowAnimation.Completed = () =>
|
|
{
|
|
if (popupTable != null)
|
|
{
|
|
WM.RemoveWindow(popupTable);
|
|
popupTable = null;
|
|
}
|
|
if (popupWindow != null)
|
|
{
|
|
WM.RemoveWindow(popupWindow);
|
|
popupWindow = null;
|
|
}
|
|
|
|
Expanded = false;
|
|
closeAnimationRunning = false;
|
|
Render();
|
|
};
|
|
windowAnimation.Start();
|
|
tableAnimation?.Start();
|
|
}
|
|
|
|
private void PopupSelected(int index)
|
|
{
|
|
if (index < 0 || index >= Items.Count)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_selectedIndex = index;
|
|
Collapse();
|
|
Render();
|
|
SelectionChanged?.Invoke(_selectedIndex, Items[_selectedIndex]);
|
|
}
|
|
|
|
private void SyncPopupSelection()
|
|
{
|
|
if (popupTable != null)
|
|
{
|
|
popupTable.SelectedCellIndex = _selectedIndex;
|
|
}
|
|
}
|
|
|
|
private void RebuildPopup()
|
|
{
|
|
if (!Expanded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (popupTable != null)
|
|
{
|
|
WM.RemoveWindow(popupTable);
|
|
popupTable = null;
|
|
}
|
|
if (popupWindow != null)
|
|
{
|
|
WM.RemoveWindow(popupWindow);
|
|
popupWindow = null;
|
|
}
|
|
|
|
int visibleItems = Math.Max(1, Math.Min(MaxVisibleItems, Math.Max(1, Items.Count)));
|
|
int popupHeight = Math.Max(24, visibleItems * 22);
|
|
|
|
popupWindow = new Window(Process, hostWindow, X, Y + Height, Width, 1);
|
|
popupWindow.Clear(UITheme.Surface);
|
|
WM.AddWindow(popupWindow);
|
|
|
|
popupTable = new Table(popupWindow, 0, 0, popupWindow.Width, popupHeight);
|
|
popupTable.AllowDeselection = false;
|
|
popupTable.CellHeight = 22;
|
|
popupTable.Background = UITheme.Surface;
|
|
popupTable.Foreground = Foreground;
|
|
popupTable.Border = Border;
|
|
popupTable.SelectedBackground = UITheme.AccentLight;
|
|
popupTable.SelectedBorder = Highlight;
|
|
popupTable.SelectedForeground = Foreground;
|
|
popupTable.TableCellSelected = PopupSelected;
|
|
|
|
for (int i = 0; i < Items.Count; i++)
|
|
{
|
|
popupTable.Cells.Add(new TableCell(Items[i], i));
|
|
}
|
|
|
|
popupTable.SelectedCellIndex = _selectedIndex;
|
|
popupTable.Render();
|
|
WM.AddWindow(popupTable);
|
|
|
|
MovementAnimation windowAnimation = new MovementAnimation(popupWindow)
|
|
{
|
|
From = new Rectangle(popupWindow.X, popupWindow.Y, popupWindow.Width, 1),
|
|
To = new Rectangle(popupWindow.X, popupWindow.Y, popupWindow.Width, popupHeight),
|
|
Duration = 8,
|
|
EasingType = EasingType.Sine,
|
|
EasingDirection = EasingDirection.Out
|
|
};
|
|
MovementAnimation tableAnimation = new MovementAnimation(popupTable)
|
|
{
|
|
From = new Rectangle(popupTable.X, popupTable.Y, popupTable.Width, 1),
|
|
To = new Rectangle(popupTable.X, popupTable.Y, popupTable.Width, popupHeight),
|
|
Duration = 8,
|
|
EasingType = EasingType.Sine,
|
|
EasingDirection = EasingDirection.Out
|
|
};
|
|
|
|
windowAnimation.Completed = () =>
|
|
{
|
|
if (popupWindow != null)
|
|
{
|
|
popupWindow.Clear(UITheme.Surface);
|
|
popupWindow.DrawRectangle(0, 0, popupWindow.Width, popupWindow.Height, Border);
|
|
WM.Update(popupWindow);
|
|
}
|
|
if (popupTable != null)
|
|
{
|
|
popupTable.MoveAndResize(0, 0, popupWindow.Width, popupWindow.Height, sendWMEvent: false);
|
|
popupTable.Render();
|
|
}
|
|
};
|
|
windowAnimation.Start();
|
|
tableAnimation.Start();
|
|
}
|
|
|
|
internal override void Render()
|
|
{
|
|
Clear(Background);
|
|
DrawFilledRectangle(0, 0, Width, Height, Background);
|
|
|
|
Color frame = Expanded ? Highlight : Border;
|
|
DrawRectangle(0, 0, Width, Height, frame);
|
|
|
|
string displayText = SelectedIndex >= 0 ? SelectedText : PlaceholderText;
|
|
Color textColor = SelectedIndex >= 0 ? Foreground : UITheme.TextSecondary;
|
|
|
|
int maxChars = Math.Max(1, (Width - 24) / 8);
|
|
if (displayText.Length > maxChars)
|
|
{
|
|
displayText = displayText.Substring(0, Math.Max(1, maxChars - 1)) + "~";
|
|
}
|
|
|
|
DrawString(displayText, textColor, 6, (Height / 2) - 8);
|
|
|
|
int arrowCenterX = Width - 12;
|
|
int arrowCenterY = Height / 2;
|
|
DrawLine(arrowCenterX - 4, arrowCenterY - 2, arrowCenterX, arrowCenterY + 2, textColor);
|
|
DrawLine(arrowCenterX, arrowCenterY + 2, arrowCenterX + 4, arrowCenterY - 2, textColor);
|
|
|
|
if (Expanded && popupWindow != null)
|
|
{
|
|
popupWindow.MoveAndResize(X, Y + Height, Width, popupWindow.Height, sendWMEvent: false);
|
|
if (popupTable != null)
|
|
{
|
|
popupTable.MoveAndResize(0, 0, popupWindow.Width, popupWindow.Height, sendWMEvent: false);
|
|
popupTable.Render();
|
|
}
|
|
popupWindow.Clear(UITheme.Surface);
|
|
popupWindow.DrawRectangle(0, 0, popupWindow.Width, popupWindow.Height, Border);
|
|
WM.Update(popupWindow);
|
|
}
|
|
|
|
WM.Update(this);
|
|
}
|
|
}
|
|
}
|