/* * A Scroll Implementation * Copyright(C) 2003-2013 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/scroll.cpp */ #include namespace nana { namespace drawerbase { namespace scroll { //struct metrics_type metrics_type::metrics_type() :peak(1), range(1), step(1), value(0), what(buttons::none), pressed(false), scroll_length(0), scroll_pos(0) {} //end struct metrics_type //class drawer drawer::drawer(metrics_type& m) :metrics_(m) {} void drawer::set_vertical(bool v) { vertical_ = v; } buttons drawer::what(graph_reference graph, const point& screen_pos) { unsigned scale; int pos; if(vertical_) { scale = graph.height(); pos = screen_pos.y; } else { scale = graph.width(); pos = screen_pos.x; } if(scale >= fixedsize * 2) { if(pos < static_cast(fixedsize)) return buttons::first; if(pos > static_cast(scale - fixedsize)) return buttons::second; } else { if(pos < static_cast(scale / 2)) return buttons::first; if(pos > static_cast(scale / 2)) return buttons::second; } if(metrics_.scroll_length) { if(metrics_.scroll_pos + static_cast(fixedsize) <= pos && pos < metrics_.scroll_pos + static_cast(fixedsize + metrics_.scroll_length)) return buttons::scroll; } if(static_cast(fixedsize) <= pos && pos < metrics_.scroll_pos) return buttons::forward; else if(metrics_.scroll_pos + static_cast(metrics_.scroll_length) <= pos && pos < static_cast(scale - fixedsize)) return buttons::backward; return buttons::none; } void drawer::scroll_delta_pos(graph_reference graph, int mouse_pos) { if(mouse_pos + metrics_.scroll_mouse_offset == metrics_.scroll_pos) return; unsigned scale = vertical_ ? graph.height() : graph.width(); if(scale > fixedsize * 2) { int pos = mouse_pos - metrics_.scroll_mouse_offset; const unsigned scroll_area = static_cast(scale - fixedsize * 2 - metrics_.scroll_length); if(pos < 0) pos = 0; else if(pos > static_cast(scroll_area)) pos = static_cast(scroll_area); metrics_.scroll_pos = pos; auto value_max = metrics_.peak - metrics_.range; metrics_.value = pos * value_max / scroll_area; if(metrics_.value < metrics_.peak - metrics_.range) { int selfpos = static_cast(metrics_.value * scroll_area / value_max); int nextpos = static_cast((metrics_.value + 1) * scroll_area / value_max); if(selfpos != nextpos && (pos - selfpos > nextpos - pos)) ++metrics_.value; } else metrics_.value = value_max; } } void drawer::auto_scroll() { if(_m_check()) { if(buttons::forward == metrics_.what) { //backward if(metrics_.value <= metrics_.range) metrics_.value = 0; else metrics_.value -= metrics_.range; } else if(buttons::backward == metrics_.what) { if(metrics_.peak - metrics_.range - metrics_.value <= metrics_.range) metrics_.value = metrics_.peak - metrics_.range; else metrics_.value += metrics_.range; } } } void drawer::draw(graph_reference graph, buttons what) { if(false == metrics_.pressed || metrics_.what != buttons::scroll) _m_adjust_scroll(graph); _m_background(graph); unsigned width, height; int x, y; if(vertical_) { x = 0; y = graph.height() - fixedsize; width = graph.width(); height = fixedsize; } else { x = graph.width() - fixedsize; y = 0; width = fixedsize; height = graph.height(); } int state = ((_m_check() == false || what == buttons::none) ? states::none : states::highlight); int moused_state = (_m_check() ? (metrics_.pressed ? states::selected : states::actived) : states::none); //draw first _m_draw_button(graph, 0, 0, width, height, buttons::first, (buttons::first == what ? moused_state : state)); //draw second _m_draw_button(graph, x, y, width, height, buttons::second, (buttons::second == what ? moused_state : state)); //draw scroll _m_draw_scroll(graph, (buttons::scroll == what ? moused_state : states::highlight)); } //private: void drawer::_m_background(graph_reference graph) { graph.rectangle(0xF0F0F0, true); if(metrics_.pressed && _m_check()) { int x = 0, y = 0; unsigned width = graph.width(), height = graph.height(); if(metrics_.what == buttons::forward) { *(vertical_ ? &y : &x) = fixedsize; *(vertical_ ? &height: &width) = metrics_.scroll_pos; } else if(buttons::backward == metrics_.what) { *(vertical_ ? &y : &x) = static_cast(fixedsize + metrics_.scroll_pos + metrics_.scroll_length); *(vertical_ ? &height: &width) = static_cast((vertical_ ? graph.height() : graph.width()) - (fixedsize * 2 + metrics_.scroll_pos + metrics_.scroll_length)); } else return; if(width && height) graph.rectangle(x, y, width, height, 0xDCDCDC, true); } } void drawer::_m_button_frame(graph_reference graph, int x, int y, unsigned width, unsigned height, int state) { if(state) { unsigned color = 0x979797; //highlight switch(state) { case states::actived: color = 0x86D5FD; break; case states::selected: color = 0x3C7FB1; break; } graph.rectangle(rectangle(x, y, width, height), color, false); unsigned color_x = graph.mix(color, 0xFFFFFF, 0.5); x += 2; y += 2; width -= 4; height -= 4; if(vertical_) { unsigned half = width / 2; graph.rectangle(x + (width - half), y, half, height, color_x, true); width -= half; } else { unsigned half = height / 2; graph.rectangle(x, y + height - half, width, half, color_x, true); height -= half; } graph.shadow_rectangle(x, y, width, height, 0xFFFFFF, color_x, !vertical_); } } bool drawer::_m_check() const { return (metrics_.scroll_length && metrics_.range && (metrics_.peak > metrics_.range)); } void drawer::_m_adjust_scroll(graph_reference graph) { if(metrics_.range == 0 || metrics_.peak <= metrics_.range) return; unsigned pixels = vertical_ ? graph.height() : graph.width(); int pos = 0; unsigned len = 0; if(pixels > fixedsize * 2) { pixels -= (fixedsize * 2); len = static_cast(pixels * metrics_.range / metrics_.peak); if(len < fixedsize) len = fixedsize; if(metrics_.value) { pos = static_cast(pixels - len); if(metrics_.value + metrics_.range >= metrics_.peak) metrics_.value = metrics_.peak - metrics_.range; else pos = static_cast((metrics_.value * pos) /(metrics_.peak - metrics_.range)); } } metrics_.scroll_pos = pos; metrics_.scroll_length = len; } void drawer::_m_draw_scroll(graph_reference graph, int state) { if(_m_check()) { int x, y; unsigned width, height; if(vertical_) { x = 0; y = fixedsize + metrics_.scroll_pos; width = graph.width(); height = static_cast(metrics_.scroll_length); } else { x = fixedsize + metrics_.scroll_pos; y = 0; width = static_cast(metrics_.scroll_length); height = graph.height(); } _m_button_frame(graph, x, y, width, height, state); } } void drawer::_m_draw_button(graph_reference graph, int x, int y, unsigned width, unsigned height, buttons what, int state) { if(_m_check()) _m_button_frame(graph, x, y, width, height, state); using namespace nana::paint::gadget; if(buttons::first == what || buttons::second == what) { nana::size sz = graph.size(); directions::t dir; if(buttons::second == what) { if(vertical_) { y = static_cast(sz.height - fixedsize); dir = directions::to_south; } else { x = static_cast(sz.width - fixedsize); dir = directions::to_east; } } else dir = vertical_ ? directions::to_north : directions::to_west; if(vertical_) x = (static_cast(sz.width) - 16) / 2; else y = (static_cast(sz.height) - 16) / 2; arrow_16_pixels(graph, x, y, _m_check() ? 0x0 : 0x808080, (states::none == state ? 0 : 1), dir); } } //end class drawer }//end namespace scroll }//end namespace drawerbase }//end namespace nana