/* * This source file is part of RmlUi, the HTML/CSS Interface Middleware * * For the latest information, see http://github.com/mikke89/RmlUi * * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd * Copyright (c) 2019 The RmlUi Team, and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "../../Include/RmlUi/Core/StyleSheetContainer.h" #include "../../Include/RmlUi/Core/Context.h" #include "../../Include/RmlUi/Core/PropertyDictionary.h" #include "../../Include/RmlUi/Core/Profiling.h" #include "../../Include/RmlUi/Core/StyleSheet.h" #include "../../Include/RmlUi/Core/Utilities.h" #include "ComputeProperty.h" #include "StyleSheetParser.h" namespace Rml { StyleSheetContainer::StyleSheetContainer() { } StyleSheetContainer::~StyleSheetContainer() { } bool StyleSheetContainer::LoadStyleSheetContainer(Stream* stream, int begin_line_number) { StyleSheetParser parser; bool result = parser.Parse(media_blocks, stream, begin_line_number); return result; } bool StyleSheetContainer::UpdateCompiledStyleSheet(const Context* context) { RMLUI_ZoneScoped; const float dp_ratio = context->GetDensityIndependentPixelRatio(); const Vector2i vp_dimensions_i(context->GetDimensions()); const Vector2f vp_dimensions(vp_dimensions_i); Vector new_active_media_block_indices; const float font_size = DefaultComputedValues.font_size; for (int media_block_index = 0; media_block_index < (int)media_blocks.size(); media_block_index++) { const MediaBlock& media_block = media_blocks[media_block_index]; bool all_match = true; for (const auto& property : media_block.properties.GetProperties()) { const MediaQueryId id = static_cast(property.first); Vector2i ratio; switch (id) { case MediaQueryId::Width: if (vp_dimensions.x != ComputeLength(&property.second, font_size, font_size, dp_ratio, vp_dimensions)) all_match = false; break; case MediaQueryId::MinWidth: if (vp_dimensions.x < ComputeLength(&property.second, font_size, font_size, dp_ratio, vp_dimensions)) all_match = false; break; case MediaQueryId::MaxWidth: if (vp_dimensions.x > ComputeLength(&property.second, font_size, font_size, dp_ratio, vp_dimensions)) all_match = false; break; case MediaQueryId::Height: if (vp_dimensions.y != ComputeLength(&property.second, font_size, font_size, dp_ratio, vp_dimensions)) all_match = false; break; case MediaQueryId::MinHeight: if (vp_dimensions.y < ComputeLength(&property.second, font_size, font_size, dp_ratio, vp_dimensions)) all_match = false; break; case MediaQueryId::MaxHeight: if (vp_dimensions.y > ComputeLength(&property.second, font_size, font_size, dp_ratio, vp_dimensions)) all_match = false; break; case MediaQueryId::AspectRatio: ratio = Vector2i(property.second.Get()); if (vp_dimensions_i.x * ratio.y != vp_dimensions_i.y * ratio.x) all_match = false; break; case MediaQueryId::MinAspectRatio: ratio = Vector2i(property.second.Get()); if (vp_dimensions_i.x * ratio.y < vp_dimensions_i.y * ratio.x) all_match = false; break; case MediaQueryId::MaxAspectRatio: ratio = Vector2i(property.second.Get()); if (vp_dimensions_i.x * ratio.y > vp_dimensions_i.y * ratio.x) all_match = false; break; case MediaQueryId::Resolution: if (dp_ratio != property.second.Get()) all_match = false; break; case MediaQueryId::MinResolution: if (dp_ratio < property.second.Get()) all_match = false; break; case MediaQueryId::MaxResolution: if (dp_ratio > property.second.Get()) all_match = false; break; case MediaQueryId::Orientation: // Landscape (x > y) = 0 // Portrait (x <= y) = 1 if ((vp_dimensions.x <= vp_dimensions.y) != property.second.Get()) all_match = false; break; case MediaQueryId::Theme: if (!context->IsThemeActive(property.second.Get())) all_match = false; break; // Invalid properties case MediaQueryId::Invalid: case MediaQueryId::NumDefinedIds: break; } if (!all_match) break; } if (all_match) new_active_media_block_indices.push_back(media_block_index); } const bool style_sheet_changed = (new_active_media_block_indices != active_media_block_indices || !compiled_style_sheet); if (style_sheet_changed) { StyleSheet* first_sheet = nullptr; UniquePtr new_sheet; for (int index : new_active_media_block_indices) { MediaBlock& media_block = media_blocks[index]; if (!first_sheet) first_sheet = media_block.stylesheet.get(); else if (!new_sheet) new_sheet = first_sheet->CombineStyleSheet(*media_block.stylesheet); else new_sheet->MergeStyleSheet(*media_block.stylesheet); } if (!first_sheet) { new_sheet.reset(new StyleSheet); first_sheet = new_sheet.get(); } compiled_style_sheet = (new_sheet ? new_sheet.get() : first_sheet); combined_compiled_style_sheet = std::move(new_sheet); compiled_style_sheet->BuildNodeIndex(); } active_media_block_indices = std::move(new_active_media_block_indices); return style_sheet_changed; } StyleSheet* StyleSheetContainer::GetCompiledStyleSheet() { return compiled_style_sheet; } SharedPtr StyleSheetContainer::CombineStyleSheetContainer(const StyleSheetContainer& container) const { RMLUI_ZoneScoped; SharedPtr new_sheet = MakeShared(); for (const MediaBlock& media_block : media_blocks) { new_sheet->media_blocks.emplace_back(media_block.properties, media_block.stylesheet); } new_sheet->MergeStyleSheetContainer(container); return new_sheet; } void StyleSheetContainer::MergeStyleSheetContainer(const StyleSheetContainer& other) { RMLUI_ZoneScoped; // Style sheet container must not be merged after it's been compiled. This will invalidate references to the compiled style sheet. RMLUI_ASSERT(!compiled_style_sheet); auto it_other_begin = other.media_blocks.begin(); #if 0 // If the last block here has the same media requirements as the first block in other, we can safely merge them // while retaining correct specificity of all properties. This is essentially an optimization to avoid more // style sheet merging later on. if (!media_blocks.empty() && !other.media_blocks.empty()) { MediaBlock& block_local = media_blocks.back(); const MediaBlock& block_other = other.media_blocks.front(); if (block_local.properties.GetProperties() == block_other.properties.GetProperties()) { // Now we can safely merge the two style sheets. block_local.stylesheet = block_local.stylesheet->CombineStyleSheet(*block_other.stylesheet); // And we need to skip the first media block in the 'other' style sheet, since we merged it just now. ++it_other_begin; } } #endif // Add all the other blocks into ours. for (auto it = it_other_begin; it != other.media_blocks.end(); ++it) { const MediaBlock& block_other = *it; media_blocks.emplace_back(block_other.properties, block_other.stylesheet); } } } // namespace Rml