| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805 |
- // ================================================================================
- // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
- // == See tb_core.h for more information. ==
- // ================================================================================
- #include "tb_widgets_common.h"
- #include "tb_font_renderer.h"
- #include "tb_widgets_listener.h"
- #include "tb_system.h"
- #include <assert.h>
- namespace tb {
- // == TBWidgetString =======================================
- TBWidgetString::TBWidgetString()
- : m_text_align(TB_TEXT_ALIGN_CENTER)
- {
- }
- int TBWidgetString::GetWidth(TBWidget *widget)
- {
- return widget->GetFont()->GetStringWidth(m_text);
- }
- int TBWidgetString::GetHeight(TBWidget *widget)
- {
- return widget->GetFont()->GetHeight();
- }
- void TBWidgetString::Paint(TBWidget *widget, const TBRect &rect, const TBColor &color)
- {
- // TODO: store calculated string width to avoid recalculation each frame
- TBFontFace *font = widget->GetFont();
- int string_w = GetWidth(widget);
- int x = rect.x;
- if (m_text_align == TB_TEXT_ALIGN_RIGHT)
- x += rect.w - string_w;
- else if (m_text_align == TB_TEXT_ALIGN_CENTER)
- x += MAX(0, (rect.w - string_w) / 2);
- int y = rect.y + (rect.h - GetHeight(widget)) / 2;
- if (string_w <= rect.w)
- {
- widget->SetShortened(false);
- font->DrawString(x, y, color, m_text);
- }
- else
- {
- // There's not enough room for the entire string
- // so cut it off and end with ellipsis (...)
- // const char *end = "…"; // 2026 HORIZONTAL ELLIPSIS
- // Some fonts seem to render ellipsis a lot uglier than three dots.
- const char *end = "...";
- widget->SetShortened(true);
- int endw = font->GetStringWidth(end);
- int startw = 0;
- int startlen = 0;
- while (m_text.CStr()[startlen])
- {
- int new_startw = font->GetStringWidth(m_text, startlen);
- if (new_startw + endw > rect.w)
- break;
- startw = new_startw;
- startlen++;
- }
- startlen = MAX(0, startlen - 1);
- font->DrawString(x, y, color, m_text, startlen);
- font->DrawString(x + startw, y, color, end);
- }
- }
- // == TBTextField =======================================
- /** This value on m_cached_text_width means it needs to be updated again. */
- #define UPDATE_TEXT_WIDTH_CACHE -1
- TBTextField::TBTextField()
- : m_cached_text_width(UPDATE_TEXT_WIDTH_CACHE)
- , m_squeezable(false)
- {
- SetSkinBg(TBIDC("TBTextField"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- bool TBTextField::SetText(const char *text)
- {
- if (m_text.m_text.Equals(text))
- return true;
- m_cached_text_width = UPDATE_TEXT_WIDTH_CACHE;
- Invalidate();
- InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
- return m_text.SetText(text);
- }
- void TBTextField::SetSqueezable(bool squeezable)
- {
- if (squeezable == m_squeezable)
- return;
- m_squeezable = squeezable;
- Invalidate();
- InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
- }
- PreferredSize TBTextField::OnCalculatePreferredContentSize(const SizeConstraints &constraints)
- {
- PreferredSize ps;
- if (m_cached_text_width == UPDATE_TEXT_WIDTH_CACHE)
- m_cached_text_width = m_text.GetWidth(this);
- ps.pref_w = m_cached_text_width;
- ps.pref_h = ps.min_h = m_text.GetHeight(this);
- // If gravity pull both up and down, use default max_h (grow as much as possible).
- // Otherwise it makes sense to only accept one line height.
- if (!((GetGravity() & WIDGET_GRAVITY_TOP) && (GetGravity() & WIDGET_GRAVITY_BOTTOM)))
- ps.max_h = ps.pref_h;
- if (!m_squeezable)
- ps.min_w = ps.pref_w;
- return ps;
- }
- void TBTextField::OnFontChanged()
- {
- m_cached_text_width = UPDATE_TEXT_WIDTH_CACHE;
- InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
- }
- void TBTextField::OnPaint(const PaintProps &paint_props)
- {
- m_text.Paint(this, GetPaddingRect(), paint_props.text_color);
- }
- // == TBButton =======================================
- const int auto_click_first_delay = 500;
- const int auto_click_repeat_delay = 100;
- TBButton::TBButton()
- : m_auto_repeat_click(false)
- , m_toggle_mode(false)
- {
- SetIsFocusable(true);
- SetClickByKey(true);
- SetSkinBg(TBIDC("TBButton"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- AddChild(&m_layout);
- // Set the textfield gravity to all, even though it would display the same with default gravity.
- // This will make the buttons layout expand if there is space available, without forcing the parent
- // layout to grow to make the space available.
- m_textfield.SetGravity(WIDGET_GRAVITY_ALL);
- m_layout.AddChild(&m_textfield);
- m_layout.SetRect(GetPaddingRect());
- m_layout.SetGravity(WIDGET_GRAVITY_ALL);
- m_layout.SetPaintOverflowFadeout(false);
- }
- TBButton::~TBButton()
- {
- m_layout.RemoveChild(&m_textfield);
- RemoveChild(&m_layout);
- }
- bool TBButton::SetText(const char *text)
- {
- bool ret = m_textfield.SetText(text);
- UpdateTextFieldVisibility();
- return ret;
- }
- void TBButton::OnCaptureChanged(bool captured)
- {
- if (captured && m_auto_repeat_click)
- PostMessageDelayed(TBIDC("auto_click"), nullptr, auto_click_first_delay);
- else if (!captured)
- {
- if (TBMessage *msg = GetMessageByID(TBIDC("auto_click")))
- DeleteMessage(msg);
- }
- SetCaptured(captured);
- }
- void TBButton::OnSkinChanged()
- {
- m_layout.SetRect(GetPaddingRect());
- }
- bool TBButton::OnEvent(const TBWidgetEvent &ev)
- {
- if (m_toggle_mode && ev.type == EVENT_TYPE_CLICK && ev.target == this)
- {
- TBWidgetSafePointer this_widget(this);
- SetValue(!GetValue());
- if (!this_widget.Get())
- return true; // We got removed so we actually handled this event.
- // Invoke a changed event.
- TBWidgetEvent ev(EVENT_TYPE_CHANGED);
- InvokeEvent(ev);
- if (!this_widget.Get())
- return true; // We got removed so we actually handled this event.
- // Intentionally don't return true for this event. We want it to continue propagating.
- }
- return TBWidget::OnEvent(ev);
- }
- void TBButton::OnMessageReceived(TBMessage *msg)
- {
- if (msg->message == TBIDC("auto_click"))
- {
- assert(captured_widget == this);
- if (!cancel_click && GetHitStatus(pointer_move_widget_x, pointer_move_widget_y))
- {
- TBWidgetEvent ev(EVENT_TYPE_CLICK, pointer_move_widget_x, pointer_move_widget_y, true);
- captured_widget->InvokeEvent(ev);
- }
- if (auto_click_repeat_delay)
- PostMessageDelayed(TBIDC("auto_click"), nullptr, auto_click_repeat_delay);
- }
- }
- WIDGET_HIT_STATUS TBButton::GetHitStatus(int x, int y)
- {
- // Never hit any of the children to the button. We always want to the button itself.
- return TBWidget::GetHitStatus(x, y) ? WIDGET_HIT_STATUS_HIT_NO_CHILDREN : WIDGET_HIT_STATUS_NO_HIT;
- }
- void TBButton::UpdateTextFieldVisibility()
- {
- // Auto-collapse the textfield if the text is empty and there are other
- // widgets added apart from the textfield. This removes the extra spacing
- // added between the textfield and the other widget.
- bool collapse_textfield = m_textfield.IsEmpty() && m_layout.GetFirstChild() != m_layout.GetLastChild();
- m_textfield.SetVisibilility(collapse_textfield ? WIDGET_VISIBILITY_GONE : WIDGET_VISIBILITY_VISIBLE);
- }
- void TBButton::ButtonLayout::OnChildAdded(TBWidget *child)
- {
- static_cast<TBButton*>(GetParent())->UpdateTextFieldVisibility();
- }
- void TBButton::ButtonLayout::OnChildRemove(TBWidget *child)
- {
- static_cast<TBButton*>(GetParent())->UpdateTextFieldVisibility();
- }
- // == TBClickLabel ==========================================================================================
- TBClickLabel::TBClickLabel()
- {
- AddChild(&m_layout);
- m_layout.AddChild(&m_textfield);
- m_layout.SetRect(GetPaddingRect());
- m_layout.SetGravity(WIDGET_GRAVITY_ALL);
- m_layout.SetLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP);
- }
- TBClickLabel::~TBClickLabel()
- {
- m_layout.RemoveChild(&m_textfield);
- RemoveChild(&m_layout);
- }
- bool TBClickLabel::OnEvent(const TBWidgetEvent &ev)
- {
- // Get a widget from the layout that isn't the textfield, or just bail out
- // if we only have the textfield.
- if (m_layout.GetFirstChild() == m_layout.GetLastChild())
- return false;
- TBWidget *click_target = (m_layout.GetFirstChild() == &m_textfield ? m_layout.GetLastChild() : m_layout.GetFirstChild());
- // Invoke the event on it, as if it was invoked on the target itself.
- if (click_target && ev.target != click_target)
- {
- // Focus the target if we clicked the label.
- if (ev.type == EVENT_TYPE_CLICK)
- click_target->SetFocus(WIDGET_FOCUS_REASON_POINTER);
- // Sync our pressed state with the click target. Special case for when we're just about to
- // lose it ourself (pointer is being released).
- bool pressed_state = (ev.target->GetAutoState() & WIDGET_STATE_PRESSED) ? true : false;
- if (ev.type == EVENT_TYPE_POINTER_UP || ev.type == EVENT_TYPE_CLICK)
- pressed_state = false;
- click_target->SetState(WIDGET_STATE_PRESSED, pressed_state);
- TBWidgetEvent target_ev(ev.type, ev.target_x - click_target->GetRect().x, ev.target_y - click_target->GetRect().y,
- ev.touch, ev.modifierkeys);
- return click_target->InvokeEvent(target_ev);
- }
- return false;
- }
- // == TBSkinImage =======================================
- PreferredSize TBSkinImage::OnCalculatePreferredSize(const SizeConstraints &constraints)
- {
- PreferredSize ps = TBWidget::OnCalculatePreferredSize(constraints);
- // FIX: Make it stretched proportionally if shrunk.
- ps.max_w = ps.pref_w;
- ps.max_h = ps.pref_h;
- return ps;
- }
- // == TBSeparator ===========================================
- TBSeparator::TBSeparator()
- {
- SetSkinBg(TBIDC("TBSeparator"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- SetState(WIDGET_STATE_DISABLED, true);
- }
- // == TBProgressSpinner =====================================
- // FIX: Add spin_speed to skin!
- // FIX: Make it post messages only if visible
- const int spin_speed = 1000/30; ///< How fast should the spinner animation animate.
- TBProgressSpinner::TBProgressSpinner()
- : m_value(0)
- , m_frame(0)
- {
- SetSkinBg(TBIDC("TBProgressSpinner"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- m_skin_fg.Set(TBIDC("TBProgressSpinner.fg"));
- }
- void TBProgressSpinner::SetValue(int value)
- {
- if (value == m_value)
- return;
- InvalidateSkinStates();
- assert(value >= 0); // If this happens, you probably have unballanced Begin/End calls.
- m_value = value;
- if (value > 0)
- {
- // Start animation
- if (!GetMessageByID(TBID(1)))
- {
- m_frame = 0;
- PostMessageDelayed(TBID(1), nullptr, spin_speed);
- }
- }
- else
- {
- // Stop animation
- if (TBMessage *msg = GetMessageByID(TBID(1)))
- DeleteMessage(msg);
- }
- }
- void TBProgressSpinner::OnPaint(const PaintProps &paint_props)
- {
- if (IsRunning())
- {
- TBSkinElement *e = g_tb_skin->GetSkinElement(m_skin_fg);
- if (e && e->bitmap)
- {
- int size = e->bitmap->Height();
- int num_frames = e->bitmap->Width() / e->bitmap->Height();
- int current_frame = m_frame % num_frames;
- g_renderer->DrawBitmap(GetPaddingRect(), TBRect(current_frame * size, 0, size, size), e->bitmap);
- }
- }
- }
- void TBProgressSpinner::OnMessageReceived(TBMessage *msg)
- {
- m_frame++;
- Invalidate();
- // Keep animation running
- PostMessageDelayed(TBID(1), nullptr, spin_speed);
- }
- // == TBRadioCheckBox =======================================
- TBRadioCheckBox::TBRadioCheckBox()
- : m_value(0)
- {
- SetIsFocusable(true);
- SetClickByKey(true);
- }
- //static
- void TBRadioCheckBox::ToggleGroup(TBWidget *root, TBWidget *toggled)
- {
- if (root != toggled && root->GetGroupID() == toggled->GetGroupID())
- root->SetValue(0);
- for (TBWidget *child = root->GetFirstChild(); child; child = child->GetNext())
- ToggleGroup(child, toggled);
- }
- void TBRadioCheckBox::SetValue(int value)
- {
- if (m_value == value)
- return;
- m_value = value;
- SetState(WIDGET_STATE_SELECTED, value ? true : false);
- Invalidate();
- TBWidgetEvent ev(EVENT_TYPE_CHANGED);
- InvokeEvent(ev);
- if (!value || !GetGroupID())
- return;
- // Toggle all other widgets in the same group. First get a root widget
- // for the search.
- TBWidget *group = this;
- while (group && !group->GetIsGroupRoot())
- group = group->GetParent();
- if (group)
- {
- ToggleGroup(group, this);
- }
- }
- PreferredSize TBRadioCheckBox::OnCalculatePreferredSize(const SizeConstraints &constraints)
- {
- PreferredSize ps = TBWidget::OnCalculatePreferredSize(constraints);
- ps.min_w = ps.max_w = ps.pref_w;
- ps.min_h = ps.max_h = ps.pref_h;
- return ps;
- }
- bool TBRadioCheckBox::OnEvent(const TBWidgetEvent &ev)
- {
- if (ev.target == this && ev.type == EVENT_TYPE_CLICK)
- {
- // Toggle the value, if it's not a grouped widget with value on.
- if (!(GetGroupID() && GetValue()))
- {
- SetValue(!GetValue());
- }
- }
- return TBWidget::OnEvent(ev);
- }
- // == TBScrollBar =======================================
- TBScrollBar::TBScrollBar()
- : m_axis(AXIS_Y) ///< Make SetAxis below always succeed and set the skin
- , m_value(0)
- , m_min(0)
- , m_max(1)
- , m_visible(1)
- , m_to_pixel_factor(0)
- {
- SetAxis(AXIS_X);
- AddChild(&m_handle);
- }
- TBScrollBar::~TBScrollBar()
- {
- RemoveChild(&m_handle);
- }
- void TBScrollBar::SetAxis(AXIS axis)
- {
- if (axis == m_axis)
- return;
- m_axis = axis;
- if (axis == AXIS_X)
- {
- SetSkinBg(TBIDC("TBScrollBarBgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- m_handle.SetSkinBg(TBIDC("TBScrollBarFgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- else
- {
- SetSkinBg(TBIDC("TBScrollBarBgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- m_handle.SetSkinBg(TBIDC("TBScrollBarFgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- Invalidate();
- }
- void TBScrollBar::SetLimits(double min, double max, double visible)
- {
- max = MAX(min, max);
- visible = MAX(visible, 0.0);
- if (min == m_min && max == m_max && m_visible == visible)
- return;
- m_min = min;
- m_max = max;
- m_visible = visible;
- SetValueDouble(m_value);
- // If we're currently dragging the scrollbar handle, convert the down point
- // to root and then back after the applying the new limit.
- // This prevents sudden jumps to unexpected positions when scrolling.
- if (captured_widget == &m_handle)
- m_handle.ConvertToRoot(pointer_down_widget_x, pointer_down_widget_y);
- UpdateHandle();
- if (captured_widget == &m_handle)
- m_handle.ConvertFromRoot(pointer_down_widget_x, pointer_down_widget_y);
- }
- void TBScrollBar::SetValueDouble(double value)
- {
- value = CLAMP(value, m_min, m_max);
- if (value == m_value)
- return;
- m_value = value;
- UpdateHandle();
- TBWidgetEvent ev(EVENT_TYPE_CHANGED);
- InvokeEvent(ev);
- }
- bool TBScrollBar::OnEvent(const TBWidgetEvent &ev)
- {
- if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == &m_handle)
- {
- if (m_to_pixel_factor > 0)
- {
- int dx = ev.target_x - pointer_down_widget_x;
- int dy = ev.target_y - pointer_down_widget_y;
- double delta_val = (m_axis == AXIS_X ? dx : dy) / m_to_pixel_factor;
- SetValueDouble(m_value + delta_val);
- }
- return true;
- }
- else if (ev.type == EVENT_TYPE_POINTER_MOVE && ev.target == this)
- return true;
- else if (ev.type == EVENT_TYPE_POINTER_DOWN && ev.target == this)
- {
- bool after_handle = (m_axis == AXIS_X ? ev.target_x > m_handle.GetRect().x : ev.target_y > m_handle.GetRect().y);
- SetValueDouble(m_value + (after_handle ? m_visible : -m_visible));
- return true;
- }
- else if (ev.type == EVENT_TYPE_WHEEL)
- {
- double old_val = m_value;
- SetValueDouble(m_value + ev.delta_y * TBSystem::GetPixelsPerLine());
- return m_value != old_val;
- }
- return false;
- }
- void TBScrollBar::UpdateHandle()
- {
- // Calculate the mover size and position
- bool horizontal = m_axis == AXIS_X;
- int available_pixels = horizontal ? GetRect().w : GetRect().h;
- int min_thickness_pixels = MIN(GetRect().h, GetRect().w);
- int visible_pixels = available_pixels;
- if (m_max - m_min > 0 && m_visible > 0)
- {
- double visible_proportion = m_visible / (m_visible + m_max - m_min);
- visible_pixels = (int)(visible_proportion * available_pixels);
- // Limit the size of the indicator to the slider thickness so that it doesn't
- // become too tiny when the visible proportion is very small.
- visible_pixels = MAX(visible_pixels, min_thickness_pixels);
- m_to_pixel_factor = (double)(available_pixels - visible_pixels) / (m_max - m_min)/*+ 0.5*/;
- }
- else
- {
- m_to_pixel_factor = 0;
- // If we can't scroll anything, make the handle invisible
- visible_pixels = 0;
- }
- int pixel_pos = (int)(m_value * m_to_pixel_factor);
- TBRect rect;
- if (horizontal)
- rect.Set(pixel_pos, 0, visible_pixels, GetRect().h);
- else
- rect.Set(0, pixel_pos, GetRect().w, visible_pixels);
- m_handle.SetRect(rect);
- }
- void TBScrollBar::OnResized(int old_w, int old_h)
- {
- UpdateHandle();
- }
- // == TBSlider ============================================
- TBSlider::TBSlider()
- : m_axis(AXIS_Y) ///< Make SetAxis below always succeed and set the skin
- , m_value(0)
- , m_min(0)
- , m_max(1)
- , m_to_pixel_factor(0)
- {
- SetIsFocusable(true);
- SetAxis(AXIS_X);
- AddChild(&m_handle);
- }
- TBSlider::~TBSlider()
- {
- RemoveChild(&m_handle);
- }
- void TBSlider::SetAxis(AXIS axis)
- {
- if (axis == m_axis)
- return;
- m_axis = axis;
- if (axis == AXIS_X)
- {
- SetSkinBg(TBIDC("TBSliderBgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- m_handle.SetSkinBg(TBIDC("TBSliderFgX"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- else
- {
- SetSkinBg(TBIDC("TBSliderBgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- m_handle.SetSkinBg(TBIDC("TBSliderFgY"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- Invalidate();
- }
- void TBSlider::SetLimits(double min, double max)
- {
- min = MIN(min, max);
- if (min == m_min && max == m_max)
- return;
- m_min = min;
- m_max = max;
- SetValueDouble(m_value);
- UpdateHandle();
- }
- void TBSlider::SetValueDouble(double value)
- {
- value = CLAMP(value, m_min, m_max);
- if (value == m_value)
- return;
- m_value = value;
- UpdateHandle();
- TBWidgetEvent ev(EVENT_TYPE_CHANGED);
- InvokeEvent(ev);
- }
- bool TBSlider::OnEvent(const TBWidgetEvent &ev)
- {
- if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == &m_handle)
- {
- if (m_to_pixel_factor > 0)
- {
- int dx = ev.target_x - pointer_down_widget_x;
- int dy = ev.target_y - pointer_down_widget_y;
- double delta_val = (m_axis == AXIS_X ? dx : -dy) / m_to_pixel_factor;
- SetValueDouble(m_value + delta_val);
- }
- return true;
- }
- else if (ev.type == EVENT_TYPE_WHEEL)
- {
- double old_val = m_value;
- double step = (m_axis == AXIS_X ? GetSmallStep() : -GetSmallStep());
- SetValueDouble(m_value + step * ev.delta_y);
- return m_value != old_val;
- }
- else if (ev.type == EVENT_TYPE_KEY_DOWN)
- {
- double step = (m_axis == AXIS_X ? GetSmallStep() : -GetSmallStep());
- if (ev.special_key == TB_KEY_LEFT || ev.special_key == TB_KEY_UP)
- SetValueDouble(GetValueDouble() - step);
- else if (ev.special_key == TB_KEY_RIGHT || ev.special_key == TB_KEY_DOWN)
- SetValueDouble(GetValueDouble() + step);
- else
- return false;
- return true;
- }
- else if (ev.type == EVENT_TYPE_KEY_UP)
- {
- if (ev.special_key == TB_KEY_LEFT || ev.special_key == TB_KEY_UP ||
- ev.special_key == TB_KEY_RIGHT || ev.special_key == TB_KEY_DOWN)
- return true;
- }
- return TBWidget::OnEvent(ev);
- }
- void TBSlider::UpdateHandle()
- {
- // Calculate the handle position
- bool horizontal = m_axis == AXIS_X;
- int available_pixels = horizontal ? GetRect().w : GetRect().h;
- TBRect rect;
- if (m_max - m_min > 0)
- {
- PreferredSize ps = m_handle.GetPreferredSize();
- int handle_pixels = horizontal ? ps.pref_w : ps.pref_h;
- m_to_pixel_factor = (double)(available_pixels - handle_pixels) / (m_max - m_min)/*+ 0.5*/;
- int pixel_pos = (int)((m_value - m_min) * m_to_pixel_factor);
- if (horizontal)
- rect.Set(pixel_pos, (GetRect().h - ps.pref_h) / 2, ps.pref_w, ps.pref_h);
- else
- rect.Set((GetRect().w - ps.pref_w) / 2, GetRect().h - handle_pixels - pixel_pos, ps.pref_w, ps.pref_h);
- }
- else
- m_to_pixel_factor = 0;
- m_handle.SetRect(rect);
- }
- void TBSlider::OnResized(int old_w, int old_h)
- {
- UpdateHandle();
- }
- // == TBContainer ===================================
- TBContainer::TBContainer()
- {
- SetSkinBg(TBIDC("TBContainer"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- // == TBMover =======================================
- TBMover::TBMover()
- {
- SetSkinBg(TBIDC("TBMover"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- bool TBMover::OnEvent(const TBWidgetEvent &ev)
- {
- TBWidget *target = GetParent();
- if (!target)
- return false;
- if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == this)
- {
- int dx = ev.target_x - pointer_down_widget_x;
- int dy = ev.target_y - pointer_down_widget_y;
- TBRect rect = target->GetRect().Offset(dx, dy);
- if (target->GetParent())
- {
- // Apply limit.
- rect.x = CLAMP(rect.x, -pointer_down_widget_x, target->GetParent()->GetRect().w - pointer_down_widget_x);
- rect.y = CLAMP(rect.y, -pointer_down_widget_y, target->GetParent()->GetRect().h - pointer_down_widget_y);
- }
- target->SetRect(rect);
- return true;
- }
- return false;
- }
- // == TBResizer =======================================
- TBResizer::TBResizer()
- {
- SetSkinBg(TBIDC("TBResizer"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- }
- WIDGET_HIT_STATUS TBResizer::GetHitStatus(int x, int y)
- {
- // Shave off some of the upper left diagonal half from the hit area.
- const int extra_hit_area = 3;
- if (x < GetRect().w - y - extra_hit_area)
- return WIDGET_HIT_STATUS_NO_HIT;
- return TBWidget::GetHitStatus(x, y);
- }
- bool TBResizer::OnEvent(const TBWidgetEvent &ev)
- {
- TBWidget *target = GetParent();
- if (!target)
- return false;
- if (ev.type == EVENT_TYPE_POINTER_MOVE && captured_widget == this)
- {
- int dx = ev.target_x - pointer_down_widget_x;
- int dy = ev.target_y - pointer_down_widget_y;
- TBRect rect = target->GetRect();
- rect.w += dx;
- rect.h += dy;
- // Apply limit. We should not use minimum size since we can squeeze
- // the layout much more, and provide scroll/pan when smaller.
- rect.w = MAX(rect.w, 50);
- rect.h = MAX(rect.h, 50);
- target->SetRect(rect);
- }
- else
- return false;
- return true;
- }
- // == TBDimmer =======================================
- TBDimmer::TBDimmer()
- {
- SetSkinBg(TBIDC("TBDimmer"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
- SetGravity(WIDGET_GRAVITY_ALL);
- }
- void TBDimmer::OnAdded()
- {
- SetRect(TBRect(0, 0, GetParent()->GetRect().w, GetParent()->GetRect().h));
- }
- }; // namespace tb
|