Skip to content

Commit

Permalink
Merge pull request #615 from purpasmart96/tasklist_vertical_buttons
Browse files Browse the repository at this point in the history
Add new button label positions along with new vertical tasklist mode when labelpos is set to top or bottom
  • Loading branch information
joewing authored Jul 27, 2024
2 parents 6ab663e + e28ce1b commit 9319813
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 129 deletions.
8 changes: 8 additions & 0 deletions jwm.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,14 @@ Determines if a label is shown for items in the task list.
The default is true.
.RE
.P
\fBlabelpos\fP \fIstring\fP
.RS
Determines the label position in the task list. The default is "right" where
the icon is on the left and the label is on the right.
Possible values are "right", "top" and "bottom". When the tray is vertical and "top"
or "bottom" is chosen, an alternate method for resizing the task list will be used.
.RE
.P
\fBmaxwidth\fP \fIint\fP
.RS
The maximum width of an item in the task list. 0 indicates no maximum.
Expand Down
309 changes: 195 additions & 114 deletions src/button.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,137 @@
#include "misc.h"
#include "settings.h"

/** Draw a button. */
void DrawButton(ButtonNode *bp)
/** Determine the colors to use. */
static void GetButtonColors(ButtonNode *bp, ColorType *fg, long *bg1, long *bg2,
long *up, long *down, DecorationsType *decorations)
{
switch(bp->type) {
case BUTTON_LABEL:
*fg = COLOR_MENU_FG;
*bg1 = colors[COLOR_MENU_BG];
*bg2 = colors[COLOR_MENU_BG];
*up = colors[COLOR_MENU_UP];
*down = colors[COLOR_MENU_DOWN];
*decorations = settings.menuDecorations;
break;
case BUTTON_MENU_ACTIVE:
*fg = COLOR_MENU_ACTIVE_FG;
*bg1 = colors[COLOR_MENU_ACTIVE_BG1];
*bg2 = colors[COLOR_MENU_ACTIVE_BG2];
*down = colors[COLOR_MENU_ACTIVE_UP];
*up = colors[COLOR_MENU_ACTIVE_DOWN];
*decorations = settings.menuDecorations;
break;
case BUTTON_TRAY:
*fg = COLOR_TRAYBUTTON_FG;
*bg1 = colors[COLOR_TRAYBUTTON_BG1];
*bg2 = colors[COLOR_TRAYBUTTON_BG2];
*up = colors[COLOR_TRAYBUTTON_UP];
*down = colors[COLOR_TRAYBUTTON_DOWN];
*decorations = settings.trayDecorations;
break;
case BUTTON_TRAY_ACTIVE:
*fg = COLOR_TRAYBUTTON_ACTIVE_FG;
*bg1 = colors[COLOR_TRAYBUTTON_ACTIVE_BG1];
*bg2 = colors[COLOR_TRAYBUTTON_ACTIVE_BG2];
*down = colors[COLOR_TRAYBUTTON_ACTIVE_UP];
*up = colors[COLOR_TRAYBUTTON_ACTIVE_DOWN];
*decorations = settings.trayDecorations;
break;
case BUTTON_TASK:
*fg = COLOR_TASKLIST_FG;
*bg1 = colors[COLOR_TASKLIST_BG1];
*bg2 = colors[COLOR_TASKLIST_BG2];
*up = colors[COLOR_TASKLIST_UP];
*down = colors[COLOR_TASKLIST_DOWN];
*decorations = settings.taskListDecorations;
break;
case BUTTON_TASK_ACTIVE:
*fg = COLOR_TASKLIST_ACTIVE_FG;
*bg1 = colors[COLOR_TASKLIST_ACTIVE_BG1];
*bg2 = colors[COLOR_TASKLIST_ACTIVE_BG2];
*down = colors[COLOR_TASKLIST_ACTIVE_UP];
*up = colors[COLOR_TASKLIST_ACTIVE_DOWN];
*decorations = settings.taskListDecorations;
break;
case BUTTON_MENU:
default:
*fg = COLOR_MENU_FG;
*bg1 = colors[COLOR_MENU_BG];
*bg2 = colors[COLOR_MENU_BG];
*up = colors[COLOR_MENU_UP];
*down = colors[COLOR_MENU_DOWN];
*decorations = settings.menuDecorations;
break;
}

}

/** Determine the size of the icon (if any) to display. */
static void GetButtonIconSize(ButtonNode *bp, int *width, int *height, int *iconWidth, int *iconHeight)
{
*iconWidth = 0;
*iconHeight = 0;

if(!bp->icon)
return;

int maxIconWidth = *width - BUTTON_BORDER * 2;
int maxIconHeight = *height - BUTTON_BORDER * 2;

if(bp->text) {
if(bp->labelPos > LABEL_POSITION_RIGHT) {
/* Make room for the text. */
maxIconHeight -= GetStringHeight(bp->font) + BUTTON_BORDER;
} else {
/* Showing text, keep the icon square. */
maxIconWidth = Min(*width, *height) - BUTTON_BORDER * 2;
}
}
if(!bp->icon->width || !bp->icon->height) {
*iconWidth = Min(maxIconWidth, maxIconHeight);
*iconHeight = *iconWidth;
} else {
const int ratio = (bp->icon->width << 16) / bp->icon->height;
*iconHeight = maxIconHeight;
*iconWidth = (*iconHeight * ratio) >> 16;
if(*iconWidth > maxIconWidth) {
*iconWidth = maxIconWidth;
*iconHeight = (*iconWidth << 16) / ratio;
}
}

}

/** Determine how much room is left for text. */
static void GetTextSpaceRemaining(ButtonNode *bp, int *width, int *height, int *iconWidth,
int *iconHeight, int *textWidth, int *textHeight)
{
*textWidth = 0;
*textHeight = 0;

if(!bp->text) {
return;
}

*textWidth = GetStringWidth(bp->font, bp->text);
*textHeight = GetStringHeight(bp->font);

if(bp->labelPos < LABEL_POSITION_TOP) {
if(*width > *height || !bp->icon) {
const int borderWidth = BUTTON_BORDER * (bp->icon ? 3 : 2);
*textWidth = Min(*textWidth, *width - *iconWidth - borderWidth);
}
} else {
*textHeight = Min(*textHeight, *height - *iconHeight - BUTTON_BORDER * 3);
*textWidth = Min(*textWidth, *width - BUTTON_BORDER * 2);
}

*textWidth = Max(*textWidth, 0);
}

void DrawButton(ButtonNode *bp)
{
ColorType fg;
long bg1, bg2;
long up, down;
Expand All @@ -33,7 +160,7 @@ void DrawButton(ButtonNode *bp)

int iconWidth, iconHeight;
int textWidth, textHeight;

Assert(bp);

drawable = bp->drawable;
Expand All @@ -44,65 +171,7 @@ void DrawButton(ButtonNode *bp)
gc = JXCreateGC(display, drawable, 0, NULL);

/* Determine the colors to use. */
switch(bp->type) {
case BUTTON_LABEL:
fg = COLOR_MENU_FG;
bg1 = colors[COLOR_MENU_BG];
bg2 = colors[COLOR_MENU_BG];
up = colors[COLOR_MENU_UP];
down = colors[COLOR_MENU_DOWN];
decorations = settings.menuDecorations;
break;
case BUTTON_MENU_ACTIVE:
fg = COLOR_MENU_ACTIVE_FG;
bg1 = colors[COLOR_MENU_ACTIVE_BG1];
bg2 = colors[COLOR_MENU_ACTIVE_BG2];
down = colors[COLOR_MENU_ACTIVE_UP];
up = colors[COLOR_MENU_ACTIVE_DOWN];
decorations = settings.menuDecorations;
break;
case BUTTON_TRAY:
fg = COLOR_TRAYBUTTON_FG;
bg1 = colors[COLOR_TRAYBUTTON_BG1];
bg2 = colors[COLOR_TRAYBUTTON_BG2];
up = colors[COLOR_TRAYBUTTON_UP];
down = colors[COLOR_TRAYBUTTON_DOWN];
decorations = settings.trayDecorations;
break;
case BUTTON_TRAY_ACTIVE:
fg = COLOR_TRAYBUTTON_ACTIVE_FG;
bg1 = colors[COLOR_TRAYBUTTON_ACTIVE_BG1];
bg2 = colors[COLOR_TRAYBUTTON_ACTIVE_BG2];
down = colors[COLOR_TRAYBUTTON_ACTIVE_UP];
up = colors[COLOR_TRAYBUTTON_ACTIVE_DOWN];
decorations = settings.trayDecorations;
break;
case BUTTON_TASK:
fg = COLOR_TASKLIST_FG;
bg1 = colors[COLOR_TASKLIST_BG1];
bg2 = colors[COLOR_TASKLIST_BG2];
up = colors[COLOR_TASKLIST_UP];
down = colors[COLOR_TASKLIST_DOWN];
decorations = settings.taskListDecorations;
break;
case BUTTON_TASK_ACTIVE:
fg = COLOR_TASKLIST_ACTIVE_FG;
bg1 = colors[COLOR_TASKLIST_ACTIVE_BG1];
bg2 = colors[COLOR_TASKLIST_ACTIVE_BG2];
down = colors[COLOR_TASKLIST_ACTIVE_UP];
up = colors[COLOR_TASKLIST_ACTIVE_DOWN];
decorations = settings.taskListDecorations;
break;
case BUTTON_MENU:
default:
fg = COLOR_MENU_FG;
bg1 = colors[COLOR_MENU_BG];
bg2 = colors[COLOR_MENU_BG];
up = colors[COLOR_MENU_UP];
down = colors[COLOR_MENU_DOWN];
decorations = settings.menuDecorations;
break;
}
GetButtonColors(bp, &fg, &bg1, &bg2, &up, &down, &decorations);

/* Draw the background. */
if(bp->fill) {
Expand Down Expand Up @@ -138,66 +207,77 @@ void DrawButton(ButtonNode *bp)
}

/* Determine the size of the icon (if any) to display. */
iconWidth = 0;
iconHeight = 0;
if(bp->icon) {
if(!bp->icon->width || !bp->icon->height) {
iconWidth = Min(width - BUTTON_BORDER * 2, height - BUTTON_BORDER * 2);
iconHeight = iconWidth;
} else {
const int ratio = (bp->icon->width << 16) / bp->icon->height;
int maxIconWidth = width - BUTTON_BORDER * 2;
if(bp->text) {
/* Showing text, keep the icon square. */
maxIconWidth = Min(width, height) - BUTTON_BORDER * 2;
}
iconHeight = height - BUTTON_BORDER * 2;
iconWidth = (iconHeight * ratio) >> 16;
if(iconWidth > maxIconWidth) {
iconWidth = maxIconWidth;
iconHeight = (iconWidth << 16) / ratio;
}
}
}
GetButtonIconSize(bp, &width, &height, &iconWidth, &iconHeight);

/* Determine how much room is left for text. */
textWidth = 0;
textHeight = 0;
if(bp->text && (width > height || !bp->icon)) {
const int borderWidth = BUTTON_BORDER * (bp->icon ? 3 : 2);
textWidth = GetStringWidth(bp->font, bp->text);
textHeight = GetStringHeight(bp->font);
if(textWidth + iconWidth + borderWidth > width) {
textWidth = width - iconWidth - borderWidth;
GetTextSpaceRemaining(bp, &width, &height, &iconWidth, &iconHeight,
&textWidth, &textHeight);

if(bp->labelPos == LABEL_POSITION_RIGHT) {
if(bp->alignment == ALIGN_CENTER || width <= height) {
xoffset = Max(0, (width - iconWidth - textWidth + 1) / 2);
} else {
xoffset = BUTTON_BORDER;
}
textWidth = textWidth < 0 ? 0 : textWidth;
}

/* Determine the offset of the text in the button. */
if(bp->alignment == ALIGN_CENTER || width <= height) {
xoffset = (width - iconWidth - textWidth + 1) / 2;
if(xoffset < 0) {
xoffset = 0;
/* Display the icon. */
if(bp->icon) {
yoffset = (height - iconHeight + 1) / 2;
PutIcon(bp->icon, drawable, colors[fg],
x + xoffset, y + yoffset,
iconWidth, iconHeight);
xoffset += iconWidth + BUTTON_BORDER;
}
} else {
xoffset = BUTTON_BORDER;
}

/* Display the icon. */
if(bp->icon) {
/* Display the label. */
if(textWidth > 0) {
yoffset = (height - textHeight + 1) / 2;
RenderString(drawable, bp->font, fg,
x + xoffset, y + yoffset,
textWidth, bp->text);
}
} else if (bp->labelPos == LABEL_POSITION_TOP) {
const int ycenter = (height - textHeight - iconHeight - BUTTON_BORDER + 1) / 2;
yoffset = (height - iconHeight + 1) / 2;
PutIcon(bp->icon, drawable, colors[fg],
x + xoffset, y + yoffset,
iconWidth, iconHeight);
xoffset += iconWidth + BUTTON_BORDER;
}

/* Display the label. */
if(textWidth > 0) {
yoffset = (height - textHeight + 1) / 2;
RenderString(drawable, bp->font, fg,
x + xoffset, y + yoffset,
textWidth, bp->text);
/* Display the label before the icon. */
if(textHeight > 0 && textWidth > 0) {
xoffset = (width - textWidth + 1) / 2;
yoffset = width <= height ? Max(BUTTON_BORDER, ycenter) : BUTTON_BORDER;

RenderString(drawable, bp->font, fg,
x + xoffset, y + yoffset,
textWidth, bp->text);
yoffset += textHeight + BUTTON_BORDER;
}

/* Display the icon. */
if(bp->icon) {
xoffset = (width - iconWidth + 1) / 2;
PutIcon(bp->icon, drawable, colors[fg],
x + xoffset, y + yoffset,
iconWidth, iconHeight);
}
} else if (bp->labelPos == LABEL_POSITION_BOTTOM) {
const int ycenter = (height - iconHeight - textHeight - BUTTON_BORDER + 1) / 2;
yoffset = width <= height ? Max(BUTTON_BORDER, ycenter) : BUTTON_BORDER;

/* Display the icon. */
if(bp->icon) {
xoffset = (width - iconWidth + 1) / 2;
PutIcon(bp->icon, drawable, colors[fg],
x + xoffset, y + yoffset,
iconWidth, iconHeight);
yoffset += iconHeight + BUTTON_BORDER;
}

/* Display the label. */
if(textHeight > 0 && textWidth > 0) {
xoffset = (width - textWidth + 1) / 2;
RenderString(drawable, bp->font, fg,
x + xoffset, y + yoffset,
textWidth, bp->text);
}
}

JXFreeGC(display, gc);
Expand All @@ -214,6 +294,7 @@ void ResetButton(ButtonNode *bp, Drawable d)
bp->drawable = d;
bp->font = FONT_TRAY;
bp->alignment = ALIGN_LEFT;
bp->labelPos = LABEL_POSITION_RIGHT;
bp->x = 0;
bp->y = 0;
bp->width = 1;
Expand Down
7 changes: 7 additions & 0 deletions src/button.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,18 @@ typedef unsigned char ButtonType;
#define BUTTON_TASK 5 /**< Item in the task list. */
#define BUTTON_TASK_ACTIVE 6 /**< Active item in the task list. */

/** Enumeration of button label positions. */
typedef unsigned char LabelPosition;
#define LABEL_POSITION_RIGHT 0 /**< Right of the button icon. */
#define LABEL_POSITION_TOP 1 /**< Above of the button icon. */
#define LABEL_POSITION_BOTTOM 2 /**< Below the button icon.. */

/** Data used for drawing a button. */
typedef struct {

ButtonType type; /**< The type of button to draw. */
AlignmentType alignment; /**< Alignment of the button content. */
LabelPosition labelPos; /**< Position of the button label. */
FontType font; /**< The font for button text. */
char fill; /**< Determine if we should fill. */
char border; /**< Determine if we should draw a border. */
Expand Down
1 change: 1 addition & 0 deletions src/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,7 @@ void DrawMenuItem(Menu *menu, MenuItem *item, int index)
button.type = BUTTON_LABEL;
button.text = menu->label;
button.alignment = ALIGN_CENTER;
button.labelPos = LABEL_POSITION_RIGHT;
DrawButton(&button);
}
return;
Expand Down
Loading

0 comments on commit 9319813

Please sign in to comment.