| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- // ================================================================================
- // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
- // == See tb_core.h for more information. ==
- // ================================================================================
- #include "tb_skin.h"
- #include "tb_system.h"
- #include "tb_tempbuffer.h"
- #include "tb_node_tree.h"
- #include <string.h>
- #include <assert.h>
- #include <stdio.h>
- namespace tb {
- // == Util functions ==========================================================
- /*TB_TEXT_ALIGN StringToTextAlign(const char *align_str)
- {
- TB_TEXT_ALIGN align = TB_TEXT_ALIGN_CENTER;
- if (strstr(state_str, "left")) align = TB_TEXT_ALIGN_LEFT;
- if (strstr(state_str, "center")) align = TB_TEXT_ALIGN_CENTER;
- if (strstr(state_str, "right")) align = TB_TEXT_ALIGN_RIGHT;
- return state;
- }*/
- SKIN_STATE StringToState(const char *state_str)
- {
- SKIN_STATE state = SKIN_STATE_NONE;
- if (strstr(state_str, "all")) state |= SKIN_STATE_ALL;
- if (strstr(state_str, "disabled")) state |= SKIN_STATE_DISABLED;
- if (strstr(state_str, "focused")) state |= SKIN_STATE_FOCUSED;
- if (strstr(state_str, "pressed")) state |= SKIN_STATE_PRESSED;
- if (strstr(state_str, "selected")) state |= SKIN_STATE_SELECTED;
- if (strstr(state_str, "hovered")) state |= SKIN_STATE_HOVERED;
- return state;
- }
- SKIN_ELEMENT_TYPE StringToType(const char *type_str)
- {
- if (strcmp(type_str, "StretchBox") == 0)
- return SKIN_ELEMENT_TYPE_STRETCH_BOX;
- else if (strcmp(type_str, "Image") == 0)
- return SKIN_ELEMENT_TYPE_IMAGE;
- else if (strcmp(type_str, "Stretch Image") == 0)
- return SKIN_ELEMENT_TYPE_STRETCH_IMAGE;
- else if (strcmp(type_str, "Tile") == 0)
- return SKIN_ELEMENT_TYPE_TILE;
- else if (strcmp(type_str, "StretchBorder") == 0)
- return SKIN_ELEMENT_TYPE_STRETCH_BORDER;
- TBDebugOut("Skin error: Unknown skin type!\n");
- return SKIN_ELEMENT_TYPE_STRETCH_BOX;
- }
- TBSkinCondition::TARGET StringToTarget(const char *target_str)
- {
- if (strcmp(target_str, "this") == 0)
- return TBSkinCondition::TARGET_THIS;
- else if (strcmp(target_str, "parent") == 0)
- return TBSkinCondition::TARGET_PARENT;
- else if (strcmp(target_str, "ancestors") == 0)
- return TBSkinCondition::TARGET_ANCESTORS;
- else if (strcmp(target_str, "prev sibling") == 0)
- return TBSkinCondition::TARGET_PREV_SIBLING;
- else if (strcmp(target_str, "next sibling") == 0)
- return TBSkinCondition::TARGET_NEXT_SIBLING;
- TBDebugOut("Skin error: Unknown target in condition!\n");
- return TBSkinCondition::TARGET_THIS;
- }
- TBSkinCondition::PROPERTY StringToProperty(const char *prop_str)
- {
- if (strcmp(prop_str, "skin") == 0)
- return TBSkinCondition::PROPERTY_SKIN;
- else if (strcmp(prop_str, "window active") == 0)
- return TBSkinCondition::PROPERTY_WINDOW_ACTIVE;
- else if (strcmp(prop_str, "axis") == 0)
- return TBSkinCondition::PROPERTY_AXIS;
- else if (strcmp(prop_str, "align") == 0)
- return TBSkinCondition::PROPERTY_ALIGN;
- else if (strcmp(prop_str, "id") == 0)
- return TBSkinCondition::PROPERTY_ID;
- else if (strcmp(prop_str, "state") == 0)
- return TBSkinCondition::PROPERTY_STATE;
- else if (strcmp(prop_str, "value") == 0)
- return TBSkinCondition::PROPERTY_VALUE;
- else if (strcmp(prop_str, "hover") == 0)
- return TBSkinCondition::PROPERTY_HOVER;
- else if (strcmp(prop_str, "capture") == 0)
- return TBSkinCondition::PROPERTY_CAPTURE;
- else if (strcmp(prop_str, "focus") == 0)
- return TBSkinCondition::PROPERTY_FOCUS;
- return TBSkinCondition::PROPERTY_CUSTOM;
- }
- // == TBSkinCondition =======================================================
- TBSkinCondition::TBSkinCondition(TARGET target, PROPERTY prop, const TBID &custom_prop, const TBID &value, TEST test)
- : m_target(target)
- , m_test(test)
- {
- m_info.prop = prop;
- m_info.custom_prop = custom_prop;
- m_info.value = value;
- }
- bool TBSkinCondition::GetCondition(TBSkinConditionContext &context) const
- {
- bool equal = context.GetCondition(m_target, m_info);
- return equal == (m_test == TEST_EQUAL);
- }
- // == TBSkin ================================================================
- TBSkin::TBSkin()
- : m_listener(nullptr)
- , m_default_disabled_opacity(0.3f)
- , m_default_placeholder_opacity(0.2f)
- , m_default_spacing(0)
- {
- g_renderer->AddListener(this);
- // Avoid filtering artifacts at edges when we draw fragments stretched.
- m_frag_manager.SetAddBorder(true);
- }
- bool TBSkin::Load(const char *skin_file, const char *override_skin_file)
- {
- if (!LoadInternal(skin_file))
- return false;
- if (override_skin_file && strlen(override_skin_file) && !LoadInternal(override_skin_file))
- return false;
- return ReloadBitmaps();
- }
- bool TBSkin::LoadInternal(const char *skin_file)
- {
- TBNode node;
- if (!node.ReadFile(skin_file))
- return false;
- TBTempBuffer skin_path;
- if (!skin_path.AppendPath(skin_file))
- return false;
- if (node.GetNode("description"))
- {
- // Check which DPI mode the dimension converter should use.
- // The base-dpi is the dpi in which the padding, spacing (and so on)
- // is specified in. If the skin supports a different DPI that is
- // closer to the screen DPI, all such dimensions will be scaled.
- int base_dpi = node.GetValueInt("description>base-dpi", 96);
- int supported_dpi = base_dpi;
- if (TBNode *supported_dpi_node = node.GetNode("description>supported-dpi"))
- {
- assert(supported_dpi_node->GetValue().IsArray() || supported_dpi_node->GetValue().GetInt() == base_dpi);
- if (TBValueArray *arr = supported_dpi_node->GetValue().GetArray())
- {
- int screen_dpi = TBSystem::GetDPI();
- int best_supported_dpi = 0;
- for (int i = 0; i < arr->GetLength(); i++)
- {
- int candidate_dpi = arr->GetValue(i)->GetInt();
- if (!best_supported_dpi || ABS(candidate_dpi - screen_dpi) < ABS(best_supported_dpi - screen_dpi))
- best_supported_dpi = candidate_dpi;
- }
- supported_dpi = best_supported_dpi;
- }
- }
- m_dim_conv.SetDPI(base_dpi, supported_dpi);
- }
- // Read skin constants
- if (const char *color = node.GetValueString("defaults>text-color", nullptr))
- m_default_text_color.SetFromString(color, strlen(color));
- m_default_disabled_opacity = node.GetValueFloat("defaults>disabled>opacity",
- m_default_disabled_opacity);
- m_default_placeholder_opacity = node.GetValueFloat("defaults>placeholder>opacity",
- m_default_placeholder_opacity);
- m_default_spacing = GetPxFromNode(node.GetNode("defaults>spacing"), m_default_spacing);
- // Iterate through all elements nodes and add skin elements or patch already
- // existing elements.
- TBNode *elements = node.GetNode("elements");
- if (elements)
- {
- TBNode *n = elements->GetFirstChild();
- while (n)
- {
- // If we have a "clone" node, clone all children from that node
- // into this node.
- while (TBNode *clone = n->GetNode("clone"))
- {
- n->Remove(clone);
- TBNode *clone_source = elements->GetNode(clone->GetValue().GetString());
- if (clone_source)
- n->CloneChildren(clone_source);
- delete clone;
- }
- // If the skin element already exist, we will call Load on it again.
- // This will patch the element with any new data from the node.
- TBID element_id(n->GetName());
- TBSkinElement *e = GetSkinElement(element_id);
- if (!e)
- {
- e = new TBSkinElement;
- if (!e)
- return false;
- m_elements.Add(element_id, e);
- }
- e->Load(n, this, skin_path.GetData());
- if (m_listener)
- m_listener->OnSkinElementLoaded(this, e, n);
- n = n->GetNext();
- }
- }
- return true;
- }
- void TBSkin::UnloadBitmaps()
- {
- // Unset all bitmap pointers.
- TBHashTableIteratorOf<TBSkinElement> it(&m_elements);
- while (TBSkinElement *element = it.GetNextContent())
- element->bitmap = nullptr;
- // Clear all fragments and bitmaps.
- m_frag_manager.Clear();
- }
- bool TBSkin::ReloadBitmaps()
- {
- UnloadBitmaps();
- bool success = ReloadBitmapsInternal();
- // Create all bitmaps for the bitmap fragment maps
- if (success)
- success = m_frag_manager.ValidateBitmaps();
- #ifdef TB_RUNTIME_DEBUG_INFO
- TBStr info;
- info.SetFormatted("Skin loaded using %d bitmaps.\n", m_frag_manager.GetNumMaps());
- TBDebugOut(info);
- #endif
- return success;
- }
- bool TBSkin::ReloadBitmapsInternal()
- {
- // Load all bitmap files into new bitmap fragments.
- TBTempBuffer filename_dst_DPI;
- bool success = true;
- TBHashTableIteratorOf<TBSkinElement> it(&m_elements);
- while (TBSkinElement *element = it.GetNextContent())
- {
- if (!element->bitmap_file.IsEmpty())
- {
- assert(!element->bitmap);
- // FIX: dedicated_map is not needed for all backends (only deprecated fixed function GL)
- bool dedicated_map = element->type == SKIN_ELEMENT_TYPE_TILE;
- // Try to load bitmap fragment in the destination DPI (F.ex "foo.png" becomes "[email protected]")
- int bitmap_dpi = m_dim_conv.GetSrcDPI();
- if (m_dim_conv.NeedConversion())
- {
- m_dim_conv.GetDstDPIFilename(element->bitmap_file, &filename_dst_DPI);
- element->bitmap = m_frag_manager.GetFragmentFromFile(filename_dst_DPI.GetData(), dedicated_map);
- if (element->bitmap)
- bitmap_dpi = m_dim_conv.GetDstDPI();
- }
- element->SetBitmapDPI(m_dim_conv, bitmap_dpi);
- // If we still have no bitmap fragment, load from default file.
- if (!element->bitmap)
- element->bitmap = m_frag_manager.GetFragmentFromFile(element->bitmap_file, dedicated_map);
- if (!element->bitmap)
- success = false;
- }
- }
- return success;
- }
- TBSkin::~TBSkin()
- {
- g_renderer->RemoveListener(this);
- }
- TBSkinElement *TBSkin::GetSkinElement(const TBID &skin_id) const
- {
- if (!skin_id)
- return nullptr;
- return m_elements.Get(skin_id);
- }
- TBSkinElement *TBSkin::GetSkinElementStrongOverride(const TBID &skin_id, SKIN_STATE state, TBSkinConditionContext &context) const
- {
- if (TBSkinElement *skin_element = GetSkinElement(skin_id))
- {
- // Avoid eternal recursion when overrides refer to elements referring back.
- if (skin_element->is_getting)
- return nullptr;
- skin_element->is_getting = true;
- // Check if there's any strong overrides for this element with the given state.
- TBSkinElementState *override_state = skin_element->m_strong_override_elements.GetStateElement(state, context);
- if (override_state)
- {
- if (TBSkinElement *override_element = GetSkinElementStrongOverride(override_state->element_id, state, context))
- {
- skin_element->is_getting = false;
- return override_element;
- }
- }
- skin_element->is_getting = false;
- return skin_element;
- }
- return nullptr;
- }
- TBSkinElement *TBSkin::PaintSkin(const TBRect &dst_rect, const TBID &skin_id, SKIN_STATE state, TBSkinConditionContext &context)
- {
- return PaintSkin(dst_rect, GetSkinElement(skin_id), state, context);
- }
- TBSkinElement *TBSkin::PaintSkin(const TBRect &dst_rect, TBSkinElement *element, SKIN_STATE state, TBSkinConditionContext &context)
- {
- if (!element || element->is_painting)
- return nullptr;
- // Avoid potential endless recursion in evil skins
- element->is_painting = true;
- // Return the override if we have one.
- TBSkinElement *return_element = element;
- TB_IF_DEBUG(bool paint_error_highlight = false);
- // If there's any override for this state, paint it.
- TBSkinElementState *override_state = element->m_override_elements.GetStateElement(state, context);
- if (override_state)
- {
- if (TBSkinElement *used_override = PaintSkin(dst_rect, override_state->element_id, state, context))
- return_element = used_override;
- else
- {
- TB_IF_DEBUG(paint_error_highlight = true);
- TBDebugOut("Skin error: The skin references a missing element, or has a reference loop!\n");
- // Fall back to the standard skin.
- override_state = nullptr;
- }
- }
- // If there was no override, paint the standard skin element.
- if (!override_state)
- PaintElement(dst_rect, element);
- // Paint all child elements that matches the state (or should be painted for all states)
- if (element->m_child_elements.HasStateElements())
- {
- const TBSkinElementState *state_element = element->m_child_elements.GetFirstElement();
- while (state_element)
- {
- if (state_element->IsMatch(state, context))
- PaintSkin(dst_rect, state_element->element_id, state_element->state & state, context);
- state_element = state_element->GetNext();
- }
- }
- // Paint ugly rectangles on invalid skin elements in debug builds.
- TB_IF_DEBUG(if (paint_error_highlight) g_renderer->DrawRect(dst_rect.Expand(1, 1), TBColor(255, 205, 0)));
- TB_IF_DEBUG(if (paint_error_highlight) g_renderer->DrawRect(dst_rect.Shrink(1, 1), TBColor(255, 0, 0)));
- element->is_painting = false;
- return return_element;
- }
- void TBSkin::PaintSkinOverlay(const TBRect &dst_rect, TBSkinElement *element, SKIN_STATE state, TBSkinConditionContext &context)
- {
- if (!element || element->is_painting)
- return;
- // Avoid potential endless recursion in evil skins
- element->is_painting = true;
- // Paint all overlay elements that matches the state (or should be painted for all states)
- const TBSkinElementState *state_element = element->m_overlay_elements.GetFirstElement();
- while (state_element)
- {
- if (state_element->IsMatch(state, context))
- PaintSkin(dst_rect, state_element->element_id, state_element->state & state, context);
- state_element = state_element->GetNext();
- }
- element->is_painting = false;
- }
- void TBSkin::PaintElement(const TBRect &dst_rect, TBSkinElement *element)
- {
- PaintElementBGColor(dst_rect, element);
- if (!element->bitmap)
- return;
- if (element->type == SKIN_ELEMENT_TYPE_IMAGE)
- PaintElementImage(dst_rect, element);
- else if (element->type == SKIN_ELEMENT_TYPE_TILE)
- PaintElementTile(dst_rect, element);
- else if (element->type == SKIN_ELEMENT_TYPE_STRETCH_IMAGE || element->cut == 0)
- PaintElementStretchImage(dst_rect, element);
- else if (element->type == SKIN_ELEMENT_TYPE_STRETCH_BORDER)
- PaintElementStretchBox(dst_rect, element, false);
- else
- PaintElementStretchBox(dst_rect, element, true);
- }
- TBRect TBSkin::GetFlippedRect(const TBRect &src_rect, TBSkinElement *element) const
- {
- // Turning the source rect "inside out" will flip the result when rendered.
- TBRect tmp_rect = src_rect;
- if (element->flip_x)
- {
- tmp_rect.x += tmp_rect.w;
- tmp_rect.w = -tmp_rect.w;
- }
- if (element->flip_y)
- {
- tmp_rect.y += tmp_rect.h;
- tmp_rect.h = -tmp_rect.h;
- }
- return tmp_rect;
- }
- void TBSkin::PaintElementBGColor(const TBRect &dst_rect, TBSkinElement *element)
- {
- if (element->bg_color == 0)
- return;
- g_renderer->DrawRectFill(dst_rect, element->bg_color);
- }
- void TBSkin::PaintElementImage(const TBRect &dst_rect, TBSkinElement *element)
- {
- TBRect src_rect(0, 0, element->bitmap->Width(), element->bitmap->Height());
- TBRect rect = dst_rect.Expand(element->expand, element->expand);
- rect.Set(rect.x + element->img_ofs_x + (rect.w - src_rect.w) * element->img_position_x / 100,
- rect.y + element->img_ofs_y + (rect.h - src_rect.h) * element->img_position_y / 100,
- src_rect.w, src_rect.h);
- g_renderer->DrawBitmap(rect, GetFlippedRect(src_rect, element), element->bitmap);
- }
- void TBSkin::PaintElementTile(const TBRect &dst_rect, TBSkinElement *element)
- {
- TBRect rect = dst_rect.Expand(element->expand, element->expand);
- g_renderer->DrawBitmapTile(rect, element->bitmap->GetBitmap());
- }
- void TBSkin::PaintElementStretchImage(const TBRect &dst_rect, TBSkinElement *element)
- {
- if (dst_rect.IsEmpty())
- return;
- TBRect rect = dst_rect.Expand(element->expand, element->expand);
- TBRect src_rect = GetFlippedRect(TBRect(0, 0, element->bitmap->Width(), element->bitmap->Height()), element);
- g_renderer->DrawBitmap(rect, src_rect, element->bitmap);
- }
- void TBSkin::PaintElementStretchBox(const TBRect &dst_rect, TBSkinElement *element, bool fill_center)
- {
- if (dst_rect.IsEmpty())
- return;
- TBRect rect = dst_rect.Expand(element->expand, element->expand);
- // Stretch the dst_cut (if rect is smaller than the skin size)
- // FIX: the expand should also be stretched!
- int cut = element->cut;
- int dst_cut_w = MIN(cut, rect.w / 2);
- int dst_cut_h = MIN(cut, rect.h / 2);
- int bw = element->bitmap->Width();
- int bh = element->bitmap->Height();
- bool has_left_right_edges = rect.h > dst_cut_h * 2;
- bool has_top_bottom_edges = rect.w > dst_cut_w * 2;
- rect = GetFlippedRect(rect, element);
- if (element->flip_x)
- dst_cut_w = -dst_cut_w;
- if (element->flip_y)
- dst_cut_h = -dst_cut_h;
- // Corners
- g_renderer->DrawBitmap(TBRect(rect.x, rect.y, dst_cut_w, dst_cut_h), TBRect(0, 0, cut, cut), element->bitmap);
- g_renderer->DrawBitmap(TBRect(rect.x + rect.w - dst_cut_w, rect.y, dst_cut_w, dst_cut_h), TBRect(bw - cut, 0, cut, cut), element->bitmap);
- g_renderer->DrawBitmap(TBRect(rect.x, rect.y + rect.h - dst_cut_h, dst_cut_w, dst_cut_h), TBRect(0, bh - cut, cut, cut), element->bitmap);
- g_renderer->DrawBitmap(TBRect(rect.x + rect.w - dst_cut_w, rect.y + rect.h - dst_cut_h, dst_cut_w, dst_cut_h), TBRect(bw - cut, bh - cut, cut, cut), element->bitmap);
- // Left & right edge
- if (has_left_right_edges)
- {
- g_renderer->DrawBitmap(TBRect(rect.x, rect.y + dst_cut_h, dst_cut_w, rect.h - dst_cut_h * 2), TBRect(0, cut, cut, bh - cut * 2), element->bitmap);
- g_renderer->DrawBitmap(TBRect(rect.x + rect.w - dst_cut_w, rect.y + dst_cut_h, dst_cut_w, rect.h - dst_cut_h * 2), TBRect(bw - cut, cut, cut, bh - cut * 2), element->bitmap);
- }
- // Top & bottom edge
- if (has_top_bottom_edges)
- {
- g_renderer->DrawBitmap(TBRect(rect.x + dst_cut_w, rect.y, rect.w - dst_cut_w * 2, dst_cut_h), TBRect(cut, 0, bw - cut * 2, cut), element->bitmap);
- g_renderer->DrawBitmap(TBRect(rect.x + dst_cut_w, rect.y + rect.h - dst_cut_h, rect.w - dst_cut_w * 2, dst_cut_h), TBRect(cut, bh - cut, bw - cut * 2, cut), element->bitmap);
- }
- // Center
- if (fill_center && has_top_bottom_edges && has_left_right_edges)
- g_renderer->DrawBitmap(TBRect(rect.x + dst_cut_w, rect.y + dst_cut_h, rect.w - dst_cut_w * 2, rect.h - dst_cut_h * 2), TBRect(cut, cut, bw - cut * 2, bh - cut * 2), element->bitmap);
- }
- #ifdef TB_RUNTIME_DEBUG_INFO
- void TBSkin::Debug()
- {
- m_frag_manager.Debug();
- }
- #endif // TB_RUNTIME_DEBUG_INFO
- void TBSkin::OnContextLost()
- {
- // We could simply do: m_frag_manager.DeleteBitmaps() and then all bitmaps
- // would be recreated automatically when needed. But because it's easy,
- // we unload everything so we save some memory (by not keeping any image
- // data around).
- UnloadBitmaps();
- }
- void TBSkin::OnContextRestored()
- {
- // Reload bitmaps (since we unloaded everything in OnContextLost())
- ReloadBitmaps();
- }
- int TBSkin::GetPxFromNode(TBNode *node, int def_value) const
- {
- return node ? m_dim_conv.GetPxFromValue(&node->GetValue(), def_value) : def_value;
- }
- // == TBSkinElement =========================================================
- TBSkinElement::TBSkinElement()
- : bitmap(nullptr), cut(0), expand(0), type(SKIN_ELEMENT_TYPE_STRETCH_BOX)
- , is_painting(false), is_getting(false)
- , padding_left(0), padding_top(0), padding_right(0), padding_bottom(0)
- , width(SKIN_VALUE_NOT_SPECIFIED), height(SKIN_VALUE_NOT_SPECIFIED)
- , pref_width(SKIN_VALUE_NOT_SPECIFIED), pref_height(SKIN_VALUE_NOT_SPECIFIED)
- , min_width(SKIN_VALUE_NOT_SPECIFIED), min_height(SKIN_VALUE_NOT_SPECIFIED)
- , max_width(SKIN_VALUE_NOT_SPECIFIED), max_height(SKIN_VALUE_NOT_SPECIFIED)
- , spacing(SKIN_VALUE_NOT_SPECIFIED)
- , content_ofs_x(0), content_ofs_y(0)
- , img_ofs_x(0), img_ofs_y(0)
- , img_position_x(50), img_position_y(50)
- , flip_x(0), flip_y(0), opacity(1.f)
- , text_color(0, 0, 0, 0)
- , bg_color(0, 0, 0, 0)
- , bitmap_dpi(0)
- {
- }
- TBSkinElement::~TBSkinElement()
- {
- }
- int TBSkinElement::GetIntrinsicMinWidth() const
- {
- // Sizes below the skin cut size would start to shrink the skin below pretty,
- // so assume that's the default minimum size if it's not specified (minus expansion)
- return cut * 2 - expand * 2;
- }
- int TBSkinElement::GetIntrinsicMinHeight() const
- {
- // Sizes below the skin cut size would start to shrink the skin below pretty,
- // so assume that's the default minimum size if it's not specified (minus expansion)
- return cut * 2 - expand * 2;
- }
- int TBSkinElement::GetIntrinsicWidth() const
- {
- if (width != SKIN_VALUE_NOT_SPECIFIED)
- return width;
- if (bitmap)
- return bitmap->Width() - expand * 2;
- // FIX: We may want to check child elements etc.
- return SKIN_VALUE_NOT_SPECIFIED;
- }
- int TBSkinElement::GetIntrinsicHeight() const
- {
- if (height != SKIN_VALUE_NOT_SPECIFIED)
- return height;
- if (bitmap)
- return bitmap->Height() - expand * 2;
- // FIX: We may want to check child elements etc.
- return SKIN_VALUE_NOT_SPECIFIED;
- }
- void TBSkinElement::SetBitmapDPI(const TBDimensionConverter &dim_conv, int bitmap_dpi)
- {
- if (this->bitmap_dpi)
- {
- // We have already applied the modifications so abort. This may
- // happen when we reload bitmaps without reloading the skin.
- return;
- }
- if (dim_conv.NeedConversion())
- {
- if (bitmap_dpi == dim_conv.GetDstDPI())
- {
- // The bitmap was loaded in a different DPI than the base DPI so
- // we must scale the bitmap properties.
- expand = expand * dim_conv.GetDstDPI() / dim_conv.GetSrcDPI();
- cut = cut * dim_conv.GetDstDPI() / dim_conv.GetSrcDPI();
- }
- else
- {
- // The bitmap was loaded in the base DPI and we need to scale it.
- // Apply the DPI conversion to the skin element scale factor.
- // FIX: For this to work well, we would need to apply scale to both
- // image and all the other types of drawing too.
- // scale_x = scale_x * dim_conv.GetDstDPI() / dim_conv.GetSrcDPI();
- // scale_y = scale_y * dim_conv.GetDstDPI() / dim_conv.GetSrcDPI();
- }
- }
- this->bitmap_dpi = bitmap_dpi;
- }
- bool TBSkinElement::HasState(SKIN_STATE state, TBSkinConditionContext &context)
- {
- return m_override_elements.GetStateElement(state, context, TBSkinElementState::MATCH_RULE_ONLY_SPECIFIC_STATE) ||
- m_child_elements.GetStateElement(state, context, TBSkinElementState::MATCH_RULE_ONLY_SPECIFIC_STATE) ||
- m_overlay_elements.GetStateElement(state, context, TBSkinElementState::MATCH_RULE_ONLY_SPECIFIC_STATE);
- }
- void TBSkinElement::Load(TBNode *n, TBSkin *skin, const char *skin_path)
- {
- if (const char *bitmap = n->GetValueString("bitmap", nullptr))
- {
- bitmap_file.Clear();
- bitmap_file.Append(skin_path);
- bitmap_file.Append(bitmap);
- }
- // Note: Always read cut and expand as pixels. These values might later be
- // recalculated depending on the DPI the bitmaps are available in.
- cut = n->GetValueInt("cut", cut);
- expand = n->GetValueInt("expand", expand);
- name.Set(n->GetName());
- id.Set(n->GetName());
- const TBDimensionConverter *dim_conv = skin->GetDimensionConverter();
- if (TBNode *padding_node = n->GetNode("padding"))
- {
- TBValue &val = padding_node->GetValue();
- if (val.GetArrayLength() == 4)
- {
- padding_top = dim_conv->GetPxFromValue(val.GetArray()->GetValue(0), 0);
- padding_right = dim_conv->GetPxFromValue(val.GetArray()->GetValue(1), 0);
- padding_bottom = dim_conv->GetPxFromValue(val.GetArray()->GetValue(2), 0);
- padding_left = dim_conv->GetPxFromValue(val.GetArray()->GetValue(3), 0);
- }
- else if (val.GetArrayLength() == 2)
- {
- padding_top = padding_bottom = dim_conv->GetPxFromValue(val.GetArray()->GetValue(0), 0);
- padding_left = padding_right = dim_conv->GetPxFromValue(val.GetArray()->GetValue(1), 0);
- }
- else
- {
- padding_top = padding_right = padding_bottom = padding_left = dim_conv->GetPxFromValue(&val, 0);
- }
- }
- width = skin->GetPxFromNode(n->GetNode("width"), width);
- height = skin->GetPxFromNode(n->GetNode("height"), height);
- pref_width = skin->GetPxFromNode(n->GetNode("pref-width"), pref_width);
- pref_height = skin->GetPxFromNode(n->GetNode("pref-height"), pref_height);
- min_width = skin->GetPxFromNode(n->GetNode("min-width"), min_width);
- min_height = skin->GetPxFromNode(n->GetNode("min-height"), min_height);
- max_width = skin->GetPxFromNode(n->GetNode("max-width"), max_width);
- max_height = skin->GetPxFromNode(n->GetNode("max-height"), max_height);
- spacing = skin->GetPxFromNode(n->GetNode("spacing"), spacing);
- content_ofs_x = skin->GetPxFromNode(n->GetNode("content-ofs-x"), content_ofs_x);
- content_ofs_y = skin->GetPxFromNode(n->GetNode("content-ofs-y"), content_ofs_y);
- img_position_x = n->GetValueInt("img-position-x", img_position_x);
- img_position_y = n->GetValueInt("img-position-y", img_position_y);
- img_ofs_x = skin->GetPxFromNode(n->GetNode("img-ofs-x"), img_ofs_x);
- img_ofs_y = skin->GetPxFromNode(n->GetNode("img-ofs-y"), img_ofs_y);
- flip_x = n->GetValueInt("flip-x", flip_x);
- flip_y = n->GetValueInt("flip-y", flip_y);
- opacity = n->GetValueFloat("opacity", opacity);
- if (const char *color = n->GetValueString("text-color", nullptr))
- text_color.SetFromString(color, strlen(color));
- if (const char *color = n->GetValueString("background-color", nullptr))
- bg_color.SetFromString(color, strlen(color));
- if (const char *type_str = n->GetValueString("type", nullptr))
- type = StringToType(type_str);
- // Create all state elements
- m_override_elements.Load(n->GetNode("overrides"));
- m_strong_override_elements.Load(n->GetNode("strong-overrides"));
- m_child_elements.Load(n->GetNode("children"));
- m_overlay_elements.Load(n->GetNode("overlays"));
- }
- // == TBSkinElementState ====================================================
- bool TBSkinElementState::IsMatch(SKIN_STATE state, TBSkinConditionContext &context, MATCH_RULE rule) const
- {
- if (rule == MATCH_RULE_ONLY_SPECIFIC_STATE && this->state == SKIN_STATE_ALL)
- return false;
- if ((state & this->state) || this->state == SKIN_STATE_ALL)
- {
- for (TBSkinCondition *condition = conditions.GetFirst(); condition; condition = condition->GetNext())
- if (!condition->GetCondition(context))
- return false;
- return true;
- }
- return false;
- }
- bool TBSkinElementState::IsExactMatch(SKIN_STATE state, TBSkinConditionContext &context, MATCH_RULE rule) const
- {
- if (rule == MATCH_RULE_ONLY_SPECIFIC_STATE && this->state == SKIN_STATE_ALL)
- return false;
- if (state == this->state || this->state == SKIN_STATE_ALL)
- {
- for (TBSkinCondition *condition = conditions.GetFirst(); condition; condition = condition->GetNext())
- if (!condition->GetCondition(context))
- return false;
- return true;
- }
- return false;
- }
- // == TBSkinElementStateList ==================================================
- TBSkinElementStateList::~TBSkinElementStateList()
- {
- while (TBSkinElementState *state = m_state_elements.GetFirst())
- {
- m_state_elements.Remove(state);
- delete state;
- }
- }
- TBSkinElementState *TBSkinElementStateList::GetStateElement(SKIN_STATE state, TBSkinConditionContext &context, TBSkinElementState::MATCH_RULE rule) const
- {
- // First try to get a state element with a exact match to the current state
- if (TBSkinElementState *element_state = GetStateElementExactMatch(state, context, rule))
- return element_state;
- // No exact state match. Get a state with a partly match if there is one.
- TBSkinElementState *state_element = m_state_elements.GetFirst();
- while (state_element)
- {
- if (state_element->IsMatch(state, context, rule))
- return state_element;
- state_element = state_element->GetNext();
- }
- return nullptr;
- }
- TBSkinElementState *TBSkinElementStateList::GetStateElementExactMatch(SKIN_STATE state, TBSkinConditionContext &context, TBSkinElementState::MATCH_RULE rule) const
- {
- TBSkinElementState *state_element = m_state_elements.GetFirst();
- while (state_element)
- {
- if (state_element->IsExactMatch(state, context, rule))
- return state_element;
- state_element = state_element->GetNext();
- }
- return nullptr;
- }
- void TBSkinElementStateList::Load(TBNode *n)
- {
- if (!n)
- return;
- // For each node, create a new state element.
- TBNode *element_node = n->GetFirstChild();
- while (element_node)
- {
- TBSkinElementState *state = new TBSkinElementState;
- if (!state)
- return;
- // By default, a state element applies to all combinations of states
- state->state = SKIN_STATE_ALL;
- state->element_id.Set(element_node->GetValue().GetString());
- // Loop through all nodes, read state and create all found conditions.
- for (TBNode *condition_node = element_node->GetFirstChild(); condition_node; condition_node = condition_node->GetNext())
- {
- if (strcmp(condition_node->GetName(), "state") == 0)
- state->state = StringToState(condition_node->GetValue().GetString());
- else if (strcmp(condition_node->GetName(), "condition") == 0)
- {
- TBSkinCondition::TARGET target = StringToTarget(condition_node->GetValueString("target", ""));
- const char *prop_str = condition_node->GetValueString("property", "");
- TBSkinCondition::PROPERTY prop = StringToProperty(prop_str);
- TBID custom_prop;
- if (prop == TBSkinCondition::PROPERTY_CUSTOM)
- custom_prop.Set(prop_str);
- TBID value;
- if (TBNode *value_n = condition_node->GetNode("value"))
- {
- // Set the it to number or string. If it's a state, we must first convert the
- // state string to the SKIN_STATE state combo.
- if (prop == TBSkinCondition::PROPERTY_STATE)
- value.Set(StringToState(value_n->GetValue().GetString()));
- else if (value_n->GetValue().IsString())
- value.Set(value_n->GetValue().GetString());
- else
- value.Set(value_n->GetValue().GetInt());
- }
- TBSkinCondition::TEST test = TBSkinCondition::TEST_EQUAL;
- if (const char *test_str = condition_node->GetValueString("test", nullptr))
- {
- if (strcmp(test_str, "!=") == 0)
- test = TBSkinCondition::TEST_NOT_EQUAL;
- }
- if (TBSkinCondition *condition = new TBSkinCondition(target, prop, custom_prop, value, test))
- state->conditions.AddLast(condition);
- }
- }
- // State is reado to add
- m_state_elements.AddLast(state);
- element_node = element_node->GetNext();
- }
- }
- }; // namespace tb
|