Fix default size_range() calculation (issue #392, STR 3352)

This commit is contained in:
Albrecht Schlosser 2022-02-22 23:28:04 +01:00
parent d4ceb20ad3
commit 091712bea8
10 changed files with 257 additions and 139 deletions

View File

@ -163,9 +163,12 @@ public:
See the \ref resize chapter for more examples and detailed explanation.
\note The resizable() widget of a window can also affect the window's
resizing behavior if Fl_Window::size_range() is not called.
resizing behavior if Fl_Window::size_range() is not called. Please
see Fl_Window::default_size_range() for more information on how the
default size range is calculated.
\see Fl_Window::size_range()
\see Fl_Window::default_size_range()
*/
void resizable(Fl_Widget* o) {resizable_ = o;}
/**

View File

@ -1,7 +1,7 @@
//
// Window header file for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2016 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -74,10 +74,12 @@ private:
const char* iconlabel_;
char* xclass_;
// size_range stuff:
int minw, minh, maxw, maxh;
int dw, dh, aspect;
uchar size_range_set;
// private size_range stuff:
int minw_, minh_, maxw_, maxh_;
int dw_, dh_, aspect_;
uchar size_range_set_; // true (1) if size_range() has been set or calculated
// cursor stuff
Fl_Cursor cursor_default;
@ -119,6 +121,9 @@ protected:
void free_icons();
void default_size_range(); // calculate size_range() if not set explicitly
int is_resizable(); // calculate size_range() and return whether this is resizable
public:
/**
@ -321,6 +326,7 @@ public:
\deprecated please use force_position(0) instead
*/
void free_position() {clear_flag(FORCE_POSITION);}
void size_range(int minw, int minh, int maxw=0, int maxh=0, int dw=0, int dh=0, int aspect=0);
/** See void Fl_Window::label(const char*) */

View File

@ -1,7 +1,7 @@
//
// Window widget class for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2021 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -54,8 +54,8 @@ void Fl_Window::_Fl_Window() {
xclass_ = 0;
iconlabel_ = 0;
resizable(0);
size_range_set = 0;
minw = maxw = minh = maxh = 0;
size_range_set_ = 0;
minw_ = maxw_ = minh_ = maxh_ = 0;
no_fullscreen_x = 0;
no_fullscreen_y = 0;
no_fullscreen_w = w();
@ -542,6 +542,8 @@ void Fl_Window::show() {
labeltype(FL_NO_LABEL);
}
Fl_Tooltip::exit(this);
if (!shown())
default_size_range();
pWindowDriver->show();
}
@ -600,32 +602,24 @@ int Fl_Window::handle(int ev)
It is undefined what happens if the current window size does not fit
in the constraints passed to size_range().
If this function is \b not called, FLTK tries to figure out the range
from the setting of the window's resizable() widget:
We recommend to call size_range() if you have a resizable() widget
in a main window.
- If resizable() is NULL (this is the default) then the window cannot
be resized and the resize border and max-size control will not be
displayed for the window.
If this function is \b not called, FLTK tries to figure out the range.
Please see the protected method default_size_range() for details.
- If either dimension of resizable() is zero, then the minimum size is
also the maximum size (so the window cannot resize in that direction).
- Otherwise the size of the resizable is irrelevant and the window's
minimum size is the current size and the maximum size is unlimited
like if size_range(w(), h(), 0, 0) was called.
\param[in] minWidth, minHeight The smallest the window can be.
\param[in] minWidth,minHeight The smallest the window can be.
Either value must be greater than 0.
\param[in] maxWidth, maxHeight The largest the window can be. If either
\param[in] maxWidth,maxHeight The largest the window can be. If either
is equal to the minimum then you cannot resize in that direction.
If either is zero then FLTK picks a maximum size in that direction
such that the window will fill the screen.
\param[in] deltaX, deltaY These are size increments. The window will be
\param[in] deltaX,deltaY These are size increments. The window will be
constrained to widths of <tt>minWidth + N * deltaX</tt>, where N is any
non-negative integer. If these are less or equal to 1 they are ignored
(this is ignored on Windows).
(this is always ignored on Windows).
\param[in] aspectRatio A flag that indicates that the window should preserve
its aspect ratio. This only works if both the maximum and minimum have
@ -634,14 +628,181 @@ int Fl_Window::handle(int ev)
void Fl_Window::size_range(int minWidth, int minHeight,
int maxWidth, int maxHeight,
int deltaX, int deltaY, int aspectRatio) {
minw = minWidth;
minh = minHeight;
maxw = maxWidth;
maxh = maxHeight;
dw = deltaX;
dh = deltaY;
aspect = aspectRatio;
pWindowDriver->size_range();
minw_ = minWidth;
minh_ = minHeight;
maxw_ = maxWidth;
maxh_ = maxHeight;
dw_ = deltaX;
dh_ = deltaY;
aspect_ = aspectRatio;
size_range_set_ = 1;
pWindowDriver->size_range(); // platform specific stuff
}
/**
Protected method to calculate the default size range of a window.
This method is called internally prior to showing a window to ensure that
the window's size range values are calculated if a resizable() widget has
been set but size_range() has not been called explicitly.
This method does nothing if size_range() has been called before.
Otherwise FLTK tries to figure out the window's size range from the
setting of the window's resizable() widget as follows and roughly in
the given order.
-# If resizable() is NULL (this is the default) then the window cannot
be resized and the resize border and max-size control will not be
displayed for the window.
-# If either dimension of resizable() is zero, then the window cannot
resize in that direction.
-# The resizable() widget is clipped to the window area.
-# The non-resizable portion of the window is calculated as the difference
of the window's size and the clipped resizable() widget's size.
-# If either dimension of the clipped resizable() widget is greater
than 100, then 100 is considered its minimum width/height. This
allows the resizable widget to shrink below its original size.
-# Finally the minimum width/height of the window is set to the
non-resizable portion plus the width/height of the resizable()
widget as calculated above.
In simple words:
- It is assumed that the resizable() widget can be indefinitely
enlarged and/or shrunk to a minimum width/height of 100 unless
it is smaller than that, which is then considered the minimum.
- The window's size_range() minimum values are set to the sum
of the non-resizable portion of the window and the previously
calculated minimum size of the resizable() widget.
Examples:
\code
Fl_Window win(400, 400);
win.resizable(win);
// win.size_range(100, 100, 0, 0);
\endcode
The minimum size of the resizable is 100, hence the minimum size
of the total window is also 100 in both directions.
\code
Fl_Window win(400, 400);
Fl_Box box(20, 20, 360, 360);
win.resizable(box);
// win.size_range(140, 140, 0, 0);
\endcode
The calculated minimum width and height would be 20 + 100 + 20 in both
dimensions.
\code
Fl_Window win(400, 400);
Fl_Box box(200, 0, 500, 300); // note: width 500 too large: clipped
win.resizable(box);
// win.size_range(300, 200, 0, 0);
\endcode
The width of the resizable is clipped to 200, hence the minimum size of
the total window is also 200 (fix) + 100 (min. resizable) in x direction.
The minimum value in y direction is 100 (resizable) + 100 (fixed part).
The calculation is based on clipping the resizable widget to the window
area to prevent programming errors and the assumption that the resizable
widget can be shrunk to 100x100 or its original size, whichever is smaller.
If this is not what you want, please use Fl_Window::size_range()
explicitly so you can set any appropriate range.
*/
void Fl_Window::default_size_range() {
if (size_range_set_)
return;
if (!resizable()) {
size_range(w(), h(), w(), h());
return;
}
// Calculate default size range depending on the resizable() widget
Fl_Widget *r = resizable();
int maxw = 0;
int maxh = 0;
// Clip the resizable() widget to the window
int L = r->x();
int R = L + r->w();
if (R < 0 || L > w()) R = L; // outside the window
else {
if (L < 0) L = 0;
if (R > w()) R = w();
}
int rw = R - L;
int T = r->y();
int B = T + r->h();
if (B < 0 || T > h()) B = T; // outside the window
else {
if (T < 0) T = 0;
if (B > h()) B = h();
}
int rh = B - T;
// Calculate the non-resizable part of the window (STR 3352)
// before reducing the size of the resizable widget !
int minw = w() - rw;
int minh = h() - rh;
// Limit the resizable dimensions to 100x100 according to the docs.
// This makes the resizable widget shrinkable, otherwise it would
// only be able to grow (issue #392)
if (rw > 100) rw = 100;
if (rh > 100) rh = 100;
// Add the clipped resizable() width/height so we have at least
// the non-resizable part + the clipped resizable() size
minw += rw;
minh += rh;
// Disable resizing in the respective directions if any dimension
// of the resizable widget is zero (see docs)
if (r->w() == 0) minw = maxw = w();
if (r->h() == 0) minh = maxh = h();
// Finally set the size range
size_range(minw, minh, maxw, maxh);
}
/**
Protected method to determine whether a window is resizable.
If size_range() has not yet been called this method calculates the
default size range values by calling default_size_range().
This method is for internal use only. The returned value is a bit mask
and non-zero if the window is resizable in at least one direction.
\return non-zero if the window is resizable
\retval 0 the window is not resizable
\retval 1 the window is resizable in horizontal direction (w)
\retval 2 the window is resizable in vertical direction (h)
\retval 3 the window is resizable in both directions (w and h)
\see default_size_range()
*/
int Fl_Window::is_resizable() {
default_size_range();
int ret = 0;
if (minw_ != maxw_) ret |= 1;
if (minh_ != maxh_) ret |= 2;
return ret;
}
/** The number of the screen containing the mapped window */

View File

@ -97,11 +97,12 @@ public:
int dw();
int dh();
int aspect();
unsigned char size_range_set();
int is_resizable() { return pWindow->is_resizable(); }
int fullscreen_screen_top();
int fullscreen_screen_bottom();
int fullscreen_screen_left();
int fullscreen_screen_right();
unsigned char size_range_set();
int force_position();
void force_position(int c);
void x(int X);

View File

@ -2,7 +2,7 @@
// A base class for platform specific window handling code
// for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2018 by Bill Spitzak and others.
// Copyright 1998-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -33,29 +33,34 @@ extern void fl_throw_focus(Fl_Widget *o);
/**
Create a new Window Driver.
This calls should be derived into a new class that manages desktop windows
This class must be derived into a new class that manages windows
on the target platform.
*/
Fl_Window_Driver::Fl_Window_Driver(Fl_Window *win) :
pWindow(win)
{
Fl_Window_Driver::Fl_Window_Driver(Fl_Window *win)
: pWindow(win) {
shape_data_ = NULL;
wait_for_expose_value = 0;
other_xid = 0;
}
Fl_Window_Driver::~Fl_Window_Driver()
{
Fl_Window_Driver::~Fl_Window_Driver() {
// empty
}
int Fl_Window_Driver::minw() {return pWindow->minw;}
int Fl_Window_Driver::minh() {return pWindow->minh;}
int Fl_Window_Driver::maxw() {return pWindow->maxw;}
int Fl_Window_Driver::maxh() {return pWindow->maxh;}
int Fl_Window_Driver::dw() {return pWindow->dw;}
int Fl_Window_Driver::dh() {return pWindow->dh;}
int Fl_Window_Driver::aspect() {return pWindow->aspect;}
// accessors to Fl_Window's size_range stuff
int Fl_Window_Driver::minw() {return pWindow->minw_;}
int Fl_Window_Driver::minh() {return pWindow->minh_;}
int Fl_Window_Driver::maxw() {return pWindow->maxw_;}
int Fl_Window_Driver::maxh() {return pWindow->maxh_;}
int Fl_Window_Driver::dw() {return pWindow->dw_;}
int Fl_Window_Driver::dh() {return pWindow->dh_;}
int Fl_Window_Driver::aspect() {return pWindow->aspect_;}
unsigned char Fl_Window_Driver::size_range_set() {return pWindow->size_range_set_;}
// other Fl_Window accessors
int Fl_Window_Driver::force_position() {return pWindow->force_position(); }
void Fl_Window_Driver::force_position(int c) { pWindow->force_position(c); }
void Fl_Window_Driver::x(int X) {pWindow->x(X); }
@ -66,23 +71,19 @@ int Fl_Window_Driver::fullscreen_screen_left() {return pWindow->fullscreen_scree
int Fl_Window_Driver::fullscreen_screen_right() {return pWindow->fullscreen_screen_right;}
void Fl_Window_Driver::current(Fl_Window *c) {pWindow->current_ = c;}
unsigned char Fl_Window_Driver::size_range_set() {return pWindow->size_range_set;}
void Fl_Window_Driver::flush_Fl_Window() { pWindow->Fl_Window::flush(); }
/**
Draw the window content.
A new driver can add code before or after drawing an individua window.
A new driver can add code before or after drawing an individual window.
*/
void Fl_Window_Driver::draw() { pWindow->draw(); }
/**
Prepare this window for rendering.
A new driver may prepare bitmaps and clipping areas for calls to the
Graphics driver.
graphics driver.
*/
void Fl_Window_Driver::make_current() { }
@ -94,42 +95,31 @@ void Fl_Window_Driver::show() { }
/**
Change the window title.
A new drive should provide an interface to change the title of the window
A new driver should provide an interface to change the title of the window
in the title bar.
*/
void Fl_Window_Driver::label(const char *name, const char *mininame) {}
void Fl_Window_Driver::take_focus()
{
void Fl_Window_Driver::take_focus() {
// nothing to do
}
void Fl_Window_Driver::flush_double()
{
void Fl_Window_Driver::flush_double() {
flush_Fl_Window();
}
void Fl_Window_Driver::flush_overlay()
{
void Fl_Window_Driver::flush_overlay() {
flush_Fl_Window();
}
void Fl_Window_Driver::draw_begin()
{
void Fl_Window_Driver::draw_begin() {
// nothing to do
}
void Fl_Window_Driver::draw_end()
{
void Fl_Window_Driver::draw_end() {
// nothing to do
}
void Fl_Window_Driver::destroy_double_buffer() {
fl_delete_offscreen(other_xid);
other_xid = 0;
@ -193,7 +183,8 @@ void Fl_Window_Driver::use_border() {
}
void Fl_Window_Driver::size_range() {
pWindow->size_range_set = 1;
// *FIXME* This should not be necessary!
// pWindow->size_range_set = 1;
}
int Fl_Window_Driver::can_do_overlay() {
@ -252,7 +243,6 @@ void Fl_Window_Driver::resize_after_scale_change(int ns, float old_f, float new_
else if (Y+H/2 > sY+sH-1) Y = sY+sH-1-H/2-d;
}
is_a_rescale_ = true;
size_range();
pWindow->resize(X, Y, W, H);
is_a_rescale_ = false;
}

View File

@ -2953,8 +2953,13 @@ Fl_X* Fl_Cocoa_Window_Driver::makeWindow()
w->border(0);
show_iconic(0);
}
if (w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
else winstyle = NSBorderlessWindowMask;
if (w->border()) {
winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
if (is_resizable())
winstyle |= NSResizableWindowMask;
} else {
winstyle = NSBorderlessWindowMask;
}
if (show_iconic() && !w->parent()) { // prevent window from being out of work area when created iconized
int sx, sy, sw, sh;
Fl::screen_work_area (sx, sy, sw, sh, w->x(), w->y());
@ -2965,23 +2970,7 @@ Fl_X* Fl_Cocoa_Window_Driver::makeWindow()
int yp = w->y();
int wp = w->w();
int hp = w->h();
if (size_range_set()) {
if ( minh() != maxh() || minw() != maxw()) {
if (w->border()) winstyle |= NSResizableWindowMask;
}
} else {
if (w->resizable()) {
Fl_Widget *o = w->resizable();
int minw = w->w(); // minw is window's initial width
int minh = w->h(); // minh is window's initial height
int maxw = (o->w() == 0) ? minw : 0; // if resizable w()==0, disable resize w()
int maxh = (o->h() == 0) ? minh : 0; // if resizable h()==0, disable resize h()
w->size_range(minw, minh, maxw, maxh);
if (w->border()) winstyle |= NSResizableWindowMask;
} else {
w->size_range(w->w(), w->h(), w->w(), w->h());
}
}
int xwm = xp, ywm = yp, bt, bx, by;
if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) {
@ -3291,7 +3280,6 @@ void Fl_Cocoa_Window_Driver::use_border() {
* Tell the OS what window sizes we want to allow
*/
void Fl_Cocoa_Window_Driver::size_range() {
Fl_Window_Driver::size_range();
Fl_X *i = Fl_X::i(pWindow);
if (i && i->xid) {
float s = Fl::screen_driver()->scale(0);

View File

@ -1908,8 +1908,6 @@ void Fl_WinAPI_Window_Driver::resize(int X, int Y, int W, int H) {
if (!border())
flags |= SWP_NOACTIVATE;
if (resize_from_program && shown()) {
if (!pWindow->resizable())
pWindow->size_range(w(), h(), w(), h());
int dummy_x, dummy_y, bt, bx, by;
// compute window position and size in scaled units
float s = Fl::screen_driver()->scale(screen_num());
@ -2073,24 +2071,12 @@ Fl_X *Fl_WinAPI_Window_Driver::makeWindow() {
style |= WS_CHILD;
styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
parent = fl_xid(w->window());
} else {
if (!size_range_set()) {
if (w->resizable()) {
Fl_Widget *o = w->resizable();
int minw = w->w(); // minw is window's initial width
int minh = w->h(); // minh is window's initial height
int maxw = (o->w() == 0) ? minw : 0; // if resizable w()==0, disable resize w()
int maxh = (o->h() == 0) ? minh : 0; // if resizable h()==0, disable resize h()
w->size_range(minw, minh, maxw, maxh);
} else {
w->size_range(w->w(), w->h(), w->w(), w->h());
}
}
} else { // top level window
styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
int wintype = 0;
if (w->border() && !w->parent()) {
if (size_range_set() && (maxw() != minw() || maxh() != minh()))
if (w->border()) {
if (is_resizable())
wintype = 2;
else
wintype = 1;

View File

@ -2356,14 +2356,9 @@ void Fl_X11_Window_Driver::resize(int X,int Y,int W,int H) {
}
}
if (resize_from_program && is_a_resize && !pWindow->resizable()) {
pWindow->size_range(w(), h(), w(), h());
}
if (resize_from_program && shown()) {
float s = Fl::screen_driver()->scale(screen_num());
if (is_a_resize) {
if (!pWindow->resizable()) pWindow->size_range(w(), h(), w(), h());
if (is_a_move) {
XMoveResizeWindow(fl_display, fl_xid(pWindow), rint(X*s), rint(Y*s), W>0 ? W*s : 1, H>0 ? H*s : 1);
} else {
@ -2672,7 +2667,7 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
}
Fl_X11_Window_Driver::driver(win)->screen_num(nscreen);
s = Fl::screen_driver()->scale(nscreen);
//if (!win->parent()) printf("win creation on screen #%d\n", nscreen);
// if (!win->parent()) printf("win creation on screen #%d\n", nscreen);
#endif
Fl_X* xp =
set_xid(win, XCreateWindow(fl_display,
@ -2823,33 +2818,19 @@ void Fl_X11_Window_Driver::sendxjunk() {
Fl_Window *w = pWindow;
if (w->parent() || w->override()) return; // it's not a window manager window!
if (!size_range_set()) { // default size_range based on resizable():
if (w->resizable()) {
Fl_Widget *o = w->resizable();
int minw = w->w(); // minw is window's initial width
int minh = w->h(); // minh is window's initial height
int maxw = (o->w() == 0) ? minw : 0; // if resizable w()==0, disable resize w()
int maxh = (o->h() == 0) ? minh : 0; // if resizable h()==0, disable resize h()
w->size_range(minw, minh, maxw, maxh);
} else {
w->size_range(w->w(), w->h(), w->w(), w->h());
}
return; // because this recursively called here
}
XSizeHints *hints = XAllocSizeHints();
// memset(&hints, 0, sizeof(hints)); jreiser suggestion to fix purify?
float s = Fl::screen_driver()->scale(screen_num());
hints->min_width = s*minw();
hints->min_height = s*minh();
hints->max_width = s*maxw();
hints->max_height = s*maxh();
hints->min_width = s * minw();
hints->min_height = s * minh();
hints->max_width = s * maxw();
hints->max_height = s * maxh();
if (int(s) == s) { // use win size increment value only if scale is an integer. Is it possible to do better?
hints->width_inc = s*dw();
hints->height_inc = s*dh();
hints->width_inc = s * dw();
hints->height_inc = s * dh();
} else {
hints->width_inc = 0;
hints->width_inc = 0;
hints->height_inc = 0;
}

View File

@ -2,7 +2,7 @@
// Definition of Windows window driver
// for the Fast Light Tool Kit (FLTK).
//
// Copyright 2010-2018 by Bill Spitzak and others.
// Copyright 2010-2022 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@ -89,6 +89,9 @@ public:
// --- window management
virtual Fl_X *makeWindow();
virtual void size_range() {
// currently nothing to do
}
virtual void flush_double();
virtual void flush_overlay();
virtual void draw_begin();

View File

@ -423,7 +423,6 @@ void Fl_X11_Window_Driver::use_border() {
}
void Fl_X11_Window_Driver::size_range() {
Fl_Window_Driver::size_range();
if (shown()) sendxjunk();
}