diff --git a/jwm.1.in b/jwm.1.in index d31b1bfb..8ae30b50 100644 --- a/jwm.1.in +++ b/jwm.1.in @@ -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. diff --git a/src/button.c b/src/button.c index e07c387f..df86cf8d 100644 --- a/src/button.c +++ b/src/button.c @@ -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; @@ -33,7 +160,7 @@ void DrawButton(ButtonNode *bp) int iconWidth, iconHeight; int textWidth, textHeight; - + Assert(bp); drawable = bp->drawable; @@ -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) { @@ -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); @@ -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; diff --git a/src/button.h b/src/button.h index 29175cdd..a8018c70 100644 --- a/src/button.h +++ b/src/button.h @@ -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. */ diff --git a/src/menu.c b/src/menu.c index 5b8dd4cd..badcd159 100644 --- a/src/menu.c +++ b/src/menu.c @@ -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; diff --git a/src/parse.c b/src/parse.c index 0201b340..353dbbbe 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1443,6 +1443,12 @@ void ParseTaskList(const TokenNode *tp, TrayType *tray) if(temp && !strcmp(temp, FALSE_VALUE)) { SetTaskBarLabeled(cp, 0); } + + temp = FindAttribute(tp->attributes, "labelpos"); + if(temp) { + SetTaskBarLabelPosition(cp, temp); + } + } /** Parse the tray button style. */ diff --git a/src/taskbar.c b/src/taskbar.c index 718872c9..af8bf8fc 100644 --- a/src/taskbar.c +++ b/src/taskbar.c @@ -38,6 +38,7 @@ typedef struct TaskBarType { int itemWidth; LayoutType layout; char labeled; + LabelPosition labelPos; Pixmap buffer; @@ -62,6 +63,7 @@ static TaskBarType *bars; static TaskEntry *taskEntries; static TaskEntry *taskEntriesTail; +static unsigned TallyVisibleItems(void); static void ComputeItemSize(TaskBarType *tp); static char ShouldShowEntry(const TaskEntry *tp); static char ShouldFocusEntry(const TaskEntry *tp); @@ -128,6 +130,7 @@ TrayComponentType *CreateTaskBar() tp->maxItemWidth = 0; tp->layout = LAYOUT_HORIZONTAL; tp->labeled = 1; + tp->labelPos = LABEL_POSITION_RIGHT; tp->mousex = -settings.doubleClickDelta; tp->mousey = -settings.doubleClickDelta; tp->mouseTime.seconds = 0; @@ -187,38 +190,60 @@ void Resize(TrayComponentType *cp) ClearTrayDrawable(cp); } +/** Count the number of items that should be shown in the task bar. */ +unsigned TallyVisibleItems(void) +{ + TaskEntry *ep; + unsigned count = 0; + for(ep = taskEntries; ep; ep = ep->next) { + if(ShouldShowEntry(ep)) { + count += 1; + } + } + return count; +} + /** Determine the size of items in the task bar. */ void ComputeItemSize(TaskBarType *tp) { TrayComponentType *cp = tp->cp; - if(tp->layout == LAYOUT_VERTICAL) { - if(tp->userHeight > 0) { - tp->itemHeight = tp->userHeight; - } else { - tp->itemHeight = GetStringHeight(FONT_TASKLIST) + 12; - } - tp->itemWidth = cp->width; + if(tp->layout == LAYOUT_VERTICAL) { + if(tp->labelPos > LABEL_POSITION_RIGHT) { + unsigned itemCount = TallyVisibleItems(); + if(itemCount == 0) { + return; + } - } else { + tp->itemWidth = cp->width; + tp->itemHeight = Max(1, cp->height / itemCount); - TaskEntry *ep; - unsigned itemCount = 0; + if(!tp->labeled) { + tp->itemHeight = Min(tp->itemWidth, tp->itemHeight); + } else { + tp->itemHeight = Min(tp->itemWidth + GetStringHeight(FONT_TASKLIST), tp->itemHeight); + } - tp->itemHeight = cp->height; - for(ep = taskEntries; ep; ep = ep->next) { - if(ShouldShowEntry(ep)) { - itemCount += 1; + if(tp->maxItemWidth > 0) { + tp->itemHeight = Min(tp->maxItemWidth, tp->itemHeight); } + } else { + tp->itemHeight = tp->userHeight > 0 ? tp->userHeight : GetStringHeight(FONT_TASKLIST) + 12; + tp->itemWidth = cp->width; } + } else { + unsigned itemCount = TallyVisibleItems(); if(itemCount == 0) { return; } + tp->itemHeight = cp->height; tp->itemWidth = Max(1, cp->width / itemCount); + if(!tp->labeled) { tp->itemWidth = Min(tp->itemHeight, tp->itemWidth); } + if(tp->maxItemWidth > 0) { tp->itemWidth = Min(tp->maxItemWidth, tp->itemWidth); } @@ -713,7 +738,7 @@ void UpdateTaskBar(void) } for(bp = bars; bp; bp = bp->next) { - if(bp->layout == LAYOUT_VERTICAL) { + if(bp->layout == LAYOUT_VERTICAL && bp->labelPos < LABEL_POSITION_TOP) { TaskEntry *tp; lastHeight = bp->cp->requestedHeight; if(bp->userHeight > 0) { @@ -786,6 +811,7 @@ void Render(const TaskBarType *bp) button.font = FONT_TASKLIST; button.height = bp->itemHeight; button.width = bp->itemWidth; + button.labelPos = bp->labelPos; button.text = NULL; x = 0; @@ -839,6 +865,7 @@ void Render(const TaskBarType *bp) } } DrawButton(&button); + if(displayName) { Release(displayName); } @@ -1045,6 +1072,25 @@ void SetTaskBarLabeled(TrayComponentType *cp, char labeled) bp->labeled = labeled; } +/** Set the label's postion. */ +void SetTaskBarLabelPosition(TrayComponentType *cp, const char *value) +{ + TaskBarType *bp = (TaskBarType*)cp->object; + + Assert(cp); + Assert(value); + + if(!strcmp(value, "right")) { + bp->labelPos = LABEL_POSITION_RIGHT; + } else if(!strcmp(value, "top")) { + bp->labelPos = LABEL_POSITION_TOP; + } else if(!strcmp(value, "bottom")) { + bp->labelPos = LABEL_POSITION_BOTTOM; + } else { + Warning(_("invalid labelpos for TaskList: %s"), value); + } +} + /** Maintain the _NET_CLIENT_LIST[_STACKING] properties on the root. */ void UpdateNetClientList(void) { diff --git a/src/taskbar.h b/src/taskbar.h index 7a762f1b..9c389bee 100644 --- a/src/taskbar.h +++ b/src/taskbar.h @@ -65,6 +65,12 @@ void SetTaskBarHeight(struct TrayComponentType *cp, const char *value); */ void SetTaskBarLabeled(struct TrayComponentType *cp, char value); +/** Set where labels should be positioned in relation to the icon. + * @param cp The task bar component. + * @param value 0 for right of the icon, 1 for above the icon, 2 for below the icon. + */ +void SetTaskBarLabelPosition(struct TrayComponentType *cp, const char *value); + /** Update the _NET_CLIENT_LIST property. */ void UpdateNetClientList(void); diff --git a/src/traybutton.c b/src/traybutton.c index c11ba6b6..6629c5f6 100644 --- a/src/traybutton.c +++ b/src/traybutton.c @@ -296,6 +296,7 @@ void Draw(TrayComponentType *cp) button.y = 0; button.font = FONT_TRAY; button.text = bp->label; + button.labelPos = LABEL_POSITION_RIGHT; button.icon = bp->icon; DrawButton(&button);