mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-08 08:58:00 +00:00
dialog system: add a side-by-side alignment feature.
This will let us put two controls side by side (e.g. in disjoint columns of a multi-col layout) and indicate that instead of the default behaviour of aligning their top edges, their centreline (or, even better if available, font baseline) should be aligned. NFC: nothing uses this yet.
This commit is contained in:
parent
d33f889a56
commit
1276c13e6a
1
dialog.c
1
dialog.c
@ -221,6 +221,7 @@ static union control *ctrl_new(struct controlset *s, int type,
|
||||
c->generic.handler = handler;
|
||||
c->generic.context = context;
|
||||
c->generic.label = NULL;
|
||||
c->generic.align_next_to = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
15
dialog.h
15
dialog.h
@ -113,7 +113,8 @@ typedef void (*handler_fn)(union control *ctrl, dlgparam *dp,
|
||||
int column; \
|
||||
handler_fn handler; \
|
||||
intorptr context; \
|
||||
intorptr helpctx
|
||||
intorptr helpctx; \
|
||||
union control *align_next_to
|
||||
|
||||
union control {
|
||||
/*
|
||||
@ -179,6 +180,18 @@ union control {
|
||||
* to ensure it brings up the right piece of help text.
|
||||
*/
|
||||
intorptr helpctx;
|
||||
/*
|
||||
* Setting this to non-NULL coerces two controls to have their
|
||||
* y-coordinates adjusted so that they can sit alongside each
|
||||
* other and look nicely aligned, even if they're different
|
||||
* heights.
|
||||
*
|
||||
* Set this field on the _second_ control of the pair (in
|
||||
* terms of order in the data structure), so that when it's
|
||||
* instantiated, the first one is already there to be referred
|
||||
* to.
|
||||
*/
|
||||
union control *align_next_to;
|
||||
} generic;
|
||||
struct {
|
||||
STANDARD_PREFIX;
|
||||
|
@ -2491,6 +2491,28 @@ GtkWidget *layout_ctrls(
|
||||
COLUMN_SPAN(ctrl->generic.column));
|
||||
if (left)
|
||||
columns_force_left_align(cols, w);
|
||||
if (ctrl->generic.align_next_to) {
|
||||
/*
|
||||
* Implement align_next_to by simply forcing the two
|
||||
* controls to have the same height of size allocation. At
|
||||
* least for the controls we're currently doing this with,
|
||||
* the GTK layout system will automatically vertically
|
||||
* centre each control within its allocation, which will
|
||||
* get the two controls aligned alongside each other
|
||||
* reasonably well.
|
||||
*/
|
||||
struct uctrl *uc2 = dlg_find_byctrl(
|
||||
dp, ctrl->generic.align_next_to);
|
||||
assert(uc2);
|
||||
columns_force_same_height(cols, w, uc2->toplevel);
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 10, 0)
|
||||
/* Slightly nicer to align baselines than just vertically
|
||||
* centring, where the option is available */
|
||||
gtk_widget_set_valign(w, GTK_ALIGN_BASELINE);
|
||||
gtk_widget_set_valign(uc2->toplevel, GTK_ALIGN_BASELINE);
|
||||
#endif
|
||||
}
|
||||
gtk_widget_show(w);
|
||||
|
||||
uc->toplevel = w;
|
||||
|
@ -1325,6 +1325,28 @@ struct winctrl *winctrl_findbyindex(struct winctrls *wc, int index)
|
||||
return index234(wc->byid, index);
|
||||
}
|
||||
|
||||
static void move_windows(HWND hwnd, int base_id, int num_ids, LONG dy)
|
||||
{
|
||||
if (!dy)
|
||||
return;
|
||||
for (int i = 0; i < num_ids; i++) {
|
||||
HWND win = GetDlgItem(hwnd, base_id + i);
|
||||
|
||||
RECT rect;
|
||||
if (!GetWindowRect(win, &rect))
|
||||
continue;
|
||||
|
||||
POINT p;
|
||||
p.x = rect.left;
|
||||
p.y = rect.top + dy;
|
||||
if (!ScreenToClient(hwnd, &p))
|
||||
continue;
|
||||
|
||||
SetWindowPos(win, NULL, p.x, p.y, 0, 0,
|
||||
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
|
||||
}
|
||||
}
|
||||
|
||||
void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
struct ctlpos *cp, struct controlset *s, int *id)
|
||||
{
|
||||
@ -1340,7 +1362,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
char shortcuts[MAX_SHORTCUTS_PER_CTRL];
|
||||
int nshortcuts;
|
||||
char *escaped;
|
||||
int i, actual_base_id, base_id, num_ids;
|
||||
int i, actual_base_id, base_id, num_ids, align_id_relative;
|
||||
void *data;
|
||||
|
||||
base_id = *id;
|
||||
@ -1349,7 +1371,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
if (s->boxname && *s->boxname) {
|
||||
struct winctrl *c = snew(struct winctrl);
|
||||
c->ctrl = NULL;
|
||||
c->base_id = base_id;
|
||||
c->base_id = c->align_id = base_id;
|
||||
c->num_ids = 1;
|
||||
c->data = NULL;
|
||||
memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts));
|
||||
@ -1362,7 +1384,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
if (!s->boxname && s->boxtitle) {
|
||||
struct winctrl *c = snew(struct winctrl);
|
||||
c->ctrl = NULL;
|
||||
c->base_id = base_id;
|
||||
c->base_id = c->align_id = base_id;
|
||||
c->num_ids = 1;
|
||||
c->data = dupstr(s->boxtitle);
|
||||
memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts));
|
||||
@ -1491,6 +1513,11 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
/* Almost all controls start at base_id. */
|
||||
actual_base_id = base_id;
|
||||
|
||||
/* For vertical alignment purposes, the most relevant control
|
||||
* in a group is usually the last one. But that can be
|
||||
* overridden occasionally. */
|
||||
align_id_relative = -1;
|
||||
|
||||
/*
|
||||
* Now we're ready to actually create the control, by
|
||||
* switching on its type.
|
||||
@ -1659,6 +1686,10 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
unreachable("bad control type in winctrl_layout");
|
||||
}
|
||||
|
||||
/* Translate the original align_id_relative of -1 into n-1 */
|
||||
if (align_id_relative < 0)
|
||||
align_id_relative += num_ids;
|
||||
|
||||
/*
|
||||
* Create a `struct winctrl' for this control, and advance
|
||||
* the dialog ID counter, if it's actually been created
|
||||
@ -1669,6 +1700,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
|
||||
c->ctrl = ctrl;
|
||||
c->base_id = actual_base_id;
|
||||
c->align_id = c->base_id + align_id_relative;
|
||||
c->num_ids = num_ids;
|
||||
c->data = data;
|
||||
memcpy(c->shortcuts, shortcuts, sizeof(shortcuts));
|
||||
@ -1676,6 +1708,31 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
winctrl_add_shortcuts(dp, c);
|
||||
if (actual_base_id == base_id)
|
||||
base_id += num_ids;
|
||||
|
||||
if (ctrl->generic.align_next_to) {
|
||||
/*
|
||||
* Implement align_next_to by looking at the y extents
|
||||
* of the two controls now that both are created, and
|
||||
* moving one or the other downwards so that they're
|
||||
* centred on a common horizontal line.
|
||||
*/
|
||||
struct winctrl *c2 = winctrl_findbyctrl(
|
||||
wc, ctrl->generic.align_next_to);
|
||||
HWND win1 = GetDlgItem(pos.hwnd, c->align_id);
|
||||
HWND win2 = GetDlgItem(pos.hwnd, c2->align_id);
|
||||
RECT rect1, rect2;
|
||||
if (win1 && win2 &&
|
||||
GetWindowRect(win1, &rect1) &&
|
||||
GetWindowRect(win2, &rect2)) {
|
||||
LONG top = (rect1.top < rect2.top ? rect1.top : rect2.top);
|
||||
LONG bottom = (rect1.bottom > rect2.bottom ?
|
||||
rect1.bottom : rect2.bottom);
|
||||
move_windows(pos.hwnd, c->base_id, c->num_ids,
|
||||
(top + bottom - rect1.top - rect1.bottom)/2);
|
||||
move_windows(pos.hwnd, c2->base_id, c2->num_ids,
|
||||
(top + bottom - rect2.top - rect2.bottom)/2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sfree(data);
|
||||
}
|
||||
|
@ -491,6 +491,12 @@ struct winctrl {
|
||||
*/
|
||||
int base_id;
|
||||
int num_ids;
|
||||
/*
|
||||
* For vertical alignment, the id of a particular representative
|
||||
* control that has the y-extent of the sensible part of the
|
||||
* control.
|
||||
*/
|
||||
int align_id;
|
||||
/*
|
||||
* Remember what keyboard shortcuts were used by this control,
|
||||
* so that when we remove it again we can take them out of the
|
||||
|
Loading…
Reference in New Issue
Block a user