couple classes. Hopefully it will be much easier to tune the layouts now.
Fixed problem I introduced earlier today that didn't let message pane use the bottom-most line. Stole a couple cols from the HUD and one from the HUD gutter to potentially give to the mlist or to the main view. Stole a row from the message area so even 80x24 players get get one line of monster list
Made the mlist-col layout not so unfair to the main view, and not truncate the mlist width so much. It used to pop in if you had an 81-col display; now the display needs to be larger (~95) for it to be used, but it is a lot more functional.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3972 c06c8d41-db1a-0410-9941-cceddc491573
PUZ35HA537R2HZPB7SZ5KLHDOR2DXNQG7CM2UQ7LIHF55GR7LN5QC
4B3ANMEDTZ3QVQJUCDFCPS4MNAPALLQMVF5K7TRTCRBLLBDOFNBAC
QJR4HINDRTZC7FMV6V27HBPBSLCAKBGPSMH34WPVNPRPKWXVVEOAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
EOMCPVNQLX3IMLC46EAO67DPBH5KEG2FQTPBLGU62HIRWA3UQ7XQC
FWVE7KM7BGEZUFQVM7H7UFGM3QMMPT7QHLNXSP62HG3SMBIPZBSQC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
W6IY6LF3MREPXC23AAKA2BJNUCJYCSOWY55DIWJWFLUEE2Y3LGNQC
4UQBOVCMAMNCCF6PPX222H6JJO7MPYOKKC2NBKMVYIT5R5FOACNAC
ELMP33CXUMJE3D5XQXR3GWKNAVYTBA4E5BFDVCZ5KKWKKGMGYIHQC
24IYVB5D2PTO6RJHVWDRSIYRVNDWDGGWI4YCYVUDIXY5MRZ4OJEQC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
cgotoxy(1,1, GOTO_MLIST);
cprintf("+ L");
cgotoxy(crawl_view.mlistsz.x-4, crawl_view.mlistsz.y, GOTO_MLIST);
cprintf("L +");
if (crawl_view.mlistsz.y > 0)
{
textcolor(WHITE);
cgotoxy(1,1, GOTO_MLIST);
cprintf("+ L");
cgotoxy(crawl_view.mlistsz.x-4, crawl_view.mlistsz.y, GOTO_MLIST);
cprintf("L +");
}
// already defined in header
// const int crawl_view_geometry::message_min_lines;
// const int crawl_view_geometry::hud_min_width;
// const int crawl_view_geometry::hud_min_gutter;
// const int crawl_view_geometry::hud_max_gutter;
// Moved from direct.h, where they didn't need to be.
// define VIEW_MIN_HEIGHT defined elsewhere
// define VIEW_MAX_HEIGHT use Options.view_max_height
// define VIEW_MIN_WIDTH defined elsewhere
// define VIEW_MAX_WIDTH use Options.view_max_width
#define HUD_WIDTH 39
#define HUD_HEIGHT 17
#define MSG_MIN_HEIGHT 6
// #define MLIST_MIN_HEIGHT use Options.mlist_min_height
#define MLIST_MIN_WIDTH 25 /* non-inline layout only */
#define MLIST_MAX_WIDTH 42
#define MLIST_GUTTER 1
#define HUD_MIN_GUTTER 2
#define HUD_MAX_GUTTER 4
// Helper for layouts. Tries to increment lvalue without overflowing it.
static void _increment(int& lvalue, int delta, int max_value)
{
lvalue = std::min(lvalue+delta, max_value);
}
class _layout
{
public:
_layout(coord_def termsz_) :
termp(1,1), termsz(termsz_),
viewp(-1,-1), viewsz(VIEW_MIN_WIDTH, VIEW_MIN_HEIGHT),
hudp(-1,-1), hudsz(HUD_WIDTH, HUD_HEIGHT),
msgp(-1,-1), msgsz(0, MSG_MIN_HEIGHT),
mlistp(-1,-1), mlistsz(MLIST_MIN_WIDTH, 0),
hud_gutter(HUD_MIN_GUTTER),
valid(false) {}
protected:
void _assert_validity() const
{
#ifndef USE_TILE
// Check that all the panes fit in the view.
ASSERT( (viewp+viewsz - termp).x <= termsz.x );
ASSERT( (viewp+viewsz - termp).y <= termsz.y );
ASSERT( (hudp+hudsz - termp).x <= termsz.x );
ASSERT( (hudp+hudsz - termp).y <= termsz.y );
ASSERT( (msgp+msgsz - termp).x <= termsz.x );
ASSERT( (msgp+msgsz - termp).y <= termsz.y );
// Don't stretch message all the way to the bottom-right
// character; it causes scrolling and badness.
ASSERT( (msgp+msgsz - termp) != termsz );
ASSERT( (mlistp+mlistsz-termp).x <= termsz.x );
ASSERT( (mlistp+mlistsz-termp).y <= termsz.y );
#endif
}
public:
const coord_def termp, termsz;
coord_def viewp, viewsz;
coord_def hudp;
const coord_def hudsz;
coord_def mlistp, mlistsz;
coord_def msgp, msgsz;
int hud_gutter;
bool valid;
};
// vvvvvvghhh v=view, g=hud gutter, h=hud, l=list, m=msg
// vvvvvvghhh
// vvvvvv lll
// lll
// mmmmmmmmmm
class _inline_layout : public _layout
{
public:
_inline_layout(coord_def c) : _layout(c) { valid = _init(); }
bool _init()
{
// x: View gets leftover; then mlist; then hud gutter
if (leftover_x() < 0) return false;
_increment(viewsz.x, leftover_x(), Options.view_max_width);
if ((viewsz.x % 2) != 1) --viewsz.x;
mlistsz.x = hudsz.x;
_increment(mlistsz.x, leftover_x(), MLIST_MAX_WIDTH);
_increment(hud_gutter, leftover_x(), HUD_MAX_GUTTER);
_increment(mlistsz.x, leftover_x(), INT_MAX);
msgsz.x = termsz.x-1; // Can't use last character
// y: View gets leftover; then mlist; then message
// mlist might be left with less than MLIST_MIN_HEIGHT, but
// that's the breaks.
if (leftover_y() < 0) { return false; }
{
const int saved_leftover_y = leftover_y();
_increment(viewsz.y, saved_leftover_y, Options.view_max_height);
if ((viewsz.y % 2) != 1) --viewsz.y;
if (mlistsz.y < Options.mlist_min_height)
{
_increment(mlistsz.y, saved_leftover_y, Options.mlist_min_height);
}
}
_increment(msgsz.y, leftover_y(), INT_MAX);
// Finish off by doing the positions
viewp = termp;
msgp = termp + coord_def(0, std::max(viewsz.y, hudsz.y+mlistsz.y));
hudp = viewp + coord_def(viewsz.x+hud_gutter, 0);
mlistp = hudp + coord_def(0, hudsz.y);
_assert_validity();
return true;
}
int leftover_x() const
{
int width = (viewsz.x + hud_gutter + std::max(hudsz.x, mlistsz.x));
return (termsz.x - width);
}
int leftover_y() const
{
// hud+mlist can grow longer than the view, believe it or not
int height = std::max(viewsz.y, hudsz.y+mlistsz.y) + msgsz.y;
return (termsz.y - height);
}
};
// ll vvvvvvghhh v=view, g=hud gutter, h=hud, l=list, m=msg
// ll vvvvvvghhh
// ll vvvvvv
// mmmmmmmmmmmmm
class _mlist_col_layout : public _layout
{
public:
_mlist_col_layout(coord_def c) : _layout(c) { valid = _init(); }
bool _init()
{
// Don't let the mlist column steal all the width. Up front,
// take some for the view. If it makes the layout fail, that's fine.
_increment(viewsz.x, MLIST_MIN_WIDTH/2, Options.view_max_width);
// x: View and mlist share leftover; then hud gutter
if (leftover_x() < 0) return false;
_increment(mlistsz.x, leftover_x()/2, MLIST_MAX_WIDTH);
_increment(viewsz.x, leftover_x(), Options.view_max_width);
if ((viewsz.x % 2) != 1) --viewsz.x;
_increment(mlistsz.x, leftover_x(), MLIST_MAX_WIDTH);
_increment(hud_gutter, leftover_x(), HUD_MAX_GUTTER);
msgsz.x = termsz.x-1; // Can't use last character
// y: View gets leftover; then message
if (leftover_y() < 0) return false;
_increment(viewsz.y, leftover_y(), Options.view_max_height);
if ((viewsz.y % 2) != 1) --viewsz.y;
_increment(msgsz.y, leftover_y(), INT_MAX);
mlistsz.y = viewsz.y;
// Finish off by doing the positions
mlistp = termp;
viewp = mlistp+ coord_def(mlistsz.x+MLIST_GUTTER, 0);
msgp = termp + coord_def(0, viewsz.y);
hudp = viewp + coord_def(viewsz.x+hud_gutter, 0);
_assert_validity();
return true;
}
private:
int leftover_x() const
{
int width = (mlistsz.x + MLIST_GUTTER + viewsz.x + hud_gutter + hudsz.x);
return (termsz.x - width);
}
int leftover_y() const
{
const int top_y = std::max(std::max(viewsz.y, hudsz.y), mlistsz.y);
const int height = top_y + msgsz.y;
return (termsz.y - height);
}
};
#ifndef USE_TILE
// If the terminal is too small, exit with an error.
if ((termsz.x < 80 || termsz.y < 24) && !crawl_state.need_save)
end(1, false, "Terminal too small (%d,%d), need at least (80,24)",
termsz.x, termsz.y);
#endif
int freeheight = termsz.y - message_min_lines;
// Make the viewport as tall as possible.
viewsz.y = freeheight < Options.view_max_height?
freeheight : Options.view_max_height;
// Make sure we're odd-sized.
if (!(viewsz.y % 2))
--viewsz.y;
// If the view is too short, force it to minimum height.
if (viewsz.y < VIEW_MIN_HEIGHT)
viewsz.y = VIEW_MIN_HEIGHT;
// Determine if the monster list can have its own column.
int freewidth = termsz.x - (viewsz.x + mlist_gutter +
mlist_min_width + hud_min_gutter + hudsz.x);
mlist_inline = (freewidth < 0) || Options.mlist_force_inline;
if (mlist_inline)
freewidth = termsz.x - (hud_min_width + hud_min_gutter);
// Make the viewport as wide as possible.
viewsz.x = freewidth < Options.view_max_width?
freewidth : Options.view_max_width;
if (!(viewsz.x % 2))
--viewsz.x;
if (viewsz.x < VIEW_MIN_WIDTH)
viewsz.x = VIEW_MIN_WIDTH;
vbuf.size(viewsz);
if (!mlist_inline)
{
mlistp = termp;
const int _mlist_max_width = mlist_max_width; // work around link problem?
mlistsz.x = std::min(mlist_min_width + freewidth, _mlist_max_width);
mlistsz.y = viewsz.y;
// The hud appears after the viewport + gutter and possibly the mlist.
hudp = coord_def(viewp.x + viewsz.x + hud_min_gutter, 1);
// HUD size never changes, but we may increase the gutter size (up to
// the current max of 6).
if (hudp.x + hudsz.x - 1 < termsz.x)
{
const int hudmarg = termsz.x - (hudp.x + hudsz.x - 1);
const int hud_increase_max = hud_max_gutter - hud_min_gutter;
hudp.x += hudmarg > hud_increase_max? hud_increase_max : hudmarg;
}
if (mlist_inline)
if (! lay_inline.valid)
// Monster list takes up all space between the hud and the message pane
mlistp = coord_def(hudp.x, hudp.y + hudsz.y);
// Try to grow the length of the monster list based on the options,
// but only grow if there's room for the minimum message lines.
int len = 1 + viewsz.y - mlistp.y;
len = std::max(len, Options.mlist_min_height);
len = std::min(len, 1 + termsz.y - message_min_lines - (hudp.y + hudsz.y));
mlistsz = coord_def(termsz.x - (mlistp.x - 1), len);
#ifndef USE_TILE
// Terminal too small; exit with an error
if (!crawl_state.need_save)
{
end(1, false, "Terminal too small (%d,%d); need at least (%d,%d)",
termsz.x, termsz.y,
termsz.x + std::max(0, -lay_inline.leftover_x()),
termsz.y + std::max(0, -lay_inline.leftover_y()));
}
#endif
// Calculate message area
{
// Always from lhs of term to rhs of term
msgp.x = termp.x;
msgsz.x = termsz.x;
// From bottom of mlist or view, whichever is lower, to bottom of term
msgp.y = std::max((mlistp+mlistsz).y, (viewp+viewsz).y);
msgsz.y = termsz.y - (msgp-termp).y;
const _layout* winner = &lay_inline;
if (!Options.mlist_force_inline && lay_mlist.valid)
winner = &lay_mlist;
// Well, actually not to the very bottom. Drawing the last line causes
// the screen to scroll. If this is fixed, we get another line of message.
msgsz.y -= 1;
}
msgp = winner->msgp;
msgsz = winner->msgsz;
viewp = winner->viewp;
viewsz = winner->viewsz;
hudp = winner->hudp;
// hudsz = winner->hudsz; size is constant
mlistp = winner->mlistp;
mlistsz = winner->mlistsz;
#ifndef USE_TILE
// Check that all the panes fit in the view. Note that currently
// no pane is allowed to stretch all the way to the bottom (see
// comment by message pane calculation, above.
ASSERT( (viewp+viewsz-termp).x <= termsz.x );
ASSERT( (viewp+viewsz-termp).y <= termsz.y-1 );
ASSERT( (hudp+hudsz-termp).x <= termsz.x );
ASSERT( (hudp+hudsz-termp).y <= termsz.y-1 );
ASSERT( (msgp+msgsz-termp).x <= termsz.x );
ASSERT( (msgp+msgsz-termp).y <= termsz.y-1 );
ASSERT( (mlistp+mlistsz-termp).x <= termsz.x );
ASSERT( (mlistp+mlistsz-termp).y <= termsz.y-1 );
#endif
return;
static const int message_min_lines = 7;
static const int hud_min_width = 41;
static const int hud_min_gutter = 3;
static const int hud_max_gutter = 6;
static const int mlist_gutter = 1;
static const int mlist_min_width = 14;
static const int mlist_max_width = 26;