Files
CMLeonOS/Gui/UILib/Table.cs
2026-03-01 17:03:49 +08:00

394 lines
12 KiB
C#

using Cosmos.System;
using Cosmos.System.Graphics;
using CMLeonOS.Gui.SmoothMono;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace CMLeonOS.Gui.UILib
{
internal class Table : Control
{
public Table(Window parent, int x, int y, int width, int height) : base(parent, x, y, width, height)
{
OnDown = TableDown;
}
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.ScrollbarUp.bmp")]
private static byte[] scrollbarUpBytes;
private static Bitmap scrollbarUpBitmap = new Bitmap(scrollbarUpBytes);
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.ScrollbarDown.bmp")]
private static byte[] scrollbarDownBytes;
private static Bitmap scrollbarDownBitmap = new Bitmap(scrollbarDownBytes);
internal List<TableCell> Cells { get; set; } = new List<TableCell>();
internal Action<int> TableCellSelected { get; set; }
internal bool AllowDeselection { get; set; } = true;
internal bool AllowSelection { get; set; } = true;
private double scrollY = 0;
private bool dragging = false;
private int lastDragY = 0;
private int _selectedCellIndex = -1;
internal int SelectedCellIndex
{
get
{
return _selectedCellIndex;
}
set
{
if (_selectedCellIndex != value)
{
_selectedCellIndex = value;
Render();
}
}
}
private int _scrollbarThickness = 20;
internal int ScrollbarThickness
{
get
{
return _scrollbarThickness;
}
set
{
_scrollbarThickness = value;
Render();
}
}
private Color _background = Color.LightGray;
internal Color Background
{
get
{
return _background;
}
set
{
_background = value;
Render();
}
}
private Color _foreground = Color.Black;
internal Color Foreground
{
get
{
return _foreground;
}
set
{
_foreground = value;
Render();
}
}
private Color _border = Color.Gray;
internal Color Border
{
get
{
return _border;
}
set
{
_border = value;
Render();
}
}
private Color _selectedBackground = Color.FromArgb(221, 246, 255);
internal Color SelectedBackground
{
get
{
return _selectedBackground;
}
set
{
_selectedBackground = value;
Render();
}
}
private Color _selectedForeground = Color.Black;
internal Color SelectedForeground
{
get
{
return _selectedForeground;
}
set
{
_selectedForeground = value;
Render();
}
}
private Color _selectedBorder = Color.FromArgb(126, 205, 234);
internal Color SelectedBorder
{
get
{
return _selectedBorder;
}
set
{
_selectedBorder = value;
Render();
}
}
private int _cellHeight = 20;
internal int CellHeight
{
get
{
return _cellHeight;
}
set
{
_cellHeight = value;
Render();
}
}
private Alignment _textAlignment = Alignment.Start;
internal Alignment TextAlignment
{
get
{
return _textAlignment;
}
set
{
_textAlignment = value;
Render();
}
}
private void TableDown(int x, int y)
{
if ((CanScrollUp || CanScrollDown) && x >= (Width - _scrollbarThickness))
{
int allCellsHeight = Cells.Count * CellHeight;
if (y < _scrollbarThickness && CanScrollUp)
{
scrollY = Math.Max(0, scrollY - CellHeight);
Render();
}
if (y >= Height - _scrollbarThickness && CanScrollDown)
{
scrollY = Math.Min(allCellsHeight - Height, scrollY + CellHeight);
Render();
}
if (y < Height - _scrollbarThickness && y >= _scrollbarThickness)
{
dragging = true;
lastDragY = (int)MouseManager.Y;
Render();
}
return;
}
int scrollAdjustedY = (int)(y + scrollY);
if (scrollAdjustedY < 0 || scrollAdjustedY > _cellHeight * Cells.Count)
{
if (AllowDeselection)
{
SelectedCellIndex = -1;
}
return;
}
if (AllowSelection)
{
SelectedCellIndex = scrollAdjustedY / _cellHeight;
TableCellSelected?.Invoke(_selectedCellIndex);
}
}
private bool CanScrollUp
{
get
{
return scrollY > 0;
}
}
private bool CanScrollDown
{
get
{
int allCellsHeight = Cells.Count * CellHeight;
return (scrollY < 0) || ((allCellsHeight > Height) && (scrollY < (allCellsHeight - Height)));
}
}
private void RenderScrollbar()
{
if (CanScrollUp || CanScrollDown)
{
/* Background */
DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, Height, _border);
/* Track */
int trackAvailableHeight = Height - (ScrollbarThickness * 2);
double trackSize = (double)Height / (double)(Cells.Count * CellHeight);
double trackProgress = (double)scrollY / (double)((Cells.Count * CellHeight) - Height);
int trackY = (int)(_scrollbarThickness + (((double)trackAvailableHeight - ((double)trackAvailableHeight * trackSize)) * trackProgress));
// Border
DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, Height, _border);
// Background
DrawFilledRectangle(Width - _scrollbarThickness + 1, trackY + 1, _scrollbarThickness - 2, (int)(trackSize * trackAvailableHeight) - 2, _background);
/* Up arrow */
// Border
DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, _scrollbarThickness, _border);
// Background
DrawFilledRectangle(Width - _scrollbarThickness + 1, 1, _scrollbarThickness - 2, _scrollbarThickness - 2, CanScrollUp ? _background : _border);
DrawImageAlpha(scrollbarUpBitmap, (int)((Width - _scrollbarThickness) + ((_scrollbarThickness / 2) - (scrollbarUpBitmap.Width / 2))), (int)((_scrollbarThickness / 2) - (scrollbarUpBitmap.Height / 2)));
/* Down arrow */
// Border
DrawFilledRectangle(Width - _scrollbarThickness, Height - _scrollbarThickness, _scrollbarThickness, _scrollbarThickness, _border);
// Background
DrawFilledRectangle(Width - _scrollbarThickness + 1, Height - _scrollbarThickness + 1, _scrollbarThickness - 2, _scrollbarThickness - 2, CanScrollDown ? _background : _border);
DrawImageAlpha(scrollbarDownBitmap, (int)((Width - _scrollbarThickness) + ((_scrollbarThickness / 2) - (scrollbarUpBitmap.Width / 2))), (int)((Height - _scrollbarThickness) + ((_scrollbarThickness / 2) - (scrollbarUpBitmap.Height / 2))));
}
}
internal void ScrollToTop()
{
scrollY = 0;
Render();
}
internal void ScrollToBottom()
{
int allCellsHeight = Cells.Count * CellHeight;
if (allCellsHeight > Height)
{
scrollY = allCellsHeight - Height;
}
else
{
scrollY = 0;
}
Render();
}
internal override void Render()
{
int scrollMax = (Cells.Count * CellHeight) - Height;
if (dragging)
{
scrollY += (int)(MouseManager.Y - lastDragY);
lastDragY = (int)MouseManager.Y;
if (MouseManager.MouseState != MouseState.Left)
{
dragging = false;
}
WM.UpdateQueue.Enqueue(this);
}
else if (scrollY < 0 || scrollY > scrollMax)
{
double oldScrollY = scrollY;
double move;
if (scrollY > 0)
{
move = (scrollMax - scrollY) / 8d;
}
else
{
move = (-scrollY) / 8d;
}
scrollY += move;
if (Math.Abs(scrollY - oldScrollY) > 0.05)
{
WM.UpdateQueue.Enqueue(this);
}
}
Clear(Background);
for (int i = 0; i < Cells.Count; i++)
{
TableCell cell = Cells[i];
bool selected = _selectedCellIndex == i;
Rectangle cellRect = new Rectangle(0, (int)((i * _cellHeight) - scrollY), Width, _cellHeight);
if (cellRect.Y < -cellRect.Height || cellRect.Y > Height)
{
continue;
}
if (cell.BackgroundColourOverride != null)
{
DrawFilledRectangle(cellRect.X, cellRect.Y, cellRect.Width, cellRect.Height, (Color)cell.BackgroundColourOverride);
}
else
{
// Border.
DrawFilledRectangle(cellRect.X, cellRect.Y, cellRect.Width, cellRect.Height, selected ? _selectedBorder : _border);
// Background.
DrawFilledRectangle(cellRect.X + 1, cellRect.Y + 1, cellRect.Width - 2, cellRect.Height - 2, selected ? _selectedBackground : _background);
}
int textX;
switch (_textAlignment)
{
case Alignment.Start:
textX = cellRect.X + (cell.Image != null ? (CellHeight - FontData.Height) / 2 : 0);
break;
case Alignment.Middle:
textX = cellRect.X + (cellRect.Width / 2) - (cell.Text.Length * FontData.Width / 2);
break;
case Alignment.End:
textX = cellRect.X + cellRect.Width - (cell.Text.Length * FontData.Width);
break;
default:
throw new Exception("Invalid Table alignment!");
}
int textY = cellRect.Y + (cellRect.Height / 2) - (16 / 2);
if (cell.Image != null)
{
textX += (int)cell.Image.Width;
DrawImageAlpha(cell.Image, cellRect.X, (int)(cellRect.Y + (cellRect.Height / 2) - (cell.Image.Height / 2)));
}
if (cell.ForegroundColourOverride != null)
{
DrawString(cell.Text, (Color)cell.ForegroundColourOverride, textX, textY);
}
else
{
DrawString(cell.Text, selected ? SelectedForeground : Foreground, textX, textY);
}
}
//DrawString($"{scrollY.ToString()} {dragging.ToString()} {scrollMax.ToString()}", Color.Red, 0, 0);
RenderScrollbar();
WM.Update(this);
}
}
}