diff options
| author | Albrecht Schlosser <albrechts.fltk@online.de> | 2022-02-22 23:28:04 +0100 |
|---|---|---|
| committer | Albrecht Schlosser <albrechts.fltk@online.de> | 2022-03-01 13:45:51 +0100 |
| commit | 091712bea8ff5aab89f0f8483ca572c118ca5715 (patch) | |
| tree | a6afc62f1ebd7bb33e9b1036124d0037d09f0e09 /src | |
| parent | d4ceb20ad30662861fe5a1471de2b5a202772937 (diff) | |
Fix default size_range() calculation (issue #392, STR 3352)
Diffstat (limited to 'src')
| -rw-r--r-- | src/Fl_Window.cxx | 215 | ||||
| -rw-r--r-- | src/Fl_Window_Driver.H | 3 | ||||
| -rw-r--r-- | src/Fl_Window_Driver.cxx | 68 | ||||
| -rw-r--r-- | src/Fl_cocoa.mm | 28 | ||||
| -rw-r--r-- | src/Fl_win32.cxx | 20 | ||||
| -rw-r--r-- | src/Fl_x.cxx | 35 | ||||
| -rw-r--r-- | src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H | 5 | ||||
| -rw-r--r-- | src/drivers/X11/Fl_X11_Window_Driver.cxx | 1 |
8 files changed, 242 insertions, 133 deletions
diff --git a/src/Fl_Window.cxx b/src/Fl_Window.cxx index 77090dc0b..50f39f04e 100644 --- a/src/Fl_Window.cxx +++ b/src/Fl_Window.cxx @@ -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 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. + If this function is \b not called, FLTK tries to figure out the range. + Please see the protected method default_size_range() for details. - \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 */ diff --git a/src/Fl_Window_Driver.H b/src/Fl_Window_Driver.H index 524c8be20..85f758bfb 100644 --- a/src/Fl_Window_Driver.H +++ b/src/Fl_Window_Driver.H @@ -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); diff --git a/src/Fl_Window_Driver.cxx b/src/Fl_Window_Driver.cxx index 65ccae8b8..a21776381 100644 --- a/src/Fl_Window_Driver.cxx +++ b/src/Fl_Window_Driver.cxx @@ -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; } diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 01bfc52a0..3ec130a21 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -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); diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx index 0399a2278..91a54215a 100644 --- a/src/Fl_win32.cxx +++ b/src/Fl_win32.cxx @@ -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; diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx index a993d1e92..34528a0b2 100644 --- a/src/Fl_x.cxx +++ b/src/Fl_x.cxx @@ -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; } diff --git a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H index 0cb61c25e..23a05be57 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H +++ b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H @@ -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(); diff --git a/src/drivers/X11/Fl_X11_Window_Driver.cxx b/src/drivers/X11/Fl_X11_Window_Driver.cxx index 1a98f78e3..9b5196358 100644 --- a/src/drivers/X11/Fl_X11_Window_Driver.cxx +++ b/src/drivers/X11/Fl_X11_Window_Driver.cxx @@ -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(); } |
