diff --git a/include/nana/detail/linux_X11/platform_spec.hpp b/include/nana/detail/linux_X11/platform_spec.hpp index e701c203..d6d1a26f 100644 --- a/include/nana/detail/linux_X11/platform_spec.hpp +++ b/include/nana/detail/linux_X11/platform_spec.hpp @@ -175,7 +175,8 @@ namespace detail Atom xdnd_finished; }; - struct caret_tag; + //A forward declaration of caret data + struct caret_rep; class timer_runner; @@ -243,9 +244,7 @@ namespace detail void caret_close(native_window_type); void caret_pos(native_window_type, const ::nana::point&); void caret_visible(native_window_type, bool); - void caret_flash(caret_tag&); bool caret_update(native_window_type, nana::paint::graphics& root_graph, bool is_erase_caret_from_root_graph); - static bool caret_reinstate(caret_tag&); void set_error_handler(); int rev_error_handler(); @@ -287,7 +286,7 @@ namespace detail { volatile bool exit_thread; std::unique_ptr thr; - std::map carets; + std::map carets; }caret_holder_; std::map wincontext_; diff --git a/include/nana/gui/msgbox.hpp b/include/nana/gui/msgbox.hpp index e21392e0..bb17c448 100644 --- a/include/nana/gui/msgbox.hpp +++ b/include/nana/gui/msgbox.hpp @@ -61,6 +61,12 @@ namespace nana /// Writes a string to the buffer. msgbox & operator<<(const wchar_t*); + /// Writes a UTF-8 string to the buffer. + msgbox & operator<<(const std::string&); + + /// Writes a UTF-8 string to the buffer. + msgbox & operator<<(const char*); + /// Writes a string to the buffer. msgbox & operator<<(const nana::charset&); diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index d8979911..1ff4fccc 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -168,7 +168,7 @@ namespace nana void insert(std::size_t, native_string_type&&, nana::any&&); std::size_t length() const; bool close_fly(bool); - void attach(std::size_t, window); + window attach(std::size_t, window, bool drop_other); void erase(std::size_t); void tab_color(std::size_t, bool is_bgcolor, const ::nana::color&); void tab_image(size_t, const nana::paint::image&); @@ -306,12 +306,19 @@ namespace nana API::update_window(*this); } - void attach(std::size_t pos, window attach_wd) + /// Attach a window to a specified tab. When the tab is activated, tabbar shows the attached window. + /** + * @param pos The position of tab to set the attached window. + * @param attach_wd A handle to the window to be set. + * @param drop_other Drop the attached windows of other tabs whose attach windows are equal to the parameter attach_wd. If drop_other is true, the other tabs attached windows equal to attach_wd will be dropped. + * @return A handle to the last attached window of specified tab. + */ + window attach(std::size_t pos, window attach_wd, bool drop_other = true) { if (attach_wd && API::empty_window(attach_wd)) throw std::invalid_argument("tabbar.attach: invalid window handle"); - this->get_drawer_trigger().attach(pos, attach_wd); + return this->get_drawer_trigger().attach(pos, attach_wd, drop_other); } void erase(std::size_t pos) diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index a2121991..97ce8d19 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -140,26 +140,69 @@ namespace detail //end class charset_conv #endif - struct caret_tag + //Caret implementation + struct caret_rep { native_window_type window; - bool has_input_method_focus; - bool visible; + bool has_input_method_focus{ false }; + bool visible{ false }; nana::point pos; nana::size size; nana::rectangle rev; - nana::paint::graphics graph; nana::paint::graphics rev_graph; - XIM input_method; - XIC input_context; - XFontSet input_font; + XIM input_method{ 0 }; + XIC input_context{ 0 }; + XFontSet input_font{ 0 }; XRectangle input_spot; XRectangle input_status_area; - long input_context_event_mask; + long input_context_event_mask{ 0 }; - caret_tag(native_window_type wd) - : window(wd), has_input_method_focus(false), visible(false), input_method(0), input_context(0), input_font(0), input_context_event_mask(0) + caret_rep(native_window_type wd) + : window{ wd } {} + + //Copy the reversed graphics to the window + bool reinstate() + { + if(rev.width && rev.height) + { + rev_graph.paste(window, rev, 0, 0); + //Drop the reversed graphics in order to draw the + //caret in the next flash. + rev.width = rev.height = 0; + return true; + } + return false; + } + + void twinkle() + { + if(!visible) + return; + + if(!reinstate()) + { + rev_graph.bitblt(rectangle{size}, window, pos); + rev.width = size.width; + rev.height = size.height; + rev.x = pos.x; + rev.y = pos.y; + + paint::pixel_buffer pxbuf; + pxbuf.open(rev_graph.handle()); + + auto pxsize = pxbuf.size(); + for(int y = 0; y < static_cast(pxsize.height); ++y) + for(int x = 0; x < static_cast(pxsize.width); ++x) + { + auto px = pxbuf.at({x, y}); + px->element.red = ~px->element.red; + px->element.green = ~px->element.green; + px->element.blue = ~px->element.blue; + } + pxbuf.paste(window, {rev.x, rev.y}); + } + } }; class timer_runner @@ -734,12 +777,12 @@ namespace detail { bool is_start_routine = false; platform_scope_guard psg; - caret_tag * & addr = caret_holder_.carets[wd]; - if(0 == addr) + auto & addr = caret_holder_.carets[wd]; + if(nullptr == addr) { ::XSetLocaleModifiers(""); - addr = new caret_tag(wd); + addr = new caret_rep(wd); is_start_routine = (caret_holder_.carets.size() == 1); addr->input_method = ::XOpenIM(display_, 0, 0, 0); if(addr->input_method) @@ -815,10 +858,7 @@ namespace detail } addr->visible = false; - addr->graph.make(caret_sz); - addr->graph.rectangle(true, colors::black); addr->rev_graph.make(caret_sz); - addr->size = caret_sz; if(addr->input_context && (false == addr->has_input_method_focus)) @@ -844,7 +884,7 @@ namespace detail auto i = caret_holder_.carets.find(wd); if(i != caret_holder_.carets.end()) { - caret_tag * addr = i->second; + auto addr = i->second; if(addr->input_context) { if(addr->has_input_method_focus) @@ -901,9 +941,8 @@ namespace detail auto i = caret_holder_.carets.find(wd); if(i != caret_holder_.carets.end()) { - caret_tag & crt = *i->second; - caret_reinstate(crt); - crt.pos = pos; + i->second->reinstate(); + i->second->pos = pos; } } @@ -913,12 +952,12 @@ namespace detail auto i = caret_holder_.carets.find(wd); if(i != caret_holder_.carets.end()) { - caret_tag& crt = *i->second; + auto & crt = *i->second; if(crt.visible != vis) { if(vis == false) { - caret_reinstate(crt); + crt.reinstate(); if(crt.input_context && crt.has_input_method_focus) { ::XUnsetICFocus(crt.input_context); @@ -938,60 +977,19 @@ namespace detail } } - void platform_spec::caret_flash(caret_tag & crt) - { - if(crt.visible && (false == caret_reinstate(crt))) - { - crt.rev_graph.bitblt(rectangle{crt.size}, crt.window, crt.pos); - crt.rev.width = crt.size.width; - crt.rev.height = crt.size.height; - crt.rev.x = crt.pos.x; - crt.rev.y = crt.pos.y; - crt.graph.paste(crt.window, crt.rev, 0, 0); - } - } - - bool platform_spec::caret_update(native_window_type wd, nana::paint::graphics& root_graph, bool is_erase_caret_from_root_graph) + bool platform_spec::caret_update(native_window_type wd, nana::paint::graphics& /*root_graph*/, bool after_mapping) { platform_scope_guard psg; auto i = caret_holder_.carets.find(wd); if(i != caret_holder_.carets.end()) { - caret_tag & crt = *i->second; - if(is_erase_caret_from_root_graph) + auto & crt = *i->second; + if(!after_mapping) { - root_graph.bitblt(crt.rev, crt.rev_graph); + return crt.reinstate(); } else - { - bool owns_caret = false; - nana::paint::graphics * crt_graph; - if(crt.rev.width && crt.rev.height) - { - crt.rev_graph.bitblt(rectangle{crt.size}, root_graph, crt.pos); - crt_graph = &crt.graph; - owns_caret = true; - } - else - crt_graph = &crt.rev_graph; - - root_graph.bitblt(crt.rev, *crt_graph); - return owns_caret; - } - } - return false; - } - - //Copy the reversed graphics to the window - bool platform_spec::caret_reinstate(caret_tag& crt) - { - if(crt.rev.width && crt.rev.height) - { - crt.rev_graph.paste(crt.window, crt.rev, 0, 0); - //Drop the reversed graphics in order to draw the - //caret in the next flash. - crt.rev.width = crt.rev.height = 0; - return true; + crt.twinkle(); } return false; } @@ -1021,7 +1019,7 @@ namespace detail if(xlib_locker_.try_lock()) { for(auto i : caret_holder_.carets) - caret_flash(*(i.second)); + i.second->twinkle(); xlib_locker_.unlock(); } diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index 6faefc4c..d53c9ff5 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -355,14 +355,12 @@ namespace nana msgbox::msgbox(const std::string& title) : wd_(nullptr), title_(title), button_(ok), icon_(icon_none) { - // throw_not_utf8(title_); review_utf8(title_); } msgbox::msgbox(window wd, const std::string& title, button_t b) : wd_(wd), title_(title), button_(b), icon_(icon_none) { - // throw_not_utf8(title_); review_utf8(title_); } @@ -380,19 +378,35 @@ namespace nana msgbox & msgbox::operator<<(const std::wstring& str) { - sstream_ << to_osmbstr(to_utf8(str)); + sstream_ << to_utf8(str); return *this; } msgbox & msgbox::operator<<(const wchar_t* str) { - sstream_ << to_osmbstr(to_utf8(str)); + sstream_ << to_utf8(str); return *this; } + + /// Writes a UTF-8 string to the buffer. + msgbox & msgbox::operator<<(const std::string& u8str) + { + review_utf8(u8str); + sstream_ << u8str; + + return *this; + } + + /// Writes a UTF-8 string to the buffer. + msgbox & msgbox::operator<<(const char* u8str) + { + return operator<<(std::string{ u8str }); + } + msgbox & msgbox::operator<<(const nana::charset& cs) { - std::string str = cs; + std::string str = cs.to_bytes(nana::unicode::utf8); sstream_ << str; return *this; } @@ -454,7 +468,7 @@ namespace nana return pick_yes; #elif defined(NANA_X11) msgbox_window box(wd_, title_, button_, icon_); - box.prompt(nana::charset(sstream_.str())); + box.prompt(sstream_.str()); return box.pick(); #endif return pick_yes; diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 04426064..e731b3ad 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -585,9 +585,17 @@ namespace nana { if(pos < list_.size() && (pos != basis_.active)) { - API::show_window(iterator_at(pos)->relative, true); - if(basis_.active < list_.size()) - API::show_window(iterator_at(basis_.active)->relative, false); + auto & tab_act = *iterator_at(pos); + API::show_window(tab_act.relative, true); + if (basis_.active < list_.size()) + { + auto & tab_deact = *iterator_at(basis_.active); + + //Don't hide the relative window if it is equal to active relative window. + //The tabbar allows a window to be attached to multiple tabs(pass false to DropOther of attach method) + if (tab_deact.relative != tab_act.relative) + API::show_window(tab_deact.relative, false); + } basis_.active = pos; track(); @@ -603,13 +611,27 @@ namespace nana return basis_.active; } - void attach(std::size_t pos, window wd) + window attach(std::size_t pos, window wd, bool drop_other) { if (pos >= list_.size()) throw std::out_of_range("tabbar: invalid position"); - iterator_at(pos)->relative = wd; + auto & tab = *iterator_at(pos); + auto old = tab.relative; + + if (drop_other) + { + //Drop the relative windows which are equal to wd. + for (auto & t : list_) + { + if (wd == t.relative) + t.relative = nullptr; + } + } + + tab.relative = wd; API::show_window(wd, basis_.active == pos); + return old; } bool tab_color(std::size_t pos, bool is_bgcolor, const ::nana::color& clr) @@ -1181,9 +1203,9 @@ namespace nana return layouter_->toolbox_object().close_fly(fly); } - void trigger::attach(std::size_t pos, window wd) + window trigger::attach(std::size_t pos, window wd, bool drop_other) { - layouter_->attach(pos, wd); + return layouter_->attach(pos, wd, drop_other); } void trigger::erase(std::size_t pos) diff --git a/source/paint/image.cpp b/source/paint/image.cpp index b654ca6a..498dd944 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -102,7 +102,8 @@ namespace paint { close(); #if defined(NANA_WINDOWS) - HICON handle = ::CreateIconFromResource((PBYTE)data, static_cast(bytes), TRUE, 0x00030000); + // use actual resource size, stopped using CreateIconFromResource since it loads blurry image + HICON handle = ::CreateIconFromResourceEx((PBYTE)data, static_cast(bytes), TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR); if(handle) { ICONINFO info; @@ -359,6 +360,13 @@ namespace paint else if (bytes > 9 && (0x66697845 == *reinterpret_cast(reinterpret_cast(data)+5))) //Exif ptr = std::make_shared(); #endif + +#if defined(NANA_WINDOWS) + // suppose icon data is bitmap data + if (!ptr && bytes > 40 /* sizeof(BITMAPINFOHEADER) */ && (40 == *reinterpret_cast(data))) { + ptr = std::make_shared(true); + } +#endif } }