diff --git a/CHANGES b/CHANGES index 9f27401e4..83393f698 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,18 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2017 New Features and Extensions - (add new items here) + - MacOS platform: Added support for rescaling the GUI of any app at run-time + using the command/+/-/0/ keystrokes. + - MSWindows platform: Added optional support for rescaling the GUI of any app + at run-time using the ctrl/+/-/0/ keystrokes. This requires to build the + FLTK library with -DFLTK_HIDPI_SUPPORT (for now). This option also makes + app detect the desktop scaling factor and automatically scale their GUI + accordingly. This effectively renders WIN32 FLTK apps "per-monitor DPI-aware" + whereas they were "DPI-unaware" with FLTK 1.3.4. + - FLTK apps on the MacOS platform contain automatically a Window menu, which, + under MacOS ≥ 10.12, allows to group/ungroup windows in tabbed form. The new + Fl_Sys_Menu_Bar::window_menu_style() function allows to specify various + styles for the Window menu, even not to create it. - New function: int fl_open_ext(const char* fname, int binary, int oflags, ...) to control the opening of files in binary/text mode in a cross-platform way. - New Fl_SVG_Image class: gives support of scalable vector graphics images diff --git a/FL/Fl_Sys_Menu_Bar.H b/FL/Fl_Sys_Menu_Bar.H index eb2235e81..815d36e9e 100644 --- a/FL/Fl_Sys_Menu_Bar.H +++ b/FL/Fl_Sys_Menu_Bar.H @@ -24,33 +24,48 @@ class Fl_Sys_Menu_Bar_Driver; /** - A class to create, modify and delete menus that appear on Mac OS X in the menu bar at the top of the screen. + A class to create and modify menus that appear on Mac OS X in the menu bar at the top of the screen. + To use this class, just replace Fl_Menu_Bar by Fl_Sys_Menu_Bar, and, on the Mac platform, + a system menu at the top of the screen will be available. This menu will match an array + of Fl_Menu_Item's exactly as in all other FLTK menus. On other than Mac OS X platforms, Fl_Sys_Menu_Bar is a synonym of class Fl_Menu_Bar. - \n To use this class, just replace Fl_Menu_Bar by Fl_Sys_Menu_Bar, and, on the Mac platform, - a system menu at the top of the screen will be available. This menu will match an array - of Fl_Menu_Item's exactly as with standard FLTK menus. + + On the MacOS platform, the system menu bar of any FLTK app begins with the Application + menu which the FLTK library automatically constructs. Functions + Fl_Mac_App_Menu::custom_application_menu_items() and fl_mac_set_about() can be used to further customize + the Application menu. The FLTK library also automatically constructs and handles a Window menu which can be + further customized (or even removed) calling + Fl_Sys_Menu_Bar::window_menu_style(window_menu_style_enum style). + Other member functions of this class allow the app to generate the rest of the system menu bar. + It is recommended to localize the system menu bar using the standard Mac OS X localization procedure + (see \ref osissues_localize). Changes to the menu state are immediately visible in the menubar when they are made using member functions of the Fl_Sys_Menu_Bar class. Other changes (e.g., by a call to Fl_Menu_Item::set()) should be followed by a call to update() to be visible in the menubar across all platforms. - A few FLTK features are not supported by the Mac System menu: + A few FLTK menu features are not supported by the Mac System menu: \li no symbolic labels \li no embossed labels \li no font sizes - - You can configure a callback for the 'About' menu item to invoke your own code with Fl_Sys_Menu_Bar::about(). - */ + */ class FL_EXPORT Fl_Sys_Menu_Bar : public Fl_Menu_Bar { protected: virtual void draw(); public: + /** Possible styles of the Window menu in the system menu bar */ + typedef enum { + no_window_menu = 0, ///< No Window menu in the system menu bar + tabbing_mode_none, ///< No tabbed windows, but the system menu bar contains a Window menu + tabbing_mode_automatic, ///< Windows are created by themselves but can be tabbed later + tabbing_mode_preferred ///< Windows are tabbed when created + } window_menu_style_enum; Fl_Sys_Menu_Bar(int x,int y,int w,int h,const char *l=0); virtual ~Fl_Sys_Menu_Bar(); static Fl_Sys_Menu_Bar_Driver *driver(); - /** Return the system menu's array of Fl_Menu_Item's + /** Return the system menu's array of Fl_Menu_Item's */ const Fl_Menu_Item *menu() const {return Fl_Menu_::menu();} void menu(const Fl_Menu_Item *m); @@ -81,6 +96,10 @@ public: void shortcut (int i, int s); void setonly (Fl_Menu_Item *item); static void about(Fl_Callback *cb, void *data); + + static window_menu_style_enum window_menu_style(); + static void window_menu_style(window_menu_style_enum style); + static void create_window_menu(); }; extern Fl_Sys_Menu_Bar *fl_sys_menu_bar; diff --git a/FL/Fl_Sys_Menu_Bar_Driver.H b/FL/Fl_Sys_Menu_Bar_Driver.H index 1a2dfac25..7d869ae64 100644 --- a/FL/Fl_Sys_Menu_Bar_Driver.H +++ b/FL/Fl_Sys_Menu_Bar_Driver.H @@ -19,11 +19,15 @@ #ifndef Fl_Sys_Menu_Bar_Driver_H #define Fl_Sys_Menu_Bar_Driver_H +#if !defined(FL_DOXYGEN) + #include class Fl_Sys_Menu_Bar_Driver { friend class Fl_Sys_Menu_Bar; public: + static Fl_Sys_Menu_Bar::window_menu_style_enum window_menu_style_; + static Fl_Sys_Menu_Bar_Driver *driver_; // to be assigned with a unique object of this class or of a derived class Fl_Sys_Menu_Bar *bar; Fl_Sys_Menu_Bar_Driver(); virtual ~Fl_Sys_Menu_Bar_Driver(); @@ -45,10 +49,12 @@ public: virtual void remove(int index) { bar->Fl_Menu_Bar::remove(index); } virtual void replace(int index, const char *name) { bar->Fl_Menu_Bar::replace(index, name); } virtual void mode(int i, int fl) { bar->Fl_Menu_Bar::mode(i, fl); } - - static Fl_Sys_Menu_Bar_Driver *driver_; // to be assigned with a unique object of this class or of a derived class + virtual void create_window_menu() {} + static Fl_Sys_Menu_Bar::window_menu_style_enum window_menu_style() { return window_menu_style_; } + static void window_menu_style(Fl_Sys_Menu_Bar::window_menu_style_enum style) { window_menu_style_ = style; } }; +#endif // !defined(FL_DOXYGEN) #endif // Fl_Sys_Menu_Bar_Driver_H diff --git a/src/Fl_MacOS_Sys_Menu_Bar.mm b/src/Fl_MacOS_Sys_Menu_Bar.mm index 9dec537bb..dcedcd293 100644 --- a/src/Fl_MacOS_Sys_Menu_Bar.mm +++ b/src/Fl_MacOS_Sys_Menu_Bar.mm @@ -21,9 +21,30 @@ #include #include #include "drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H" +#include "flstring.h" +#include +#include +#include +#import // keep this after include of Fl_Sys_Menu_Bar_Driver.H because of check() conflict -Fl_Sys_Menu_Bar_Driver* Fl_MacOS_Sys_Menu_Bar_Driver::driver() { +typedef const Fl_Menu_Item *pFl_Menu_Item; + +static Fl_Menu_Bar *custom_menu; +static NSString *localized_Window = nil; + +static char *remove_ampersand(const char *s); +extern void (*fl_lock_function)(); +extern void (*fl_unlock_function)(); + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 +static void previous_tab_cb(Fl_Widget *, void *data); +static void next_tab_cb(Fl_Widget *, void *data); +static void move_tab_cb(Fl_Widget *, void *data); +static void merge_all_windows_cb(Fl_Widget *, void *data); +#endif + +Fl_MacOS_Sys_Menu_Bar_Driver* Fl_MacOS_Sys_Menu_Bar_Driver::driver() { static Fl_MacOS_Sys_Menu_Bar_Driver *once = new Fl_MacOS_Sys_Menu_Bar_Driver(); if (driver_ != once) { if (driver_) { @@ -36,22 +57,6 @@ Fl_Sys_Menu_Bar_Driver* Fl_MacOS_Sys_Menu_Bar_Driver::driver() { return once; } - -#import - -#include "flstring.h" -#include -#include -#include - -typedef const Fl_Menu_Item *pFl_Menu_Item; - -static Fl_Menu_Bar *custom_menu; - -static char *remove_ampersand(const char *s); -extern void (*fl_lock_function)(); -extern void (*fl_unlock_function)(); - /* Each MacOS system menu item contains a pointer to a record of type sys_menu_item defined below. The purpose of these records is to associate each MacOS system menu item with a relevant Fl_Menu_Item. @@ -92,6 +97,9 @@ const char *Fl_Mac_App_Menu::quit = "Quit %@"; - (void) setKeyEquivalentModifierMask:(int)value; - (void) setFltkShortcut:(int)key; + (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu action:(SEL)selector; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 +- (BOOL)validateMenuItem:(NSMenuItem *)item; +#endif @end @implementation FLMenuItem @@ -197,6 +205,37 @@ const char *Fl_Mac_App_Menu::quit = "Quit %@"; [item release]; return retval; } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 +- (BOOL)validateMenuItem:(NSMenuItem *)item { + // return YES for all but items of the Window menu + if (fl_mac_os_version < 101200 || + Fl_Sys_Menu_Bar::window_menu_style() <= Fl_Sys_Menu_Bar::tabbing_mode_none || + [item hasSubmenu]) return YES; + NSString *title = [[item parentItem] title]; // 10.6 + if (!title || !localized_Window || [title compare:localized_Window] != NSOrderedSame) return YES; + const Fl_Menu_Item *flitem = [(FLMenuItem*)item getFlItem]; + Fl_Callback *item_cb = flitem->callback(); + if (item_cb == previous_tab_cb || item_cb == next_tab_cb || item_cb == move_tab_cb) { + // is the current window tabbed? + Fl_Window *win = Fl::first_window(); + NSWindow *main = win ? (NSWindow*)fl_xid(win) : nil; + return (main && [main tabbedWindows] != nil); + } else if (item_cb == merge_all_windows_cb) { + // is there any untabbed, tabbable window? + int total = 0, untabbed = 0; + while ((++flitem)->label()) { + total++; + NSWindow *nsw = (NSWindow*)fl_xid( (Fl_Window*)flitem->user_data() ); + if (![nsw tabbedWindows] && [nsw tabbingMode] != NSWindowTabbingModeDisallowed) { + untabbed++; + } + } + return (untabbed > 0 && total >= 2); + } + return YES; +} +#endif @end @@ -341,6 +380,12 @@ static void convertToMenuBar(const Fl_Menu_Item *mm) [fl_system_menu removeItem:[fl_system_menu itemAtIndex:i]]; } if (mm) createSubMenu(fl_system_menu, mm, NULL, @selector(doCallback)); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + if (localized_Window) { + NSMenuItem *item = [fl_system_menu itemWithTitle:localized_Window]; + if (item) [[item submenu] setAutoenablesItems:YES]; + } +#endif } void Fl_MacOS_Sys_Menu_Bar_Driver::update() @@ -483,6 +528,145 @@ void Fl_Mac_App_Menu::custom_application_menu_items(const Fl_Menu_Item *m) [item release]; } } + + +static void minimize_win_cb(Fl_Widget *, void *data) +{ + [[NSApp mainWindow] miniaturize:nil]; +} + +static void window_menu_cb(Fl_Widget *, void *data) +{ + if (data) ((Fl_Window*)data)->show(); +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + +static void previous_tab_cb(Fl_Widget *, void *data) +{ + [[NSApp mainWindow] selectPreviousTab:nil]; +} + +static void next_tab_cb(Fl_Widget *, void *data) +{ + [[NSApp mainWindow] selectNextTab:nil]; +} + +static void move_tab_cb(Fl_Widget *, void *data) +{ + [[NSApp mainWindow] moveTabToNewWindow:nil]; +} + +static void merge_all_windows_cb(Fl_Widget *, void *) +{ + Fl_Window *first = Fl::first_window(); + if (first) { + [(NSWindow*)fl_xid(first) mergeAllWindows:nil]; + } +} + +#endif + + +static bool window_menu_installed = false; + +void Fl_MacOS_Sys_Menu_Bar_Driver::create_window_menu(void) +{ + if (window_menu_style() == Fl_Sys_Menu_Bar::no_window_menu) return; + if (window_menu_installed) return; + window_menu_installed = true; + int rank = 0; + if (fl_sys_menu_bar && fl_sys_menu_bar->menu()) { + if (fl_sys_menu_bar->find_index("Window") >= 0) { // there's already a "Window" menu -> don't create another + window_menu_style_ = Fl_Sys_Menu_Bar::no_window_menu; + return; + } + // put the Window menu last in menu bar or before Help if it's present + const Fl_Menu_Item *item = fl_sys_menu_bar->menu(); + while (item->label() && strcmp(item->label(), "Help") != 0) { + item = item->next(); + } + rank = fl_sys_menu_bar->find_index(item); + } else if (!fl_sys_menu_bar) { + fl_open_display(); + new Fl_Sys_Menu_Bar(0,0,0,0); + } + rank = fl_sys_menu_bar->Fl_Menu_::insert(rank, "Window", 0, NULL, 0, FL_SUBMENU); + localized_Window = NSLocalizedString(@"Window", nil); + + fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Minimize", FL_COMMAND+'m', minimize_win_cb, 0, FL_MENU_DIVIDER); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + if (fl_mac_os_version >= 101200 && window_menu_style() != Fl_Sys_Menu_Bar::tabbing_mode_none) { + fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Show Previous Tab", FL_SHIFT+FL_CTRL+0x9, previous_tab_cb, 0, 0); + fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Show Next Tab", FL_CTRL+0x9, next_tab_cb, 0, 0); + fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Move Tab To New Window", 0, move_tab_cb, 0, 0); + fl_sys_menu_bar->Fl_Menu_::insert(++rank, "Merge All Windows", 0, merge_all_windows_cb, 0, FL_MENU_DIVIDER); + } +#endif + ((Fl_Menu_Item*)fl_sys_menu_bar->menu()+rank)->user_data(&window_menu_style_); + fl_sys_menu_bar->update(); +} + +int Fl_MacOS_Sys_Menu_Bar_Driver::find_first_window() +{ + int count = bar->size(), i; + for (i = 0; i < count; i++) { + if (bar->menu()[i].user_data() == &window_menu_style_) break; + } + return i < count ? i : -1; +} + +void Fl_MacOS_Sys_Menu_Bar_Driver::new_window(Fl_Window *win) +{ + if (!window_menu_style() || !win->label()) return; + int index = find_first_window(); + if (index < 0) return; + while ((bar->menu()+index+1)->label()) index++; + const char *p = win->iconlabel() ? win->iconlabel() : win->label(); + int index2 = bar->Fl_Menu_::insert(index+1, p, 0, window_menu_cb, win, FL_MENU_RADIO); + setonly((Fl_Menu_Item*)bar->menu()+index2); +} + +void Fl_MacOS_Sys_Menu_Bar_Driver::remove_window(Fl_Window *win) +{ + if (!window_menu_style()) return; + int index = find_first_window() + 1; + if (index < 1) return; + while (true) { + Fl_Menu_Item *item = (Fl_Menu_Item*)bar->menu() + index; + if (!item->label()) return; + if (item->user_data() == win) { + bool doit = item->value(); + remove(index); + if (doit) { + item = (Fl_Menu_Item*)bar->menu() + find_first_window() + 1; + if (item->label()) { + ((Fl_Window*)item->user_data())->show(); + setonly(item); + } + } + break; + } + index++; + } +} + +void Fl_MacOS_Sys_Menu_Bar_Driver::rename_window(Fl_Window *win) +{ + if (!window_menu_style()) return; + int index = find_first_window() + 1; + if (index < 1) return; + while (true) { + Fl_Menu_Item *item = (Fl_Menu_Item*)bar->menu() + index; + if (!item->label()) return; + if (item->user_data() == win) { + replace(index, win->iconlabel() ? win->iconlabel() : win->label()); + return; + } + index++; + } +} + #endif /* __APPLE__ */ // diff --git a/src/Fl_Sys_Menu_Bar.cxx b/src/Fl_Sys_Menu_Bar.cxx index 6c4ee9a51..1e1dab81b 100644 --- a/src/Fl_Sys_Menu_Bar.cxx +++ b/src/Fl_Sys_Menu_Bar.cxx @@ -166,13 +166,58 @@ void Fl_Sys_Menu_Bar::replace(int index, const char *name) * \param data a pointer transmitted as 2nd argument to the callback. */ void Fl_Sys_Menu_Bar::about(Fl_Callback *cb, void *data) { - if (fl_sys_menu_bar) fl_sys_menu_bar->driver()->about(cb, data); + driver()->about(cb, data); } void Fl_Sys_Menu_Bar::draw() { driver()->draw(); } + + +/** Get the style of the Window menu in the system menu bar */ +Fl_Sys_Menu_Bar::window_menu_style_enum Fl_Sys_Menu_Bar::window_menu_style() { + return Fl_Sys_Menu_Bar_Driver::window_menu_style(); +} + +/** Set the desired style of the Window menu in the system menu bar. + This function, to be called before the first call to Fl_Window::show(), allows to + control whether the system menu bar should contain a Window menu, + and if yes, whether new windows should be displayed in tabbed form. These are + the effects of various values for \p style : + \li \c no_window_menu : don't add a Window menu to the system menu bar + \li \c tabbing_mode_none : add a simple Window menu to the system menu bar + \li \c tabbing_mode_automatic : the window menu also contains "Merge All Windows" to group + all windows in a single tabbed display mode. This is the \b default Window menu style + for FLTK apps. + \li \c tabbing_mode_preferred : new windows are displayed in tabbed mode when first created + + The Window menu, if present, is entirely created and controlled by the FLTK library. + Mac OS version 10.12 or later must be running for windows to be displayed in tabbed form. + Under non MacOS platforms, this function does nothing. + \version 1.4 + */ +void Fl_Sys_Menu_Bar::window_menu_style(Fl_Sys_Menu_Bar::window_menu_style_enum style) { + Fl_Sys_Menu_Bar_Driver::window_menu_style(style); +} + +/** Adds a Window menu, to the end of the system menu bar. + FLTK apps typically don't need to call this function which is automatically + called by the library the first time a window is shown. The default system menu bar + contains a Window menu with a "Merge All Windows" item. + Other Window menu styles can be obtained calling + Fl_Sys_Menu_Bar::window_menu_style(window_menu_style_enum) before the first Fl_Window::show(). + Alternatively, an app can call create_window_menu() after having populated the system menu bar, + for example with menu(const Fl_Menu_Item *), and before the first Fl_Window::show(). + + This function does nothing on non MacOS platforms. + \version 1.4 + */ +void Fl_Sys_Menu_Bar::create_window_menu() { + fl_open_display(); + driver()->create_window_menu(); +} + #if !defined(FL_DOXYGEN) Fl_Sys_Menu_Bar_Driver *Fl_Sys_Menu_Bar::driver() { if (!Fl_Sys_Menu_Bar_Driver::driver_) { // initialize this static variable if it was not initialized previously @@ -186,6 +231,8 @@ Fl_Sys_Menu_Bar_Driver *Fl_Sys_Menu_Bar_Driver::driver_ = 0; Fl_Sys_Menu_Bar_Driver::Fl_Sys_Menu_Bar_Driver() {bar = NULL;} Fl_Sys_Menu_Bar_Driver::~Fl_Sys_Menu_Bar_Driver() {} + +Fl_Sys_Menu_Bar::window_menu_style_enum Fl_Sys_Menu_Bar_Driver::window_menu_style_ = Fl_Sys_Menu_Bar::tabbing_mode_automatic; #endif // !defined(FL_DOXYGEN) // diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 88b1adc51..1649f0b53 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -1309,9 +1309,14 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; } - (void)windowDidMove:(NSNotification *)notif { - fl_lock_function(); FLWindow *nsw = (FLWindow*)[notif object]; Fl_Window *window = [nsw getFl_Window]; + if (abs([[nsw contentView] frame].size.height - window->h() * fl_graphics_driver->scale()) > 0.5) { + // the contentView, but not the window frame, is resized. This happens with tabbed windows. + [self windowDidResize:notif]; + return; + } + fl_lock_function(); resize_from_system = window; NSPoint pt2; pt2 = [nsw convertBaseToScreen:NSMakePoint(0, [[nsw contentView] frame].size.height)]; @@ -1402,6 +1407,19 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; Fl_Window *window = [nsw getFl_Window]; Fl::first_window(window); update_e_xy_and_e_xy_root(nsw); + if (fl_sys_menu_bar && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style()) { + // select the corresponding Window menu item + int index = Fl_MacOS_Sys_Menu_Bar_Driver::driver()->find_first_window() + 1; + while (index > 0) { + Fl_Menu_Item *item = (Fl_Menu_Item*)fl_sys_menu_bar->menu() + index; + if (!item->label()) break; + if (item->user_data() == window) { + fl_sys_menu_bar->setonly(item); + break; + } + index++; + } + } fl_unlock_function(); } - (void)windowDidDeminiaturize:(NSNotification *)notif @@ -1868,7 +1886,13 @@ void Fl_Cocoa_Screen_Driver::disable_im() { // Gets the border sizes and the titlebar size -static void get_window_frame_sizes(int &bx, int &by, int &bt) { +static void get_window_frame_sizes(int &bx, int &by, int &bt, Fl_Window *win) { + FLWindow *flw = fl_xid(win); + if (flw) { + bt = [flw frame].size.height - [[flw contentView] frame].size.height; + bx = by = 0; + return; + } static int top = 0, left, bottom; if (!top) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -1952,7 +1976,7 @@ static int fake_X_wm(Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) { } else { ret = 1; } - get_window_frame_sizes(bx, by, bt); + get_window_frame_sizes(bx, by, bt, w); } // The coordinates of the whole window, including non-client area xoff = bx; @@ -2278,6 +2302,11 @@ static FLTextInputContext* fltextinputcontext_instance = nil; fl_lock_function(); FLWindow *cw = (FLWindow*)[self window]; Fl_Window *window = [cw getFl_Window]; + if ( !window->parent() && window->border() && abs(rect.size.height - window->h() * fl_graphics_driver->scale()) > 0.5 ) { // this happens with tabbed window + window->resize([cw frame].origin.x/fl_graphics_driver->scale(), + (main_screen_height - ([cw frame].origin.y + rect.size.height))/fl_graphics_driver->scale(), + rect.size.width/fl_graphics_driver->scale(), rect.size.height/fl_graphics_driver->scale()); + } through_drawRect = YES; Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); if (fl_mac_os_version >= 100700) { // determine whether window is mapped to a retina display @@ -2919,6 +2948,7 @@ Fl_X* Fl_Cocoa_Window_Driver::makeWindow() fl_open_display(); NSInteger winlevel = NSNormalWindowLevel; NSUInteger winstyle; + Fl_Sys_Menu_Bar::driver()->create_window_menu(); // effective once at most Fl_Window* w = pWindow; if (w->parent()) { w->border(0); @@ -3029,6 +3059,18 @@ Fl_X* Fl_Cocoa_Window_Driver::makeWindow() contentRect:crect styleMask:winstyle]; [cw setFrameOrigin:crect.origin]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + if (fl_mac_os_version >= 101200) { + if (!w->parent() && (winstyle & NSTitledWindowMask) && (winstyle & NSResizableWindowMask) + && !w->modal() && !w->non_modal() && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() > Fl_Sys_Menu_Bar::tabbing_mode_none) { + if (Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() == Fl_Sys_Menu_Bar::tabbing_mode_preferred) + [cw setTabbingMode:NSWindowTabbingModePreferred]; + else [cw setTabbingMode:NSWindowTabbingModeAutomatic]; + } else { + [cw setTabbingMode:NSWindowTabbingModeDisallowed]; + } + } +#endif if (!w->parent()) { [cw setHasShadow:YES]; [cw setAcceptsMouseMovedEvents:YES]; @@ -3105,7 +3147,10 @@ Fl_X* Fl_Cocoa_Window_Driver::makeWindow() } else { // a top-level window [cw makeKeyAndOrderFront:nil]; } - + if (fl_sys_menu_bar && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() && !w->parent() && w->border() && + !w->modal() && !w->non_modal()) { + Fl_MacOS_Sys_Menu_Bar_Driver::driver()->new_window(w); + } int old_event = Fl::e_number; w->handle(Fl::e_number = FL_SHOW); Fl::e_number = old_event; @@ -3121,7 +3166,7 @@ Fl_X* Fl_Cocoa_Window_Driver::makeWindow() */ void Fl_Cocoa_Window_Driver::size_range() { int bx, by, bt; - get_window_frame_sizes(bx, by, bt); + get_window_frame_sizes(bx, by, bt, pWindow); Fl_Window_Driver::size_range(); NSSize minSize = NSMakeSize(minw(), minh() + bt); NSSize maxSize = NSMakeSize(maxw() ? maxw():32000, maxh() ? maxh() + bt:32000); @@ -3172,6 +3217,8 @@ const char *Fl_Darwin_System_Driver::filename_name( const char *name ) void Fl_Cocoa_Window_Driver::label(const char *name, const char *mininame) { if (shown() || Fl_X::i(pWindow)) { q_set_window_title(fl_xid(pWindow), name, mininame); + if (fl_sys_menu_bar && Fl_Sys_Menu_Bar_Driver::window_menu_style()) + Fl_MacOS_Sys_Menu_Bar_Driver::driver()->rename_window(pWindow); } } @@ -3230,7 +3277,7 @@ void Fl_Cocoa_Window_Driver::resize(int X,int Y,int W,int H) { } pWindow->Fl_Group::resize(X,Y,W,H); // transmit changes in FLTK coords to cocoa - get_window_frame_sizes(bx, by, bt); + get_window_frame_sizes(bx, by, bt, pWindow); bx = X; by = Y; parent = pWindow->window(); while (parent) { @@ -3552,6 +3599,8 @@ int Fl_Darwin_System_Driver::clipboard_contains(const char *type) { } void Fl_Cocoa_Window_Driver::destroy(FLWindow *xid) { + if (fl_sys_menu_bar && Fl_Sys_Menu_Bar_Driver::window_menu_style()) + Fl_MacOS_Sys_Menu_Bar_Driver::driver()->remove_window([xid getFl_Window]); [xid close]; } @@ -4291,7 +4340,7 @@ int Fl_Cocoa_Window_Driver::decorated_w() if (!shown() || parent() || !border() || !visible()) return w(); int bx, by, bt; - get_window_frame_sizes(bx, by, bt); + get_window_frame_sizes(bx, by, bt, pWindow); return w() + 2 * bx; } @@ -4300,7 +4349,7 @@ int Fl_Cocoa_Window_Driver::decorated_h() if (!shown() || parent() || !border() || !visible()) return h(); int bx, by, bt; - get_window_frame_sizes(bx, by, bt); + get_window_frame_sizes(bx, by, bt, pWindow); return h() + bt + by; } diff --git a/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H b/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H index b2a6f125b..8c332c6cb 100644 --- a/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H +++ b/src/drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H @@ -1,7 +1,7 @@ // // "$Id$" // -// system menu bar widget for the Fast Light Tool Kit (FLTK). +// Definition of class Fl_MacOS_Sys_Menu_Bar_Driver for the Fast Light Tool Kit (FLTK). // // Copyright 2017 by Bill Spitzak and others. // @@ -39,7 +39,12 @@ public: virtual void remove(int index); virtual void replace(int index, const char *name); virtual void mode(int i, int fl); - static Fl_Sys_Menu_Bar_Driver* driver(); + virtual void create_window_menu(); + int find_first_window(); + void new_window(Fl_Window *win); + void remove_window(Fl_Window *win); + void rename_window(Fl_Window *win); + static Fl_MacOS_Sys_Menu_Bar_Driver* driver(); }; diff --git a/test/menubar.cxx b/test/menubar.cxx index 6062db889..40bc9ffbe 100644 --- a/test/menubar.cxx +++ b/test/menubar.cxx @@ -3,7 +3,7 @@ // // Menubar test program for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2010 by Bill Spitzak and others. +// Copyright 1998-2017 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 @@ -199,6 +199,7 @@ void menu_location_cb(Fl_Widget* w, void* data) smenubar->callback(test_cb); } else { // switch to window menu bar + menubar->menu(smenubar->menu()); smenubar->clear(); delete smenubar; menubar->show(); @@ -246,6 +247,8 @@ int main(int argc, char **argv) { Fl_Choice ch2(500,100,150,25,"Use:"); ch2.menu(menu_location); ch2.callback(menu_location_cb, &menubar); + ch2.value(1); + menu_location_cb(&ch2, &menubar); #endif window.end(); @@ -257,6 +260,7 @@ int main(int argc, char **argv) { {0} }; Fl_Mac_App_Menu::custom_application_menu_items(custom); + //Fl_Sys_Menu_Bar::window_menu_style(Fl_Sys_Menu_Bar::no_window_menu); #endif window.show(argc, argv); return Fl::run();