Terminal: two-column pages with table cells (holdings on the left, trading on the right)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-03 15:41:42 +08:00
1 parent a2dc1dd2f6
commit 00f7811e7c
3 files changed
+122 -39

No files matched your search

+94 -35
View File
@@ -24,7 +24,7 @@ namespace Decentralized
if (Session.Data == null) return;
var b = CustomUIManager.Window(Id, L.T("term.title"));
if (b == null) return;
b.SetSize(640, 580).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
b.SetSize(780, 580).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
b.BeginRow();
foreach (Page p in Enum.GetValues(typeof(Page)))
{
@@ -52,7 +52,7 @@ namespace Decentralized
private static void Entry(CustomUIBuilder b, string icon, string name, string line1, string line2, Page target)
{
b.BeginPanel(190f, 250f, TextAnchor.UpperCenter, 10f, 6f);
b.BeginPanel(220f, 250f, TextAnchor.UpperCenter, 10f, 6f);
b.AddImage(ModInfo.Id + ":" + icon, 96f, 96f);
b.AddHeader(name);
b.AddLabel(line1);
@@ -79,31 +79,69 @@ namespace Decentralized
b.AddLabel(r == null ? L.T("report.none") : L.T("home.lastnight", r.Day, Fmt.Num(r.Mined), r.PoweredRigs, r.Rigs));
}
private const float LeftW = 430f, RightW = 300f;
private static void Cell(CustomUIBuilder b, float w, string text, bool header = false)
{
b.BeginPanel(w, 30f, TextAnchor.MiddleLeft, 6f, 0f);
b.AddLabel(header ? "<b>" + text + "</b>" : text);
b.End();
}
private static void TableRow(CustomUIBuilder b, float[] widths, params string[] cells)
{
b.BeginRow(2f);
for (int i = 0; i < widths.Length; i++) Cell(b, widths[i], i < cells.Length ? cells[i] : "");
b.End();
}
private static void TableHeader(CustomUIBuilder b, float[] widths, params string[] cells)
{
b.BeginRow(2f);
for (int i = 0; i < widths.Length; i++) Cell(b, widths[i], cells[i], true);
b.End();
}
/// <summary>左右分栏:左边是账户与持仓表格,右边是操作区。</summary>
private static void Columns(CustomUIBuilder b, Action<CustomUIBuilder> left, Action<CustomUIBuilder> right)
{
b.BeginRow(10f);
b.BeginPanel(LeftW, -1f, TextAnchor.UpperLeft, 8f, 4f);
left(b);
b.End();
b.BeginPanel(RightW, -1f, TextAnchor.UpperLeft, 8f, 6f);
right(b);
b.End();
b.End();
}
private static void AmountRow(CustomUIBuilder b)
{
b.BeginRow();
b.AddLabel(L.T("trade.amount"));
b.BeginPanel(240f, -1f, TextAnchor.MiddleCenter, 0f, 0f); // 给输入框一个固定宽度,容得下大额数字
b.BeginPanel(RightW - 30f, -1f, TextAnchor.MiddleCenter, 0f, 0f); // 给输入框一个固定宽度,容得下大额数字
b.AddInput("0", _amount, s => _amount = s, "amount", s => _amount = s);
b.End();
b.End();
}
/// <summary>银行:真实的信用点。现金、房租,以及信用点与 Poinbase 账户(CRC)之间的存取。</summary>
private static void BankPage(CustomUIBuilder b)
{
var ps = PlayerStore.Instance;
b.AddHeader(L.T("entry.bank"));
b.AddLabel(L.T("bank.cash", ps.playerCash));
b.AddLabel(L.T("bank.rent", Session.Rent(), ps.GetRentDayCounter()));
b.AddLabel(L.T("bank.crc", Fmt.Num(Session.Data.Hot.Get(Assets.CRC))));
b.AddSeparator();
b.AddLabel(L.T("bank.hint"));
AmountRow(b);
b.BeginRow();
b.AddButton(L.T("trade.deposit"), Deposit);
b.AddButton(L.T("trade.withdraw"), Withdraw);
b.End();
var w = new[] { 200f, 220f };
Columns(b, l =>
{
l.AddHeader(L.T("entry.bank"));
TableRow(l, w, L.T("bank.cash.k"), ps.playerCash.ToString());
TableRow(l, w, L.T("bank.rent.k"), L.T("bank.rent.v", Session.Rent(), ps.GetRentDayCounter()));
TableRow(l, w, L.T("bank.crc.k"), Fmt.Num(Session.Data.Hot.Get(Assets.CRC)));
}, r =>
{
r.AddHeader(L.T("bank.ops"));
foreach (var line in L.T("bank.hint").Split('|')) r.AddLabel(line);
AmountRow(r);
r.AddButton(L.T("trade.deposit"), Deposit);
r.AddButton(L.T("trade.withdraw"), Withdraw);
});
}
private static void TradeRows(CustomUIBuilder b, Venue venue)
@@ -119,34 +157,55 @@ namespace Decentralized
}
}
/// <summary>Poinbase:合规交易所。账户余额与交易对;信用点的存取在银行。</summary>
private static readonly float[] HoldW = { 80f, 100f, 110f, 110f };
private static void Holdings(CustomUIBuilder b, Wallet w, Func<string, bool> show)
{
TableHeader(b, HoldW, L.T("col.asset"), L.T("col.amount"), L.T("col.value"), L.T("col.avg"));
bool any = false;
foreach (var kv in w.Balances.OrderBy(k => k.Key).Where(k => show(k.Key)))
{
any = true;
TableRow(b, HoldW, kv.Key, Fmt.Num(kv.Value), Fmt.Credits(kv.Value * Session.Price(kv.Key)), AvgText(w, kv.Key));
}
if (!any) b.AddLabel(L.T("wallet.empty"));
}
/// <summary>Poinbase:合规交易所。左边账户持仓,右边交易;信用点的存取在银行。</summary>
private static void ExchangePage(CustomUIBuilder b, Venue venue)
{
var w = Session.Data.Hot;
b.AddHeader(L.T("entry.poinbase"));
b.AddLabel(L.T("poin.account", Fmt.Num(w.Get(Assets.CRC)), Fmt.Num(w.Get(Assets.PSC))));
b.AddLabel(L.T("avg.line", Assets.PSC, AvgText(w, Assets.PSC)));
b.AddLabel(L.T("trade.hint"));
AmountRow(b);
b.AddSeparator();
TradeRows(b, venue);
Columns(b, l =>
{
l.AddHeader(L.T("poin.account.h"));
Holdings(l, w, a => a == Assets.CRC || a == Assets.PSC);
}, r =>
{
r.AddHeader(L.T("trade.h"));
foreach (var line in L.T("trade.hint").Split('|')) r.AddLabel(line);
AmountRow(r);
TradeRows(r, venue);
});
}
/// <summary>Web3:链上钱包与去中心化交易。</summary>
private static void Web3Page(CustomUIBuilder b)
{
var w = Session.Data.Hot;
b.AddHeader(L.T("entry.web3"));
if (w.Balances.Count == 0) b.AddLabel(L.T("wallet.empty"));
foreach (var kv in w.Balances.OrderBy(k => k.Key))
b.AddLabel(L.T("wallet.line", kv.Key, Fmt.Num(kv.Value), Fmt.Credits(kv.Value * Session.Price(kv.Key))) + " " + L.T("avg.short", AvgText(w, kv.Key)));
b.AddLabel(L.T("wallet.total", Fmt.Credits(w.Value(Session.Price))));
if (w.Taint > 0.5) b.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
b.AddSeparator();
b.AddLabel(L.T("trade.hint"));
AmountRow(b);
b.AddSeparator();
TradeRows(b, Venue.Dex);
Columns(b, l =>
{
l.AddHeader(L.T("web3.wallet.h"));
Holdings(l, w, a => true);
l.AddSeparator();
l.AddLabel(L.T("wallet.total", Fmt.Credits(w.Value(Session.Price))));
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
}, r =>
{
r.AddHeader(L.T("trade.h"));
foreach (var line in L.T("trade.hint").Split('|')) r.AddLabel(line);
AmountRow(r);
TradeRows(r, Venue.Dex);
});
}
private static string AvgText(Wallet w, string asset) => w.AvgBuy(asset) is double a ? Fmt.Num(a) : L.T("avg.none");