diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 8c0d85fe..02ce947f 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -434,6 +434,8 @@ namespace API * widget by the content extent. */ optional> content_extent(window wd, unsigned limited_px, bool limit_width); + + unsigned screen_dpi(bool x_requested); }//end namespace API }//end namespace nana diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 71280b7e..be3d571f 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -94,6 +94,12 @@ namespace nana */ virtual void fit_content(unsigned maximum = 0) noexcept = 0; + /// Sets an exclusive font for the column + virtual void typeface(const paint::font& column_font) = 0; + + /// Returns a font + virtual paint::font typeface() const noexcept = 0; + /// Determines the visibility state of the column /** * @return true if the column is visible, false otherwise @@ -1194,17 +1200,25 @@ namespace nana color_proxy selection_box{ static_cast(0x3399FF) }; ///< Color of selection box border. + + std::shared_ptr column_font; ///< Renderer draws column texts with the font if it is not a nullptr. + /// The max column width which is generated by fit_content is allowed. It is ignored when it is 0, or a max value is passed to fit_content. unsigned max_fit_content{ 0 }; - unsigned min_column_width{ 20 }; ///< def=20 . non counting suspension_width + unsigned min_column_width{ 20 }; ///< def=20 . non counting suspension_width - unsigned suspension_width { 8 }; ///< def= . the trigger will set this to the width if ("...") - unsigned text_margin { 5 }; ///< def= 5. Additional or extended with added (before) to the text width to determine the cell width. cell_w = text_w + ext_w +1 - unsigned header_height { 25 }; ///< def=25 . header height header_size - unsigned item_height_ex { 6 }; ///< Set !=0 !!!! def=6. item_height = text_height + item_height_ex + unsigned suspension_width{ 8 }; ///< def= . the trigger will set this to the width if ("...") + unsigned text_margin{ 5 }; ///< def= 5. Additional or extended with added (before) to the text width to determine the cell width. cell_w = text_w + ext_w +1 + + //deprecated + //unsigned header_height { 25 }; ///< def=25 . header height header_size + + unsigned item_height_ex{ 6 }; ///< Set !=0 !!!! def=6. item_height = text_height + item_height_ex unsigned header_splitter_area_before{ 2 }; ///< def=2. But 4 is better... IMO - unsigned header_splitter_area_after { 3 }; ///< def=3. But 4 is better... + unsigned header_splitter_area_after{ 3 }; ///< def=3. But 4 is better... + unsigned header_padding_top{ 3 }; + unsigned header_padding_bottom{ 3 }; ::nana::parameters::mouse_wheel mouse_wheel{}; ///< The number of lines/characters to scroll when vertical/horizontal mouse wheel is moved. }; diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index 8843a03f..4d3e36ec 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -38,14 +38,21 @@ namespace nana font(const font&); font(const ::std::string& name, double size_pt, const font_style& fs = {}); - font(double size_pt, const path_type& truetype, const font_style& ft = {}); + font(double size_pt, const path_type& truetype, const font_style& fs = {}); ~font(); bool empty() const; void set_default() const; ::std::string name() const; - double size() const; + + /// Returns font size, in point. + /** + * @param fixed Indicates whether to return a fixed font size. If this parameter is false, the method may return zero for default system font size. If the parameter is true, the method returns a fixed size of default font size if the font size that assigned by constructor is zero. + * @return The font size, in point. + */ + double size(bool fixed = false) const; + bool bold() const; unsigned weight() const; bool italic() const; diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index 6490045c..e3953504 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -255,6 +255,35 @@ namespace nana data::storage = nullptr; } + double platform_abstraction::font_default_pt() + { +#ifdef NANA_WINDOWS + //Create default font object. + NONCLIENTMETRICS metrics = {}; + metrics.cbSize = sizeof metrics; +#if(WINVER >= 0x0600) +#if defined(NANA_MINGW) + OSVERSIONINFO osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + ::GetVersionEx(&osvi); + if (osvi.dwMajorVersion < 6) + metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth); +#else + if (!IsWindowsVistaOrGreater()) + metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth); +#endif +#endif + ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof metrics, &metrics, 0); + + auto desktop = ::GetDC(nullptr); + auto pt = std::abs(metrics.lfMessageFont.lfHeight) * 72.0 / ::GetDeviceCaps(desktop, LOGPIXELSY); + ::ReleaseDC(nullptr, desktop); + return pt; +#else + return 10; +#endif + } + ::std::shared_ptr platform_abstraction::default_font(const ::std::shared_ptr& new_font) { auto & r = platform_storage(); @@ -337,7 +366,7 @@ namespace nana if(font_family.empty()) font_family = '*'; - std::string pat_str = font_family + '-' + std::to_string(size_pt ? size_pt : 10); + std::string pat_str = font_family + '-' + std::to_string(size_pt ? size_pt : font_default_pt()); auto pat = ::XftNameParse(pat_str.c_str()); XftResult res; auto match_pat = ::XftFontMatch(disp, ::XDefaultScreen(disp), pat, &res); @@ -407,6 +436,31 @@ namespace nana ::FcConfigAppFontClear(nullptr); } } +#endif + } + + unsigned platform_abstraction::screen_dpi(bool x_requested) + { +#ifdef NANA_WINDOWS + auto hdc = ::GetDC(nullptr); + auto dots = static_cast(::GetDeviceCaps(hdc, (x_requested ? LOGPIXELSX : LOGPIXELSY))); + ::ReleaseDC(nullptr, hdc); + return dots; +#else + auto & spec = ::nana::detail::platform_spec::instance(); + auto disp = spec.open_display(); + auto screen = ::XDefaultScreen(disp); + + double dots; + + if (x_requested) + dots += ((((double)DisplayWidth(disp, screen)) * 25.4) / + ((double)DisplayWidthMM(disp, screen))); + else + dots += ((((double)DisplayHeight(disp, screen)) * 25.4) / + ((double)DisplayHeightMM(disp, screen))); + + return static_cast(dots + 0.5); #endif } } diff --git a/source/detail/platform_abstraction.hpp b/source/detail/platform_abstraction.hpp index d22190c6..290e0a5c 100644 --- a/source/detail/platform_abstraction.hpp +++ b/source/detail/platform_abstraction.hpp @@ -33,10 +33,13 @@ namespace nana static void initialize(); /// Shutdown before destruction of platform_spec static void shutdown(); + static double font_default_pt(); static ::std::shared_ptr default_font(const ::std::shared_ptr&); static ::std::shared_ptr make_font(const ::std::string& font_family, double size_pt, const font::font_style& fs); static ::std::shared_ptr make_font_from_ttf(const path_type& ttf, double size_pt, const font::font_style& fs); static void font_resource(bool try_add, const path_type& ttf); + + static unsigned screen_dpi(bool x_requested); }; } diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 1fd7d158..237a5490 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -20,6 +20,8 @@ #include #include +#include "../../source/detail/platform_abstraction.hpp" + namespace nana { //restrict @@ -1466,5 +1468,10 @@ namespace API return{}; } + + unsigned screen_dpi(bool x_requested) + { + return ::nana::platform_abstraction::screen_dpi(x_requested); + } }//end namespace API }//end namespace nana diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index c49841c5..d8080e37 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -134,6 +134,7 @@ namespace nana std::function weak_ordering; + std::shared_ptr font; ///< The exclusive column font column() = default; @@ -150,11 +151,12 @@ namespace nana index = other.index; alignment = other.alignment; weak_ordering = other.weak_ordering; + font = other.font; } return *this; } - + column(column&& other): caption(std::move(other.caption)), width_px(other.width_px), @@ -163,6 +165,7 @@ namespace nana index(other.index), alignment(other.alignment), weak_ordering(std::move(other.weak_ordering)), + font(std::move(other.font)), ess_(other.ess_) { } @@ -237,6 +240,12 @@ namespace nana //Definition is provided after essence void fit_content(unsigned maximize = 100000) noexcept override; + /// Sets an exclusive font for the column + void typeface(const paint::font& column_font) override; + + /// Returns a font + paint::font typeface() const noexcept override; + bool visible() const noexcept override { return visible_state; @@ -2254,7 +2263,7 @@ namespace nana { /// we are inside auto const origin = content_view->origin(); - if(header.visible() && (pos.y < static_cast(scheme_ptr->header_height) + area.y)) + if (header.visible() && (pos.y < static_cast(header_visible_px()) + area.y)) { /// we are in the header new_where.first = parts::header; new_where.second = this->column_from_pos(pos.x); @@ -2339,9 +2348,32 @@ namespace nana return r; } + double header_font_px() const + { + unsigned max_font_px = 0; + + paint::graphics graph{ size{ 1, 1 } }; + for (auto & col : this->header.cont()) + { + if (!col.visible()) + continue; + + graph.typeface(col.typeface()); + + unsigned as, ds, ileading; + graph.text_metrics(as, ds, ileading); + + max_font_px = (std::max)(as + ds, max_font_px); + } + return max_font_px; + } + unsigned header_visible_px() const { - return (header.visible() ? scheme_ptr->header_height : 0); + if (!header.visible()) + return 0; + + return scheme_ptr->header_padding_top + scheme_ptr->header_padding_bottom + static_cast(header_font_px()); } bool rect_header(nana::rectangle& r) const @@ -2350,7 +2382,7 @@ namespace nana { r = this->content_area(); - r.height = scheme_ptr->header_height; + r.height = header_visible_px(); if (lister.wd_ptr()->borderless()) return !r.empty(); @@ -2760,6 +2792,29 @@ namespace nana _m_refresh(); } + + /// Sets an exclusive font for the column + void es_header::column::typeface(const paint::font& column_font) + { + this->font.reset(new paint::font{ column_font }); + + API::refresh_window(*ess_->listbox_ptr); + } + + /// Returns a font + paint::font es_header::column::typeface() const noexcept + { + //Returns the exclusive font if it is not empty + if (this->font && !this->font->empty()) + return *this->font; + + //Returns the column font if it is not empty + if (ess_->scheme_ptr->column_font && !ess_->scheme_ptr->column_font) + return *(ess_->scheme_ptr->column_font); + + //If all above fonts are invalid, returns the widget font. + return ess_->listbox_ptr->typeface(); + } //end es_header::column functions class inline_indicator @@ -3064,6 +3119,14 @@ namespace nana using item_state = essence::item_state; using parts = essence::parts; + struct column_rendering_parameter + { + unsigned margin; + unsigned height; + double max_font_px; + paint::font wdg_font; + }; + drawer_header_impl(essence* es) noexcept: essence_(es){} size_type splitter() const noexcept @@ -3175,25 +3238,31 @@ namespace nana 0, r.height - 1 }; + column_rendering_parameter crp; + //The first item includes the margin - unsigned margin = essence_->header.margin(); + crp.margin = essence_->header.margin(); + + crp.height = essence_->header_visible_px(); + crp.max_font_px = essence_->header_font_px(); + crp.wdg_font = graph.typeface(); for (auto & col : essence_->header.cont()) { if (col.visible_state) { - column_r.width = col.width_px + margin; + column_r.width = col.width_px + crp.margin; const auto right_pos = column_r.right(); //Make sure the column is in the display area. if (right_pos > r.x) { - _m_draw_header_item(graph, margin, column_r, text_color, col, (col.index == essence_->pointer_where.second ? state : item_state::normal)); + _m_draw_header_item(graph, crp, column_r, text_color, col, (col.index == essence_->pointer_where.second ? state : item_state::normal)); graph.line({ right_pos - 1, r.y }, { right_pos - 1, r.bottom() - 2 }, border_color); } - margin = 0; + crp.margin = 0; column_r.x = right_pos; if (right_pos > r.right()) @@ -3258,7 +3327,7 @@ namespace nana return npos; } - void _m_draw_header_item(graph_reference graph, unsigned margin, const rectangle& column_r, const ::nana::color& fgcolor, const es_header::column& column, item_state state) + void _m_draw_header_item(graph_reference graph, const column_rendering_parameter& crp, const rectangle& column_r, const ::nana::color& fgcolor, const es_header::column& column, item_state state) { ::nana::color bgcolor; @@ -3289,7 +3358,16 @@ namespace nana { graph.palette(true, fgcolor); - point text_pos{ column_r.x + static_cast(margin), (static_cast(essence_->scheme_ptr->header_height) - static_cast(essence_->text_height)) / 2 }; + //Set column font + graph.typeface(column.typeface()); + + unsigned ascent, descent, ileading; + graph.text_metrics(ascent, descent, ileading); + + point text_pos{ + column_r.x + static_cast(crp.margin), + column_r.bottom() - static_cast(crp.height + ascent + descent) / 2 + }; if (align::left == column.alignment) text_pos.x += text_margin; @@ -3297,6 +3375,9 @@ namespace nana text_margin = 0; text_aligner.draw(column.caption, text_pos, column_r.width - text_margin); + + //Restores widget font + graph.typeface(crp.wdg_font); } auto & sort = essence_->lister.sort_attrs(); @@ -3313,16 +3394,20 @@ namespace nana { const auto & col = essence_->header.at(essence_->pointer_where.second); - auto margin = 0; - - if (&essence_->header.at(0, true) == &col) - margin = essence_->header.margin(); + column_rendering_parameter crp; + crp.margin = 0; + crp.height = essence_->header_visible_px(); + crp.max_font_px = essence_->header_font_px(); + crp.wdg_font = essence_->listbox_ptr->typeface(); - paint::graphics fl_graph({ col.width_px + margin, essence_->scheme_ptr->header_height }); + if (&essence_->header.at(0, true) == &col) + crp.margin = essence_->header.margin(); + + paint::graphics fl_graph({ col.width_px + crp.margin, crp.height }); fl_graph.typeface(essence_->graph->typeface()); - _m_draw_header_item(fl_graph, margin, rectangle{ fl_graph.size()}, colors::white, col, item_state::floated); + _m_draw_header_item(fl_graph, crp, rectangle{ fl_graph.size()}, colors::white, col, item_state::floated); auto xpos = essence_->header.range(col.index).first + pos.x - grabs_.start_pos; essence_->graph->blend(rectangle{ point{ xpos - essence_->content_view->origin().x + rect.x, rect.y } , fl_graph.size() }, fl_graph, {}, 0.5); @@ -3944,8 +4029,6 @@ namespace nana void trigger::typeface_changed(graph_reference graph) { - //essence_->text_height = graph.text_extent_size(L"jHWn0123456789/text_height = 0; unsigned as, ds, il; if (graph.text_metrics(as, ds, il)) diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index c617b3ee..3bdb32f5 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -127,11 +127,14 @@ namespace paint return impl_->real_font->family(); } - double font::size() const + double font::size(bool fixed) const { - if(empty()) return 0; + double size_pt = (empty() ? 0.0 : impl_->real_font->size()); - return impl_->real_font->size(); + if (fixed && (0.0 == size_pt)) + return platform_abstraction::font_default_pt(); + + return size_pt; } bool font::bold() const