nana/source/gui/widgets/picture.cpp

303 lines
7.8 KiB
C++

/*
* A Picture Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/picture.cpp
* @description:
* Used for showing a picture
*/
#include <nana/gui/widgets/picture.hpp>
#include <nana/gui/layout_utility.hpp>
#include <nana/paint/image.hpp>
#include <nana/gui/element.hpp>
#include <nana/gui/detail/widget_content_measurer_interface.hpp>
namespace nana
{
namespace drawerbase
{
namespace picture
{
class content_measurer;
struct implement
{
widget* wdg_ptr{nullptr};
std::unique_ptr<content_measurer> measurer;
struct gradual_bground_tag
{
::nana::color gradual_from;
::nana::color gradual_to;
bool horizontal{true};
}gradual_bground;
struct back_image_tag
{
paint::image image;
rectangle valid_area;
::nana::align align_horz{ ::nana::align::left };
::nana::align_v align_vert{ ::nana::align_v::top };
std::unique_ptr<element::bground> bground; //If it is not a null ptr, the widget is stretchable mode
bool stretchable{ false }; //If it is true, the widget is stretchable mode without changing aspect ratio.
}backimg;
void draw_background(paint::graphics& graph, const size& dimension)
{
if (!API::dev::copy_transparent_background(*wdg_ptr, graph))
{
auto const graph_size = graph.size();
if (dimension.width < graph_size.width || dimension.height < graph_size.height || backimg.image.alpha())
{
if (gradual_bground.gradual_from.invisible() || gradual_bground.gradual_to.invisible())
graph.rectangle(true, wdg_ptr->bgcolor());
else if (gradual_bground.gradual_from == gradual_bground.gradual_to)
graph.rectangle(true, gradual_bground.gradual_from);
else
graph.gradual_rectangle(::nana::rectangle{graph_size }, gradual_bground.gradual_from, gradual_bground.gradual_to, !gradual_bground.horizontal);
}
}
}
};
class content_measurer
: public dev::widget_content_measurer_interface
{
public:
content_measurer(implement* impl)
: impl_{impl}
{}
std::optional<size> measure(graph_reference /*graph*/, unsigned limit_pixels, bool /*limit_width*/) const override
{
//Picture doesn't provide a support of vfit and hfit
if (!limit_pixels)
{
if (impl_->backimg.valid_area.empty())
return impl_->backimg.image.size();
}
return{};
}
size extension() const override
{
return{};
}
private:
implement* const impl_;
};
//class drawer
drawer::drawer() :impl_(new implement)
{
impl_->measurer.reset(new content_measurer{impl_});
}
drawer::~drawer()
{
delete impl_;
}
void drawer::attached(widget_reference& wdg, graph_reference)
{
impl_->wdg_ptr = &wdg;
API::dev::set_measurer(wdg, impl_->measurer.get());
}
void drawer::refresh(graph_reference graph)
{
auto const graphsize = graph.size();
auto & backimg = impl_->backimg;
if (!backimg.bground)
{
if (backimg.image.empty())
{
impl_->draw_background(graph, {});
return;
}
auto valid_area = backimg.valid_area;
if (valid_area.empty())
valid_area.dimension(backimg.image.size());
//The position where the image to be drawn.
::nana::point pos;
if (backimg.stretchable)
{
auto fit_size = fit_zoom(valid_area.dimension(), graphsize);
if (fit_size.width != graphsize.width)
{
switch (backimg.align_horz)
{
case ::nana::align::left: break;
case ::nana::align::center:
pos.x = (int(graphsize.width) - int(fit_size.width)) / 2;
break;
case ::nana::align::right:
pos.x = int(graphsize.width) - int(fit_size.width);
break;
}
}
else if (fit_size.height != graphsize.height)
{
switch (backimg.align_vert)
{
case ::nana::align_v::top: break;
case ::nana::align_v::center:
pos.y = (int(graphsize.height) - int(fit_size.height)) / 2;
break;
case ::nana::align_v::bottom:
pos.y = int(graphsize.height) - int(fit_size.height);
break;
}
}
impl_->draw_background(graph, fit_size);
backimg.image.stretch(valid_area, graph, ::nana::rectangle{ pos, fit_size });
}
else
{
switch (backimg.align_horz)
{
case ::nana::align::left: break;
case ::nana::align::center:
pos.x = (int(graphsize.width) - int(valid_area.width)) / 2;
break;
case ::nana::align::right:
pos.x = int(graphsize.width) - int(valid_area.width);
break;
}
switch (backimg.align_vert)
{
case ::nana::align_v::top: break;
case ::nana::align_v::center:
pos.y = (int(graphsize.height) - int(valid_area.height)) / 2;
break;
case ::nana::align_v::bottom:
pos.y = int(graphsize.height) - int(valid_area.height);
break;
}
impl_->draw_background(graph, valid_area.dimension());
backimg.image.paste(valid_area, graph, pos);
}
}
else
{
impl_->draw_background(graph, graphsize);
color invalid_clr_for_call;
backimg.bground->draw(graph, invalid_clr_for_call, invalid_clr_for_call, rectangle{ graphsize }, element_state::normal);
}
graph.setsta();
}
//end class drawer
}//end namespace picture
}//end namespace drawerbase
//class picture
picture::picture(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
picture::picture(window wd, const nana::rectangle& r, bool visible)
{
create(wd, r, visible);
}
void picture::load(::nana::paint::image img, const ::nana::rectangle& valid_area)
{
internal_scope_guard lock;
auto& backimg = get_drawer_trigger().impl_->backimg;
backimg.image = std::move(img);
backimg.valid_area = valid_area;
if (backimg.bground)
backimg.bground->image(backimg.image, true, valid_area);
API::refresh_window(*this);
}
void picture::align(::nana::align horz, align_v vert)
{
internal_scope_guard lock;
auto& backimg = get_drawer_trigger().impl_->backimg;
if (backimg.align_horz == horz && backimg.align_vert == vert)
return;
backimg.align_horz = horz;
backimg.align_vert = vert;
API::refresh_window(*this);
}
void picture::stretchable(unsigned left, unsigned top, unsigned right, unsigned bottom)
{
internal_scope_guard lock;
auto & backimg = get_drawer_trigger().impl_->backimg;
if (!backimg.bground)
{
backimg.bground.reset(new element::bground);
backimg.bground->states({ element_state::normal });
backimg.bground->image(backimg.image, true, backimg.valid_area);
}
backimg.bground->stretch_parts(left, top, right, bottom);
backimg.stretchable = false;
API::refresh_window(*this);
}
void picture::stretchable(bool enables)
{
internal_scope_guard lock;
auto & backimg = get_drawer_trigger().impl_->backimg;
backimg.bground.reset();
backimg.stretchable = enables;
API::refresh_window(*this);
}
void picture::set_gradual_background(const ::nana::color& from, const ::nana::color& to, bool horizontal)
{
auto & bground = get_drawer_trigger().impl_->gradual_bground;
bground.gradual_from = from;
bground.gradual_to = to;
bground.horizontal = horizontal;
API::refresh_window(*this);
}
void picture::transparent(bool enabled)
{
if(enabled)
API::effects_bground(*this, effects::bground_transparent(0), 0.0);
else
API::effects_bground_remove(*this);
}
bool picture::transparent() const
{
return API::is_transparent_background(*this);
}
//end class picture
}//end namespace nana