Tabgroup and treeview bugs fixed.
This commit is contained in:
parent
73f7e37ba6
commit
685c8041c1
6 changed files with 503 additions and 16 deletions
|
|
@ -248,6 +248,7 @@ typedef struct WidgetT {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int32_t scrollPos;
|
int32_t scrollPos;
|
||||||
|
int32_t scrollPosH;
|
||||||
} treeView;
|
} treeView;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,11 @@ WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TreeView manages its own children — don't recurse
|
||||||
|
if (w->type == WidgetTreeViewE) {
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
// Check children — take the last match (topmost in Z-order)
|
// Check children — take the last match (topmost in Z-order)
|
||||||
WidgetT *hit = NULL;
|
WidgetT *hit = NULL;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@
|
||||||
#define TREE_EXPAND_SIZE 9
|
#define TREE_EXPAND_SIZE 9
|
||||||
#define TREE_ICON_GAP 4
|
#define TREE_ICON_GAP 4
|
||||||
#define TREE_BORDER 2
|
#define TREE_BORDER 2
|
||||||
|
#define TREE_SB_W 14
|
||||||
|
#define TREE_MIN_ROWS 4
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Shared state (defined in widgetCore.c)
|
// Shared state (defined in widgetCore.c)
|
||||||
|
|
|
||||||
|
|
@ -114,9 +114,11 @@ void widgetTabControlLayout(WidgetT *w, const BitmapFontT *font) {
|
||||||
c->w = contentW;
|
c->w = contentW;
|
||||||
c->h = contentH;
|
c->h = contentH;
|
||||||
|
|
||||||
// Only layout the active page
|
|
||||||
if (idx == w->as.tabControl.activeTab) {
|
if (idx == w->as.tabControl.activeTab) {
|
||||||
|
c->visible = true;
|
||||||
widgetLayoutChildren(c, font);
|
widgetLayoutChildren(c, font);
|
||||||
|
} else {
|
||||||
|
c->visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx++;
|
idx++;
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,13 @@
|
||||||
|
|
||||||
static int32_t calcTreeItemsHeight(WidgetT *parent, const BitmapFontT *font);
|
static int32_t calcTreeItemsHeight(WidgetT *parent, const BitmapFontT *font);
|
||||||
static int32_t calcTreeItemsMaxWidth(WidgetT *parent, const BitmapFontT *font, int32_t depth);
|
static int32_t calcTreeItemsMaxWidth(WidgetT *parent, const BitmapFontT *font, int32_t depth);
|
||||||
|
static void drawTreeHScrollbar(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t totalW, int32_t innerW, bool hasVSb);
|
||||||
|
static void drawTreeVScrollbar(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t totalH, int32_t innerH);
|
||||||
static void layoutTreeItems(WidgetT *parent, const BitmapFontT *font, int32_t x, int32_t *y, int32_t width, int32_t depth);
|
static void layoutTreeItems(WidgetT *parent, const BitmapFontT *font, int32_t x, int32_t *y, int32_t width, int32_t depth);
|
||||||
static void paintTreeItems(WidgetT *parent, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t baseX, int32_t *itemY, int32_t depth, int32_t clipTop, int32_t clipBottom);
|
static void paintTreeItems(WidgetT *parent, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t baseX, int32_t *itemY, int32_t depth, int32_t clipTop, int32_t clipBottom);
|
||||||
|
static void treeCalcScrollbarNeeds(WidgetT *w, const BitmapFontT *font, int32_t *outTotalH, int32_t *outTotalW, int32_t *outInnerH, int32_t *outInnerW, bool *outNeedVSb, bool *outNeedHSb);
|
||||||
static WidgetT *treeItemAtY(WidgetT *parent, int32_t targetY, int32_t *curY, const BitmapFontT *font);
|
static WidgetT *treeItemAtY(WidgetT *parent, int32_t targetY, int32_t *curY, const BitmapFontT *font);
|
||||||
|
static void treeScrollbarThumb(int32_t trackLen, int32_t totalSize, int32_t innerSize, int32_t scrollPos, int32_t *thumbPos, int32_t *thumbSize);
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -69,6 +73,147 @@ static int32_t calcTreeItemsMaxWidth(WidgetT *parent, const BitmapFontT *font, i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// drawTreeHScrollbar
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
static void drawTreeHScrollbar(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t totalW, int32_t innerW, bool hasVSb) {
|
||||||
|
int32_t sbX = w->x + TREE_BORDER;
|
||||||
|
int32_t sbY = w->y + w->h - TREE_BORDER - TREE_SB_W;
|
||||||
|
int32_t sbW = innerW;
|
||||||
|
|
||||||
|
if (sbW < TREE_SB_W * 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trough background
|
||||||
|
BevelStyleT troughBevel;
|
||||||
|
troughBevel.highlight = colors->windowShadow;
|
||||||
|
troughBevel.shadow = colors->windowHighlight;
|
||||||
|
troughBevel.face = colors->scrollbarTrough;
|
||||||
|
troughBevel.width = 1;
|
||||||
|
drawBevel(d, ops, sbX, sbY, sbW, TREE_SB_W, &troughBevel);
|
||||||
|
|
||||||
|
// Left arrow button
|
||||||
|
BevelStyleT btnBevel;
|
||||||
|
btnBevel.highlight = colors->windowHighlight;
|
||||||
|
btnBevel.shadow = colors->windowShadow;
|
||||||
|
btnBevel.face = colors->windowFace;
|
||||||
|
btnBevel.width = 1;
|
||||||
|
drawBevel(d, ops, sbX, sbY, TREE_SB_W, TREE_SB_W, &btnBevel);
|
||||||
|
|
||||||
|
// Left arrow triangle
|
||||||
|
{
|
||||||
|
int32_t cx = sbX + TREE_SB_W / 2;
|
||||||
|
int32_t cy = sbY + TREE_SB_W / 2;
|
||||||
|
uint32_t fg = colors->contentFg;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < 4; i++) {
|
||||||
|
drawVLine(d, ops, cx - 2 + i, cy - i, 1 + i * 2, fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right arrow button
|
||||||
|
int32_t rightX = sbX + sbW - TREE_SB_W;
|
||||||
|
drawBevel(d, ops, rightX, sbY, TREE_SB_W, TREE_SB_W, &btnBevel);
|
||||||
|
|
||||||
|
// Right arrow triangle
|
||||||
|
{
|
||||||
|
int32_t cx = rightX + TREE_SB_W / 2;
|
||||||
|
int32_t cy = sbY + TREE_SB_W / 2;
|
||||||
|
uint32_t fg = colors->contentFg;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < 4; i++) {
|
||||||
|
drawVLine(d, ops, cx + 2 - i, cy - i, 1 + i * 2, fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumb
|
||||||
|
int32_t trackLen = sbW - TREE_SB_W * 2;
|
||||||
|
|
||||||
|
if (trackLen > 0 && totalW > 0) {
|
||||||
|
int32_t thumbPos;
|
||||||
|
int32_t thumbSize;
|
||||||
|
treeScrollbarThumb(trackLen, totalW, innerW, w->as.treeView.scrollPosH, &thumbPos, &thumbSize);
|
||||||
|
|
||||||
|
drawBevel(d, ops, sbX + TREE_SB_W + thumbPos, sbY, thumbSize, TREE_SB_W, &btnBevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill dead corner when both scrollbars present
|
||||||
|
if (hasVSb) {
|
||||||
|
rectFill(d, ops, sbX + sbW, sbY, TREE_SB_W, TREE_SB_W, colors->windowFace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// drawTreeVScrollbar
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
static void drawTreeVScrollbar(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t totalH, int32_t innerH) {
|
||||||
|
int32_t sbX = w->x + w->w - TREE_BORDER - TREE_SB_W;
|
||||||
|
int32_t sbY = w->y + TREE_BORDER;
|
||||||
|
int32_t sbH = innerH;
|
||||||
|
|
||||||
|
if (sbH < TREE_SB_W * 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trough background
|
||||||
|
BevelStyleT troughBevel;
|
||||||
|
troughBevel.highlight = colors->windowShadow;
|
||||||
|
troughBevel.shadow = colors->windowHighlight;
|
||||||
|
troughBevel.face = colors->scrollbarTrough;
|
||||||
|
troughBevel.width = 1;
|
||||||
|
drawBevel(d, ops, sbX, sbY, TREE_SB_W, sbH, &troughBevel);
|
||||||
|
|
||||||
|
// Up arrow button
|
||||||
|
BevelStyleT btnBevel;
|
||||||
|
btnBevel.highlight = colors->windowHighlight;
|
||||||
|
btnBevel.shadow = colors->windowShadow;
|
||||||
|
btnBevel.face = colors->windowFace;
|
||||||
|
btnBevel.width = 1;
|
||||||
|
drawBevel(d, ops, sbX, sbY, TREE_SB_W, TREE_SB_W, &btnBevel);
|
||||||
|
|
||||||
|
// Up arrow triangle
|
||||||
|
{
|
||||||
|
int32_t cx = sbX + TREE_SB_W / 2;
|
||||||
|
int32_t cy = sbY + TREE_SB_W / 2;
|
||||||
|
uint32_t fg = colors->contentFg;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < 4; i++) {
|
||||||
|
drawHLine(d, ops, cx - i, cy - 2 + i, 1 + i * 2, fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Down arrow button
|
||||||
|
int32_t downY = sbY + sbH - TREE_SB_W;
|
||||||
|
drawBevel(d, ops, sbX, downY, TREE_SB_W, TREE_SB_W, &btnBevel);
|
||||||
|
|
||||||
|
// Down arrow triangle
|
||||||
|
{
|
||||||
|
int32_t cx = sbX + TREE_SB_W / 2;
|
||||||
|
int32_t cy = downY + TREE_SB_W / 2;
|
||||||
|
uint32_t fg = colors->contentFg;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < 4; i++) {
|
||||||
|
drawHLine(d, ops, cx - i, cy + 2 - i, 1 + i * 2, fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumb
|
||||||
|
int32_t trackLen = sbH - TREE_SB_W * 2;
|
||||||
|
|
||||||
|
if (trackLen > 0 && totalH > 0) {
|
||||||
|
int32_t thumbPos;
|
||||||
|
int32_t thumbSize;
|
||||||
|
treeScrollbarThumb(trackLen, totalH, innerH, w->as.treeView.scrollPos, &thumbPos, &thumbSize);
|
||||||
|
|
||||||
|
drawBevel(d, ops, sbX, sbY + TREE_SB_W + thumbPos, TREE_SB_W, thumbSize, &btnBevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// layoutTreeItems
|
// layoutTreeItems
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -164,6 +309,49 @@ static void paintTreeItems(WidgetT *parent, DisplayT *d, const BlitOpsT *ops, co
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// treeCalcScrollbarNeeds
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Compute content dimensions and determine which scrollbars are
|
||||||
|
// needed, accounting for the mutual space dependency between them.
|
||||||
|
|
||||||
|
static void treeCalcScrollbarNeeds(WidgetT *w, const BitmapFontT *font, int32_t *outTotalH, int32_t *outTotalW, int32_t *outInnerH, int32_t *outInnerW, bool *outNeedVSb, bool *outNeedHSb) {
|
||||||
|
int32_t totalH = calcTreeItemsHeight(w, font);
|
||||||
|
int32_t totalW = calcTreeItemsMaxWidth(w, font, 0);
|
||||||
|
int32_t innerH = w->h - TREE_BORDER * 2;
|
||||||
|
int32_t innerW = w->w - TREE_BORDER * 2;
|
||||||
|
bool needVSb = (totalH > innerH);
|
||||||
|
bool needHSb = (totalW > innerW);
|
||||||
|
|
||||||
|
// V scrollbar reduces available width — may trigger H scrollbar
|
||||||
|
if (needVSb) {
|
||||||
|
innerW -= TREE_SB_W;
|
||||||
|
|
||||||
|
if (!needHSb && totalW > innerW) {
|
||||||
|
needHSb = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// H scrollbar reduces available height — may trigger V scrollbar
|
||||||
|
if (needHSb) {
|
||||||
|
innerH -= TREE_SB_W;
|
||||||
|
|
||||||
|
if (!needVSb && totalH > innerH) {
|
||||||
|
needVSb = true;
|
||||||
|
innerW -= TREE_SB_W;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*outTotalH = totalH;
|
||||||
|
*outTotalW = totalW;
|
||||||
|
*outInnerH = innerH;
|
||||||
|
*outInnerW = innerW;
|
||||||
|
*outNeedVSb = needVSb;
|
||||||
|
*outNeedHSb = needHSb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// treeItemAtY
|
// treeItemAtY
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -195,6 +383,33 @@ static WidgetT *treeItemAtY(WidgetT *parent, int32_t targetY, int32_t *curY, con
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// treeScrollbarThumb
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
static void treeScrollbarThumb(int32_t trackLen, int32_t totalSize, int32_t innerSize, int32_t scrollPos, int32_t *thumbPos, int32_t *thumbSize) {
|
||||||
|
// Proportional thumb size
|
||||||
|
*thumbSize = (trackLen * innerSize) / totalSize;
|
||||||
|
|
||||||
|
if (*thumbSize < TREE_SB_W) {
|
||||||
|
*thumbSize = TREE_SB_W;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*thumbSize > trackLen) {
|
||||||
|
*thumbSize = trackLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumb position
|
||||||
|
int32_t maxScroll = totalSize - innerSize;
|
||||||
|
|
||||||
|
if (maxScroll > 0) {
|
||||||
|
*thumbPos = ((trackLen - *thumbSize) * scrollPos) / maxScroll;
|
||||||
|
} else {
|
||||||
|
*thumbPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// wgtTreeItem
|
// wgtTreeItem
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -257,11 +472,10 @@ WidgetT *wgtTreeView(WidgetT *parent) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void widgetTreeViewCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
void widgetTreeViewCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
int32_t totalH = calcTreeItemsHeight(w, font);
|
int32_t minContentW = TREE_INDENT + TREE_EXPAND_SIZE + TREE_ICON_GAP + 6 * font->charWidth;
|
||||||
int32_t maxW = calcTreeItemsMaxWidth(w, font, 0);
|
|
||||||
|
|
||||||
w->calcMinW = maxW + TREE_BORDER * 2;
|
w->calcMinW = minContentW + TREE_BORDER * 2 + TREE_SB_W;
|
||||||
w->calcMinH = totalH + TREE_BORDER * 2;
|
w->calcMinH = TREE_MIN_ROWS * font->charHeight + TREE_BORDER * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -270,11 +484,20 @@ void widgetTreeViewCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void widgetTreeViewLayout(WidgetT *w, const BitmapFontT *font) {
|
void widgetTreeViewLayout(WidgetT *w, const BitmapFontT *font) {
|
||||||
|
int32_t totalH;
|
||||||
|
int32_t totalW;
|
||||||
|
int32_t innerH;
|
||||||
|
int32_t innerW;
|
||||||
|
bool needVSb;
|
||||||
|
bool needHSb;
|
||||||
|
|
||||||
|
treeCalcScrollbarNeeds(w, font, &totalH, &totalW, &innerH, &innerW, &needVSb, &needHSb);
|
||||||
|
|
||||||
|
int32_t itemW = innerW > totalW ? innerW : totalW;
|
||||||
int32_t innerX = w->x + TREE_BORDER;
|
int32_t innerX = w->x + TREE_BORDER;
|
||||||
int32_t innerY = w->y + TREE_BORDER;
|
int32_t innerY = w->y + TREE_BORDER;
|
||||||
int32_t innerW = w->w - TREE_BORDER * 2;
|
|
||||||
|
|
||||||
layoutTreeItems(w, font, innerX, &innerY, innerW, 0);
|
layoutTreeItems(w, font, innerX, &innerY, itemW, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -285,7 +508,154 @@ void widgetTreeViewLayout(WidgetT *w, const BitmapFontT *font) {
|
||||||
void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
|
void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
|
||||||
AppContextT *ctx = (AppContextT *)root->userData;
|
AppContextT *ctx = (AppContextT *)root->userData;
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t curY = hit->y + TREE_BORDER;
|
|
||||||
|
int32_t totalH;
|
||||||
|
int32_t totalW;
|
||||||
|
int32_t innerH;
|
||||||
|
int32_t innerW;
|
||||||
|
bool needVSb;
|
||||||
|
bool needHSb;
|
||||||
|
|
||||||
|
treeCalcScrollbarNeeds(hit, font, &totalH, &totalW, &innerH, &innerW, &needVSb, &needHSb);
|
||||||
|
|
||||||
|
// Clamp scroll positions
|
||||||
|
int32_t maxScrollV = totalH - innerH;
|
||||||
|
int32_t maxScrollH = totalW - innerW;
|
||||||
|
|
||||||
|
if (maxScrollV < 0) {
|
||||||
|
maxScrollV = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxScrollH < 0) {
|
||||||
|
maxScrollH = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPos > maxScrollV) {
|
||||||
|
hit->as.treeView.scrollPos = maxScrollV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPos < 0) {
|
||||||
|
hit->as.treeView.scrollPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPosH > maxScrollH) {
|
||||||
|
hit->as.treeView.scrollPosH = maxScrollH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPosH < 0) {
|
||||||
|
hit->as.treeView.scrollPosH = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if click is on the vertical scrollbar
|
||||||
|
if (needVSb) {
|
||||||
|
int32_t sbX = hit->x + hit->w - TREE_BORDER - TREE_SB_W;
|
||||||
|
|
||||||
|
if (vx >= sbX && vy < hit->y + TREE_BORDER + innerH) {
|
||||||
|
int32_t sbY = hit->y + TREE_BORDER;
|
||||||
|
int32_t sbH = innerH;
|
||||||
|
int32_t relY = vy - sbY;
|
||||||
|
int32_t trackLen = sbH - TREE_SB_W * 2;
|
||||||
|
int32_t pageSize = innerH - font->charHeight;
|
||||||
|
|
||||||
|
if (pageSize < font->charHeight) {
|
||||||
|
pageSize = font->charHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relY < TREE_SB_W) {
|
||||||
|
// Up arrow — scroll up one row
|
||||||
|
hit->as.treeView.scrollPos -= font->charHeight;
|
||||||
|
} else if (relY >= sbH - TREE_SB_W) {
|
||||||
|
// Down arrow — scroll down one row
|
||||||
|
hit->as.treeView.scrollPos += font->charHeight;
|
||||||
|
} else if (trackLen > 0) {
|
||||||
|
// Track area — page up/down based on thumb position
|
||||||
|
int32_t thumbPos;
|
||||||
|
int32_t thumbSize;
|
||||||
|
treeScrollbarThumb(trackLen, totalH, innerH, hit->as.treeView.scrollPos, &thumbPos, &thumbSize);
|
||||||
|
|
||||||
|
int32_t trackRelY = relY - TREE_SB_W;
|
||||||
|
|
||||||
|
if (trackRelY < thumbPos) {
|
||||||
|
hit->as.treeView.scrollPos -= pageSize;
|
||||||
|
} else if (trackRelY >= thumbPos + thumbSize) {
|
||||||
|
hit->as.treeView.scrollPos += pageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp after scroll
|
||||||
|
if (hit->as.treeView.scrollPos < 0) {
|
||||||
|
hit->as.treeView.scrollPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPos > maxScrollV) {
|
||||||
|
hit->as.treeView.scrollPos = maxScrollV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if click is on the horizontal scrollbar
|
||||||
|
if (needHSb) {
|
||||||
|
int32_t sbY = hit->y + hit->h - TREE_BORDER - TREE_SB_W;
|
||||||
|
|
||||||
|
if (vy >= sbY && vx < hit->x + TREE_BORDER + innerW) {
|
||||||
|
int32_t sbX = hit->x + TREE_BORDER;
|
||||||
|
int32_t sbW = innerW;
|
||||||
|
int32_t relX = vx - sbX;
|
||||||
|
int32_t trackLen = sbW - TREE_SB_W * 2;
|
||||||
|
int32_t pageSize = innerW - font->charWidth;
|
||||||
|
|
||||||
|
if (pageSize < font->charWidth) {
|
||||||
|
pageSize = font->charWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relX < TREE_SB_W) {
|
||||||
|
// Left arrow — scroll left
|
||||||
|
hit->as.treeView.scrollPosH -= font->charWidth;
|
||||||
|
} else if (relX >= sbW - TREE_SB_W) {
|
||||||
|
// Right arrow — scroll right
|
||||||
|
hit->as.treeView.scrollPosH += font->charWidth;
|
||||||
|
} else if (trackLen > 0) {
|
||||||
|
// Track area — page left/right based on thumb position
|
||||||
|
int32_t thumbPos;
|
||||||
|
int32_t thumbSize;
|
||||||
|
treeScrollbarThumb(trackLen, totalW, innerW, hit->as.treeView.scrollPosH, &thumbPos, &thumbSize);
|
||||||
|
|
||||||
|
int32_t trackRelX = relX - TREE_SB_W;
|
||||||
|
|
||||||
|
if (trackRelX < thumbPos) {
|
||||||
|
hit->as.treeView.scrollPosH -= pageSize;
|
||||||
|
} else if (trackRelX >= thumbPos + thumbSize) {
|
||||||
|
hit->as.treeView.scrollPosH += pageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp after scroll
|
||||||
|
if (hit->as.treeView.scrollPosH < 0) {
|
||||||
|
hit->as.treeView.scrollPosH = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPosH > maxScrollH) {
|
||||||
|
hit->as.treeView.scrollPosH = maxScrollH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Click in dead corner (both scrollbars present) — ignore
|
||||||
|
if (needVSb && needHSb) {
|
||||||
|
int32_t cornerX = hit->x + hit->w - TREE_BORDER - TREE_SB_W;
|
||||||
|
int32_t cornerY = hit->y + hit->h - TREE_BORDER - TREE_SB_W;
|
||||||
|
|
||||||
|
if (vx >= cornerX && vy >= cornerY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tree item click — adjust for scroll offsets
|
||||||
|
int32_t curY = hit->y + TREE_BORDER - hit->as.treeView.scrollPos;
|
||||||
|
|
||||||
WidgetT *item = treeItemAtY(hit, vy, &curY, font);
|
WidgetT *item = treeItemAtY(hit, vy, &curY, font);
|
||||||
|
|
||||||
|
|
@ -313,11 +683,36 @@ void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
|
||||||
p = p->parent;
|
p = p->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t iconX = hit->x + TREE_BORDER + depth * TREE_INDENT;
|
int32_t iconX = hit->x + TREE_BORDER + depth * TREE_INDENT - hit->as.treeView.scrollPosH;
|
||||||
|
|
||||||
if (vx >= iconX && vx < iconX + TREE_EXPAND_SIZE) {
|
if (vx >= iconX && vx < iconX + TREE_EXPAND_SIZE) {
|
||||||
item->as.treeItem.expanded = !item->as.treeItem.expanded;
|
item->as.treeItem.expanded = !item->as.treeItem.expanded;
|
||||||
|
|
||||||
|
// Clamp scroll positions if collapsing reduced content size
|
||||||
|
if (!item->as.treeItem.expanded) {
|
||||||
|
int32_t newTotalH = calcTreeItemsHeight(hit, font);
|
||||||
|
int32_t newMaxScrlV = newTotalH - innerH;
|
||||||
|
|
||||||
|
if (newMaxScrlV < 0) {
|
||||||
|
newMaxScrlV = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPos > newMaxScrlV) {
|
||||||
|
hit->as.treeView.scrollPos = newMaxScrlV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t newTotalW = calcTreeItemsMaxWidth(hit, font, 0);
|
||||||
|
int32_t newMaxScrlH = newTotalW - innerW;
|
||||||
|
|
||||||
|
if (newMaxScrlH < 0) {
|
||||||
|
newMaxScrlH = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit->as.treeView.scrollPosH > newMaxScrlH) {
|
||||||
|
hit->as.treeView.scrollPosH = newMaxScrlH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->onChange) {
|
if (item->onChange) {
|
||||||
item->onChange(item);
|
item->onChange(item);
|
||||||
}
|
}
|
||||||
|
|
@ -341,6 +736,43 @@ void widgetTreeViewOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy)
|
||||||
void widgetTreeViewPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
void widgetTreeViewPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
||||||
uint32_t bg = w->bgColor ? w->bgColor : colors->contentBg;
|
uint32_t bg = w->bgColor ? w->bgColor : colors->contentBg;
|
||||||
|
|
||||||
|
int32_t totalH;
|
||||||
|
int32_t totalW;
|
||||||
|
int32_t innerH;
|
||||||
|
int32_t innerW;
|
||||||
|
bool needVSb;
|
||||||
|
bool needHSb;
|
||||||
|
|
||||||
|
treeCalcScrollbarNeeds(w, font, &totalH, &totalW, &innerH, &innerW, &needVSb, &needHSb);
|
||||||
|
|
||||||
|
// Clamp scroll positions
|
||||||
|
int32_t maxScrollV = totalH - innerH;
|
||||||
|
int32_t maxScrollH = totalW - innerW;
|
||||||
|
|
||||||
|
if (maxScrollV < 0) {
|
||||||
|
maxScrollV = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxScrollH < 0) {
|
||||||
|
maxScrollH = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->as.treeView.scrollPos > maxScrollV) {
|
||||||
|
w->as.treeView.scrollPos = maxScrollV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->as.treeView.scrollPos < 0) {
|
||||||
|
w->as.treeView.scrollPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->as.treeView.scrollPosH > maxScrollH) {
|
||||||
|
w->as.treeView.scrollPosH = maxScrollH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->as.treeView.scrollPosH < 0) {
|
||||||
|
w->as.treeView.scrollPosH = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Sunken border
|
// Sunken border
|
||||||
BevelStyleT bevel;
|
BevelStyleT bevel;
|
||||||
bevel.highlight = colors->windowShadow;
|
bevel.highlight = colors->windowShadow;
|
||||||
|
|
@ -349,10 +781,30 @@ void widgetTreeViewPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
||||||
bevel.width = 2;
|
bevel.width = 2;
|
||||||
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
|
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
|
||||||
|
|
||||||
// Paint tree items
|
// Set clip rect to inner content area (excludes scrollbars)
|
||||||
int32_t itemY = w->y + TREE_BORDER;
|
int32_t oldClipX = d->clipX;
|
||||||
|
int32_t oldClipY = d->clipY;
|
||||||
|
int32_t oldClipW = d->clipW;
|
||||||
|
int32_t oldClipH = d->clipH;
|
||||||
|
setClipRect(d, w->x + TREE_BORDER, w->y + TREE_BORDER, innerW, innerH);
|
||||||
|
|
||||||
|
// Paint tree items offset by both scroll positions
|
||||||
|
int32_t itemY = w->y + TREE_BORDER - w->as.treeView.scrollPos;
|
||||||
|
int32_t baseX = w->x + TREE_BORDER - w->as.treeView.scrollPosH;
|
||||||
|
|
||||||
paintTreeItems(w, d, ops, font, colors,
|
paintTreeItems(w, d, ops, font, colors,
|
||||||
w->x + TREE_BORDER, &itemY, 0,
|
baseX, &itemY, 0,
|
||||||
w->y + TREE_BORDER, w->y + w->h - TREE_BORDER);
|
w->y + TREE_BORDER, w->y + TREE_BORDER + innerH);
|
||||||
|
|
||||||
|
// Restore clip rect
|
||||||
|
setClipRect(d, oldClipX, oldClipY, oldClipW, oldClipH);
|
||||||
|
|
||||||
|
// Draw scrollbars
|
||||||
|
if (needVSb) {
|
||||||
|
drawTreeVScrollbar(w, d, ops, colors, totalH, innerH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needHSb) {
|
||||||
|
drawTreeHScrollbar(w, d, ops, colors, totalW, innerW, needVSb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ static void onCloseCb(WindowT *win);
|
||||||
static void onMenuCb(WindowT *win, int32_t menuId);
|
static void onMenuCb(WindowT *win, int32_t menuId);
|
||||||
static void onOkClick(WidgetT *w);
|
static void onOkClick(WidgetT *w);
|
||||||
static void onPaintColor(WindowT *win, RectT *dirtyArea);
|
static void onPaintColor(WindowT *win, RectT *dirtyArea);
|
||||||
|
static void onToolbarClick(WidgetT *w);
|
||||||
static void onPaintPattern(WindowT *win, RectT *dirtyArea);
|
static void onPaintPattern(WindowT *win, RectT *dirtyArea);
|
||||||
static void onPaintText(WindowT *win, RectT *dirtyArea);
|
static void onPaintText(WindowT *win, RectT *dirtyArea);
|
||||||
static void setupWidgetDemo2(AppContextT *ctx);
|
static void setupWidgetDemo2(AppContextT *ctx);
|
||||||
|
|
@ -85,6 +86,26 @@ static void onOkClick(WidgetT *w) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// onToolbarClick
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
static void onToolbarClick(WidgetT *w) {
|
||||||
|
WidgetT *root = w;
|
||||||
|
|
||||||
|
while (root->parent) {
|
||||||
|
root = root->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetT *status = wgtFind(root, "advStatus");
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
wgtSetText(status, wgtGetText(w));
|
||||||
|
wgtInvalidate(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// onPaintColor
|
// onPaintColor
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -320,9 +341,12 @@ static void setupWidgetDemo2(AppContextT *ctx) {
|
||||||
WidgetT *page3 = wgtTabPage(tabs, "Toolbar");
|
WidgetT *page3 = wgtTabPage(tabs, "Toolbar");
|
||||||
|
|
||||||
WidgetT *tb = wgtToolbar(page3);
|
WidgetT *tb = wgtToolbar(page3);
|
||||||
wgtButton(tb, "New");
|
WidgetT *btnNew = wgtButton(tb, "New");
|
||||||
wgtButton(tb, "Open");
|
WidgetT *btnOpen = wgtButton(tb, "Open");
|
||||||
wgtButton(tb, "Save");
|
WidgetT *btnSave = wgtButton(tb, "Save");
|
||||||
|
btnNew->onClick = onToolbarClick;
|
||||||
|
btnOpen->onClick = onToolbarClick;
|
||||||
|
btnSave->onClick = onToolbarClick;
|
||||||
|
|
||||||
wgtLabel(page3, "Toolbar with buttons above.");
|
wgtLabel(page3, "Toolbar with buttons above.");
|
||||||
|
|
||||||
|
|
@ -330,6 +354,7 @@ static void setupWidgetDemo2(AppContextT *ctx) {
|
||||||
WidgetT *sb = wgtStatusBar(root);
|
WidgetT *sb = wgtStatusBar(root);
|
||||||
WidgetT *sbLabel = wgtLabel(sb, "Ready");
|
WidgetT *sbLabel = wgtLabel(sb, "Ready");
|
||||||
sbLabel->weight = 100;
|
sbLabel->weight = 100;
|
||||||
|
strncpy(sbLabel->name, "advStatus", MAX_WIDGET_NAME);
|
||||||
wgtLabel(sb, "Line 1, Col 1");
|
wgtLabel(sb, "Line 1, Col 1");
|
||||||
|
|
||||||
wgtInvalidate(root);
|
wgtInvalidate(root);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue