mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-07-14 15:02:50 +03:00
Load CSS default HTML element styles from a CSS file with no specificity. This simplyfies how to setup the HTML defaults. Also set new defaults matching closer HTML spec.
This commit is contained in:
@@ -220,7 +220,7 @@
|
||||
"working_dir": "${project_root}/bin"
|
||||
},
|
||||
{
|
||||
"args": "-filter=\"UIRichText.InvalidWidthLengthComputation3\"",
|
||||
"args": "",
|
||||
"command": "${project_root}/bin/unit_tests/eepp-unit_tests-debug",
|
||||
"name": "eepp-unit_tests-debug",
|
||||
"working_dir": "${project_root}/bin/unit_tests/"
|
||||
|
||||
@@ -52,91 +52,13 @@
|
||||
droppable-hovering-color: #FFFFFF20;
|
||||
}
|
||||
|
||||
MarkdownView > *,
|
||||
body {
|
||||
color: var(--font);
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-style: bold;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
u,
|
||||
ins {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
s,
|
||||
del {
|
||||
text-decoration: strikethrough;
|
||||
}
|
||||
|
||||
i,
|
||||
em,
|
||||
cite {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
CodeEditor,
|
||||
code,
|
||||
kbd {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.83em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 0.67em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: arrow;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
cursor: hand;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding-left: 8dp;
|
||||
MarkdownView {
|
||||
background-color: var(--list-back);
|
||||
border-left: 2dp solid var(--tab-line);
|
||||
padding: 4dp;
|
||||
}
|
||||
|
||||
MarkdownView body {
|
||||
color: var(--font);
|
||||
}
|
||||
|
||||
MarkdownView p,
|
||||
@@ -171,11 +93,6 @@ MarkdownView a:hover {
|
||||
color: var(--font-highlight);
|
||||
}
|
||||
|
||||
MarkdownView {
|
||||
background-color: var(--list-back);
|
||||
padding: 4dp;
|
||||
}
|
||||
|
||||
MarkdownView li > p { margin: 0; }
|
||||
MarkdownView blockquote > *:first-child { margin-top: 0dp; }
|
||||
MarkdownView blockquote > *:last-child { margin-bottom: 0dp; }
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
146
bin/unit_tests/assets/html/element_defaults.html
Normal file
146
bin/unit_tests/assets/html/element_defaults.html
Normal file
@@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>HTML Element Defaults</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; padding: 20px; }
|
||||
h2 { border-bottom: 1px solid #ccc; margin-top: 32px; }
|
||||
.group { margin-bottom: 24px; }
|
||||
.row { margin: 4px 0; }
|
||||
.label { display: inline-block; min-width: 130px; color: #555; font-size: 12px; }
|
||||
table { border-collapse: collapse; }
|
||||
td, th { border: 1px solid #ccc; padding: 4px 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>HTML Element Default Styles</h1>
|
||||
<p>This page exercises all elements whose defaults were moved to widget-creation code.</p>
|
||||
|
||||
<h2>Headings</h2>
|
||||
<div class="group">
|
||||
<h1>Heading 1 — The quick brown fox</h1>
|
||||
<h2>Heading 2 — The quick brown fox</h2>
|
||||
<h3>Heading 3 — The quick brown fox</h3>
|
||||
<h4>Heading 4 — The quick brown fox</h4>
|
||||
<h5>Heading 5 — The quick brown fox</h5>
|
||||
<h6>Heading 6 — The quick brown fox</h6>
|
||||
</div>
|
||||
|
||||
<h2>Inline Text Semantics</h2>
|
||||
<div class="group">
|
||||
<div class="row"><span class="label">b / strong</span> <b>bold text</b> <strong>strong text</strong></div>
|
||||
<div class="row"><span class="label">i / em / cite</span> <i>italic text</i> <em>emphasized</em> <cite>citation</cite></div>
|
||||
<div class="row"><span class="label">u / ins</span> <u>underlined</u> <ins>inserted</ins></div>
|
||||
<div class="row"><span class="label">s / del</span> <s>strikethrough</s> <del>deleted</del></div>
|
||||
<div class="row"><span class="label">small</span> <small>small text</small></div>
|
||||
<div class="row"><span class="label">code / kbd</span> <code>function()</code> <kbd>Ctrl+C</kbd></div>
|
||||
<div class="row"><span class="label">sub / sup</span> H<sub>2</sub>O and E=mc<sup>2</sup></div>
|
||||
<div class="row"><span class="label">a (link)</span> <a href="#">clickable link</a></div>
|
||||
<div class="row"><span class="label">a (visited)</span> <a href="">visited link</a></div>
|
||||
<div class="row"><span class="label">a (hover)</span> <a href="#" style="text-decoration:underline;cursor:hand;">hovered link</a></div>
|
||||
<div class="row"><span class="label">mark</span> <mark>highlighted text</mark></div>
|
||||
</div>
|
||||
|
||||
<h2>Block Elements</h2>
|
||||
<div class="group">
|
||||
<p>A paragraph of text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||
<p>Another paragraph with a <br>line break in the middle.</p>
|
||||
<hr>
|
||||
<pre>preformatted text
|
||||
with spaces
|
||||
and newlines</pre>
|
||||
<blockquote>
|
||||
<p>This is a blockquote. It should have left border, left padding, and background.</p>
|
||||
<p>Multiple paragraphs in a blockquote.</p>
|
||||
</blockquote>
|
||||
<div>A div container with no special styling</div>
|
||||
</div>
|
||||
|
||||
<h2>Lists</h2>
|
||||
<div class="group">
|
||||
<ul>
|
||||
<li>Unordered list item 1</li>
|
||||
<li>Unordered list item 2
|
||||
<ul>
|
||||
<li>Nested item A</li>
|
||||
<li>Nested item B</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Unordered list item 3</li>
|
||||
</ul>
|
||||
<ol>
|
||||
<li>Ordered list item 1</li>
|
||||
<li>Ordered list item 2
|
||||
<ol>
|
||||
<li>Nested numbered item A</li>
|
||||
<li>Nested numbered item B</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>Ordered list item 3</li>
|
||||
</ol>
|
||||
</div>
|
||||
<h2>Form Controls — Text Inputs</h2>
|
||||
|
||||
<div class="group">
|
||||
<div class="row"><span class="label">text</span> <input type="text" value="default text input" style="width:200px"></div>
|
||||
<div class="row"><span class="label">text (empty)</span> <input type="text" placeholder="placeholder text" style="width:200px"></div>
|
||||
<div class="row"><span class="label">password</span> <input type="password" value="secret" style="width:200px"></div>
|
||||
<div class="row"><span class="label">number</span> <input type="number" value="42" style="width:100px"></div>
|
||||
<div class="row"><span class="label">textarea</span> <textarea rows="4" cols="40">Textarea content</textarea></div>
|
||||
</div>
|
||||
|
||||
<h2>Form Controls — Buttons</h2>
|
||||
<div class="group">
|
||||
<div class="row"><span class="label">submit</span> <input type="submit" value="Submit"></div>
|
||||
<div class="row"><span class="label">button</span> <input type="button" value="Button"></div>
|
||||
<div class="row"><span class="label">reset</span> <input type="reset" value="Reset"></div>
|
||||
</div>
|
||||
|
||||
<h2>Form Controls — Checkboxes</h2>
|
||||
<div class="group">
|
||||
<div class="row"><span class="label">checkbox</span> <input type="checkbox" checked> Option A <input type="checkbox"> Option B</div>
|
||||
<div class="row"><span class="label">radio</span> <input type="radio" name="r" checked> Choice 1 <input type="radio" name="r"> Choice 2</div>
|
||||
</div>
|
||||
|
||||
<h2>Tables</h2>
|
||||
<div class="group">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Header A</th><th>Header B</th><th>Header C</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Cell 1</td><td>Cell 2</td><td>Cell 3</td></tr>
|
||||
<tr><td>Cell 4</td><td>Cell 5</td><td>Cell 6</td></tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr><td>Footer 1</td><td>Footer 2</td><td>Footer 3</td></tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2>Details / Summary</h2>
|
||||
<div class="group">
|
||||
<details>
|
||||
<summary>Click to expand details</summary>
|
||||
<p>Hidden content revealed when summary is clicked.</p>
|
||||
</details>
|
||||
<details open>
|
||||
<summary>Already open details</summary>
|
||||
<p>This content is visible by default.</p>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<h2>Misc</h2>
|
||||
<div class="group">
|
||||
<div class="row"><span class="label">image</span> <img src="triangle.svg" width="32" height="32" alt="triangle"></div>
|
||||
<div class="row"><span class="label">fieldset</span>
|
||||
<fieldset>
|
||||
<legend>Legend</legend>
|
||||
Fieldset content
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="row"><span class="label">hidden input</span> <input type="hidden" value="should be invisible"> (should not be visible)</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<head>
|
||||
<title>Lobsters</title>
|
||||
<style>
|
||||
:root {
|
||||
@@ -156,3 +157,6 @@
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -60,6 +60,8 @@ class EE_API StyleSheet {
|
||||
|
||||
void removeAllWithoutMarker( const Uint32& marker );
|
||||
|
||||
void setSelectorSpecificity( const Uint32& specificity );
|
||||
|
||||
bool markerExists( const Uint32& marker ) const;
|
||||
|
||||
StyleSheet getAllWithMarker( const Uint32& marker ) const;
|
||||
|
||||
@@ -19,6 +19,8 @@ class EE_API StyleSheetSelector {
|
||||
|
||||
const Uint32& getSpecificity() const;
|
||||
|
||||
void setSpecificity( const Uint32& specificity );
|
||||
|
||||
bool select( UIWidget* element, const bool& applyPseudo = true ) const;
|
||||
|
||||
bool isCacheable() const;
|
||||
|
||||
@@ -67,6 +67,8 @@ class EE_API StyleSheetStyle {
|
||||
|
||||
void setMarker( const Uint32& marker );
|
||||
|
||||
void setSelectorSpecificity( const Uint32& specificity );
|
||||
|
||||
bool updatePropertyValue( const std::string& name, const std::string& value );
|
||||
|
||||
protected:
|
||||
|
||||
@@ -742,6 +742,8 @@ class EE_API UISceneNode : public SceneNode {
|
||||
|
||||
Uint32 getCurrentMarker() const { return mCurrentMarker; }
|
||||
|
||||
void loadHTMLBaseCSS();
|
||||
|
||||
protected:
|
||||
friend class EE::UI::UIWindow;
|
||||
friend class EE::UI::UIWidget;
|
||||
|
||||
@@ -1375,6 +1375,10 @@ class EE_API UIWidget : public UINode {
|
||||
/* @return The size of the widget when size policy is match_parent */
|
||||
Sizef getSizeFromLayoutPolicy();
|
||||
|
||||
void setStyleSheetProperties( const CSS::StyleSheetProperties& properties );
|
||||
|
||||
void setStyleSheetProperty( const CSS::StyleSheetProperty& property );
|
||||
|
||||
protected:
|
||||
friend class UIManager;
|
||||
friend class UISceneNode;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <eepp/core.hpp>
|
||||
#include <eepp/ui/uiwidget.hpp>
|
||||
#include <eepp/ui/css/stylesheet.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
@@ -33,6 +34,8 @@ class EE_API UIWidgetCreator {
|
||||
|
||||
static std::vector<std::string> getWidgetNames();
|
||||
|
||||
static void loadHTMLBaseDefaults( CSS::StyleSheet& styleSheet, Uint32 marker );
|
||||
|
||||
protected:
|
||||
static RegisteredWidgetCallbackMap registeredWidget;
|
||||
|
||||
|
||||
@@ -95,6 +95,12 @@ void StyleSheet::removeAllWithoutMarker( const Uint32& marker ) {
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
void StyleSheet::setSelectorSpecificity( const Uint32& specificity ) {
|
||||
for ( auto& node : mNodes )
|
||||
node->setSelectorSpecificity( specificity );
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
StyleSheet StyleSheet::getAllWithMarker( const Uint32& marker ) const {
|
||||
StyleSheet style;
|
||||
std::vector<std::shared_ptr<StyleSheetStyle>> hits;
|
||||
|
||||
@@ -20,6 +20,10 @@ const Uint32& StyleSheetSelector::getSpecificity() const {
|
||||
return mSpecificity;
|
||||
}
|
||||
|
||||
void StyleSheetSelector::setSpecificity( const Uint32& specificity ) {
|
||||
mSpecificity = specificity;
|
||||
}
|
||||
|
||||
void removeExtraSpaces( std::string& string ) {
|
||||
// TODO: Optimize this
|
||||
String::trimInPlace( string );
|
||||
|
||||
@@ -340,7 +340,7 @@ void StyleSheetSpecification::registerDefaultProperties() {
|
||||
|
||||
registerProperty( "white-space-collapse", "collapse", true ).setType( PropertyType::String );
|
||||
|
||||
registerProperty( "hint", "" ).setType( PropertyType::String );
|
||||
registerProperty( "hint", "" ).setType( PropertyType::String ).addAlias( "placeholder" );
|
||||
registerProperty( "hint-color", "" ).setType( PropertyType::Color );
|
||||
registerProperty( "hint-shadow-color", "" ).setType( PropertyType::Color );
|
||||
registerProperty( "hint-shadow-offset", "" ).setType( PropertyType::Vector2 );
|
||||
|
||||
@@ -23,6 +23,14 @@ StyleSheetStyle::StyleSheetStyle( const std::string& selector,
|
||||
}
|
||||
}
|
||||
|
||||
void StyleSheetStyle::setSelectorSpecificity( const Uint32& specificity ) {
|
||||
const_cast<StyleSheetSelector&>( mSelector ).setSpecificity( specificity );
|
||||
for ( auto& it : mProperties )
|
||||
it.second.setSpecificity( specificity );
|
||||
for ( auto& it : mVariables )
|
||||
it.second.setSpecificity( specificity );
|
||||
}
|
||||
|
||||
std::string StyleSheetStyle::build( bool emitMediaQueryStart, bool emitMediaQueryEnd ) {
|
||||
std::string css;
|
||||
|
||||
|
||||
@@ -524,10 +524,18 @@ void TableLayouter::updateLayout() {
|
||||
}
|
||||
row->setPixelsSize( containerWidth - paddingH, rowHeight );
|
||||
|
||||
if ( r == 0 && mCells[start]->getParent()->isType( UI_TYPE_HTML_TABLE_HEAD ) ) {
|
||||
if ( r == 0 && mCells[start]->getParent() &&
|
||||
( mCells[start]->getParent()->isType( UI_TYPE_HTML_TABLE_HEAD ) ||
|
||||
( mCells[start]->getParent()->isType( UI_TYPE_HTML_TABLE_ROW ) &&
|
||||
mCells[start]->getParent()->getParent() &&
|
||||
mCells[start]->getParent()->getParent()->isType( UI_TYPE_HTML_TABLE_HEAD ) ) ) ) {
|
||||
headHeight = rowHeight;
|
||||
} else if ( r == rowCount - 1 && columnCount &&
|
||||
mCells[start]->getParent()->isType( UI_TYPE_HTML_TABLE_FOOTER ) ) {
|
||||
( mCells[start]->getParent()->isType( UI_TYPE_HTML_TABLE_FOOTER ) ||
|
||||
( mCells[start]->getParent()->isType( UI_TYPE_HTML_TABLE_ROW ) &&
|
||||
mCells[start]->getParent()->getParent() &&
|
||||
mCells[start]->getParent()->getParent()->isType(
|
||||
UI_TYPE_HTML_TABLE_FOOTER ) ) ) ) {
|
||||
footerHeight = rowHeight;
|
||||
} else {
|
||||
bodyHeight += rowHeight;
|
||||
|
||||
@@ -3060,6 +3060,7 @@ bool UICodeEditor::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
setLineWrapType( LineWrap::toLineWrapType( attribute.asString() ) );
|
||||
break;
|
||||
case PropertyId::Text:
|
||||
case PropertyId::Value:
|
||||
mDoc->textInput( attribute.asString() );
|
||||
break;
|
||||
case PropertyId::BackgroundColor: {
|
||||
|
||||
@@ -202,12 +202,9 @@ UIHTMLSummary* UIHTMLSummary::New() {
|
||||
UIHTMLSummary::UIHTMLSummary() : UIRichText( "summary" ) {
|
||||
mDisplay = CSSDisplay::ListItem;
|
||||
setFlags( UI_CREATING_NODE );
|
||||
applyProperty( StyleSheetProperty( "cursor", "pointer" ) );
|
||||
applyProperty( StyleSheetProperty( "padding-left", "20dp" ) );
|
||||
applyProperty( StyleSheetProperty( "list-style-type", "disclosure-closed" ) );
|
||||
getUIStyle()->setStyleSheetProperty( StyleSheetProperty( "cursor", "pointer" ) );
|
||||
getUIStyle()->setStyleSheetProperty(
|
||||
StyleSheetProperty( "list-style-type", "disclosure-closed" ) );
|
||||
setStyleSheetProperty( StyleSheetProperty( "cursor", "pointer" ) );
|
||||
setStyleSheetProperty( StyleSheetProperty( "padding-left", "20dp" ) );
|
||||
setStyleSheetProperty( StyleSheetProperty( "list-style-type", "disclosure-closed" ) );
|
||||
unsetFlags( UI_CREATING_NODE );
|
||||
setFlags( UI_TAB_FOCUSABLE );
|
||||
}
|
||||
@@ -278,7 +275,8 @@ bool UIHTMLSummary::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
setListStyleType( CSSListStyleTypeHelper::fromString( attribute.value() ) );
|
||||
return true;
|
||||
case PropertyId::PaddingLeft:
|
||||
if ( !isCreatingNode() && !mApplyingDefaultListStylePadding )
|
||||
if ( !isCreatingNode() && !mApplyingDefaultListStylePadding &&
|
||||
attribute.getSpecificity() != 0 )
|
||||
mUsingDefaultListStylePadding = false;
|
||||
break;
|
||||
default:
|
||||
@@ -348,7 +346,7 @@ void UIHTMLSummary::syncDefaultListStylePadding() {
|
||||
return;
|
||||
|
||||
mApplyingDefaultListStylePadding = true;
|
||||
UIRichText::applyProperty( StyleSheetProperty(
|
||||
setStyleSheetProperty( StyleSheetProperty(
|
||||
"padding-left", mListStyleType == CSSListStyleType::None ? "0dp" : "20dp" ) );
|
||||
mApplyingDefaultListStylePadding = false;
|
||||
}
|
||||
|
||||
@@ -119,12 +119,15 @@ void UIHTMLInput::createChildWidget() {
|
||||
mChildWidget = nullptr;
|
||||
}
|
||||
|
||||
if ( mInputType == "button" || mInputType == "submit" ) {
|
||||
if ( mInputType == "button" || mInputType == "submit" || mInputType == "reset" ) {
|
||||
mChildWidget = UIPushButton::New();
|
||||
} else if ( mInputType == "checkbox" ) {
|
||||
mChildWidget = UICheckBox::New();
|
||||
} else if ( mInputType == "hidden" ) {
|
||||
// We don't need it
|
||||
setVisible( false );
|
||||
setEnabled( false );
|
||||
mVisible = false;
|
||||
mDisplay = CSSDisplay::None;
|
||||
} else if ( mInputType == "number" ) {
|
||||
mChildWidget = UISpinBox::New();
|
||||
} else if ( mInputType == "password" ) {
|
||||
@@ -148,6 +151,7 @@ void UIHTMLInput::createChildWidget() {
|
||||
if ( mChildWidget )
|
||||
setPixelsSize( mChildWidget->getPixelsSize() );
|
||||
} );
|
||||
|
||||
for ( const auto& propIt : mProperties ) {
|
||||
mChildWidget->applyProperty( propIt.second );
|
||||
}
|
||||
|
||||
@@ -23,42 +23,6 @@
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
static void applyDefaultBlockMargins( UIWidget* widget, const std::string& tag ) {
|
||||
static const UnorderedMap<std::string, std::pair<Float, Float>> defaultsTopBottom = {
|
||||
{ "h1", { 0.67f, 0.67f } }, { "h2", { 0.83f, 0.83f } },
|
||||
{ "h3", { 1.00f, 1.00f } }, { "h4", { 1.33f, 1.33f } },
|
||||
{ "h5", { 1.67f, 1.67f } }, { "h6", { 2.33f, 2.33f } },
|
||||
{ "p", { 1.00f, 1.00f } }, { "pre", { 1.00f, 1.00f } },
|
||||
{ "blockquote", { 1.00f, 1.00f } }, { "hr", { 0.50f, 0.50f } },
|
||||
{ "ul", { 1.00f, 1.00f } }, { "ol", { 1.00f, 1.00f } },
|
||||
{ "dl", { 1.00f, 1.00f } }, { "body", { 0.67f, 0.67f } },
|
||||
};
|
||||
|
||||
static const UnorderedMap<std::string, std::pair<Float, Float>> defaultsLeftRight = {
|
||||
{ "body", { 0.67f, 0.67f } },
|
||||
};
|
||||
|
||||
widget->setFlags( UI_CREATING_NODE );
|
||||
|
||||
auto ittb = defaultsTopBottom.find( tag );
|
||||
if ( ittb != defaultsTopBottom.end() ) {
|
||||
widget->applyProperty(
|
||||
StyleSheetProperty( "margin-top", String::format( "%gem", ittb->second.first ) ) );
|
||||
widget->applyProperty(
|
||||
StyleSheetProperty( "margin-bottom", String::format( "%gem", ittb->second.second ) ) );
|
||||
}
|
||||
|
||||
auto itlr = defaultsLeftRight.find( tag );
|
||||
if ( itlr != defaultsLeftRight.end() ) {
|
||||
widget->applyProperty(
|
||||
StyleSheetProperty( "margin-left", String::format( "%gem", itlr->second.first ) ) );
|
||||
widget->applyProperty(
|
||||
StyleSheetProperty( "margin-right", String::format( "%gem", itlr->second.second ) ) );
|
||||
}
|
||||
|
||||
widget->unsetFlags( UI_CREATING_NODE );
|
||||
}
|
||||
|
||||
UIRichText::WhiteSpaceCollapse UIRichText::toWhiteSpaceCollapse( std::string val ) {
|
||||
String::toLowerInPlace( val );
|
||||
if ( "preserve" == val )
|
||||
@@ -94,6 +58,7 @@ UIHTMLHtml* UIHTMLHtml::New( const std::string& tag ) {
|
||||
|
||||
UIHTMLHtml::UIHTMLHtml( const std::string& tag ) : UIRichText( tag ) {
|
||||
enableReportSizeChangeToChildren();
|
||||
getUISceneNode()->loadHTMLBaseCSS();
|
||||
}
|
||||
|
||||
Uint32 UIHTMLHtml::getType() const {
|
||||
@@ -139,6 +104,7 @@ UIHTMLBody* UIHTMLBody::New( const std::string& tag ) {
|
||||
|
||||
UIHTMLBody::UIHTMLBody( const std::string& tag ) : UIRichText( tag ) {
|
||||
enableReportSizeChangeToChildren();
|
||||
getUISceneNode()->loadHTMLBaseCSS();
|
||||
}
|
||||
|
||||
Uint32 UIHTMLBody::getType() const {
|
||||
@@ -241,7 +207,6 @@ UIRichText* UIRichText::NewHtml() {
|
||||
UIRichText* UIRichText::NewBody() {
|
||||
auto* body = UIHTMLBody::New( "body" );
|
||||
body->setClipType( ClipType::None );
|
||||
applyDefaultBlockMargins( body, body->getElementTag() );
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -251,11 +216,7 @@ UIRichText* UIRichText::NewBr() {
|
||||
|
||||
UIRichText* UIRichText::NewHr() {
|
||||
auto* w = UILineBreak::New( "hr" );
|
||||
w->setFlags( UI_CREATING_NODE );
|
||||
applyDefaultBlockMargins( w, "hr" );
|
||||
w->setBackgroundColor( Color::Gray );
|
||||
w->mMinHeightEq = "1dp";
|
||||
w->unsetFlags( UI_CREATING_NODE );
|
||||
return w;
|
||||
};
|
||||
|
||||
@@ -264,9 +225,7 @@ UIRichText* UIRichText::New() {
|
||||
}
|
||||
|
||||
UIRichText* UIRichText::NewWithTag( const std::string& tag ) {
|
||||
auto* w = eeNew( UIRichText, ( tag ) );
|
||||
applyDefaultBlockMargins( w, tag );
|
||||
return w;
|
||||
return eeNew( UIRichText, ( tag ) );
|
||||
}
|
||||
|
||||
UIRichText::UIRichText( const std::string& tag ) : UIHTMLWidget( tag ) {
|
||||
@@ -289,6 +248,8 @@ UIRichText::UIRichText( const std::string& tag ) : UIHTMLWidget( tag ) {
|
||||
sceneNode->getUIThemeManager()->getDefaultFontSize();
|
||||
}
|
||||
|
||||
mRichText.getFontStyleConfig().FontColor = Color::Black;
|
||||
|
||||
setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent );
|
||||
}
|
||||
|
||||
|
||||
@@ -1624,4 +1624,9 @@ void UISceneNode::loadFontStyleVariants( Font* font, const std::string& family )
|
||||
ft->setBoldItalicFont( boldItalicFont );
|
||||
}
|
||||
|
||||
void UISceneNode::loadHTMLBaseCSS() {
|
||||
// Load HTML base defaults (idempotent - marker check prevents duplicates)
|
||||
UIWidgetCreator::loadHTMLBaseDefaults( mStyleSheet, String::hash( "html_defaults" ) );
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -96,6 +96,7 @@ bool UITextEdit::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
|
||||
switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
|
||||
case PropertyId::Text:
|
||||
case PropertyId::Value:
|
||||
setText( attribute.value() );
|
||||
break;
|
||||
case PropertyId::Wordwrap:
|
||||
|
||||
@@ -617,6 +617,7 @@ bool UITextInput::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
|
||||
switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
|
||||
case PropertyId::Text:
|
||||
case PropertyId::Value:
|
||||
setText( getTranslatorString( attribute.value() ) );
|
||||
break;
|
||||
case PropertyId::AllowEditing:
|
||||
|
||||
@@ -223,7 +223,7 @@ Font* UITextSpan::getFont() const {
|
||||
}
|
||||
|
||||
UITextSpan* UITextSpan::setFont( Font* font ) {
|
||||
if ( mRichText.getFontStyleConfig().Font != font ) {
|
||||
if ( mRichText.getFontStyleConfig().Font != font || !( mStyleState & StyleStateFont ) ) {
|
||||
mRichText.getFontStyleConfig().Font = font;
|
||||
mStyleState |= StyleStateFont;
|
||||
mRichText.invalidate();
|
||||
@@ -238,7 +238,8 @@ Uint32 UITextSpan::getFontSize() const {
|
||||
}
|
||||
|
||||
UITextSpan* UITextSpan::setFontSize( const Uint32& characterSize ) {
|
||||
if ( mRichText.getFontStyleConfig().CharacterSize != characterSize ) {
|
||||
if ( mRichText.getFontStyleConfig().CharacterSize != characterSize ||
|
||||
!( mStyleState & StyleStateFontSize ) ) {
|
||||
if ( characterSize == 0 )
|
||||
return this;
|
||||
mRichText.getFontStyleConfig().CharacterSize = characterSize;
|
||||
@@ -295,7 +296,8 @@ const Float& UITextSpan::getOutlineThickness() const {
|
||||
}
|
||||
|
||||
UITextSpan* UITextSpan::setOutlineThickness( const Float& outlineThickness ) {
|
||||
if ( mRichText.getFontStyleConfig().OutlineThickness != outlineThickness ) {
|
||||
if ( mRichText.getFontStyleConfig().OutlineThickness != outlineThickness ||
|
||||
!( mStyleState & StyleStateOutlineThickness ) ) {
|
||||
mRichText.getFontStyleConfig().OutlineThickness = outlineThickness;
|
||||
mStyleState |= StyleStateOutlineThickness;
|
||||
mRichText.invalidate();
|
||||
@@ -311,7 +313,8 @@ const Color& UITextSpan::getOutlineColor() const {
|
||||
}
|
||||
|
||||
UITextSpan* UITextSpan::setOutlineColor( const Color& outlineColor ) {
|
||||
if ( mRichText.getFontStyleConfig().OutlineColor != outlineColor ) {
|
||||
if ( mRichText.getFontStyleConfig().OutlineColor != outlineColor ||
|
||||
!( mStyleState & StyleStateOutlineColor ) ) {
|
||||
mRichText.getFontStyleConfig().OutlineColor = outlineColor;
|
||||
mStyleState |= StyleStateOutlineColor;
|
||||
mRichText.invalidate();
|
||||
@@ -326,7 +329,8 @@ const Color& UITextSpan::getFontColor() const {
|
||||
}
|
||||
|
||||
UITextSpan* UITextSpan::setFontColor( const Color& color ) {
|
||||
if ( mRichText.getFontStyleConfig().FontColor != color ) {
|
||||
if ( mRichText.getFontStyleConfig().FontColor != color ||
|
||||
!( mStyleState & StyleStateFontColor ) ) {
|
||||
mRichText.getFontStyleConfig().FontColor = color;
|
||||
mStyleState |= StyleStateFontColor;
|
||||
mRichText.invalidate();
|
||||
@@ -664,6 +668,9 @@ bool UIAnchorSpan::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
case PropertyId::Href:
|
||||
setHref( attribute.asString() );
|
||||
break;
|
||||
case PropertyId::Color:
|
||||
setFontColor( attribute.asColor() );
|
||||
break;
|
||||
default:
|
||||
UITextSpan::applyProperty( attribute );
|
||||
break;
|
||||
|
||||
@@ -21,6 +21,8 @@ UIWebView::UIWebView() : UIScrollView( "webview" ) {
|
||||
mDocContainer->setFlags( UI_OWNS_CHILDREN_POSITION );
|
||||
mDocContainer->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent );
|
||||
mDocContainer->setParent( this );
|
||||
mDocContainer->setBackgroundColor( Color::White );
|
||||
mStyleSheetDefaultMarker = String::hash( "html_defaults" );
|
||||
}
|
||||
|
||||
UIWebView::~UIWebView() {}
|
||||
|
||||
@@ -2816,4 +2816,25 @@ Float UIWidget::getPropertyHeight() const {
|
||||
}
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
void UIWidget::setStyleSheetProperties( const CSS::StyleSheetProperties& properties ) {
|
||||
mStyle->setStyleSheetProperties( properties );
|
||||
for ( const auto& [_, property] : properties )
|
||||
applyProperty( property );
|
||||
}
|
||||
|
||||
void UIWidget::setStyleSheetProperty( const CSS::StyleSheetProperty& property ) {
|
||||
mStyle->setStyleSheetProperty( property );
|
||||
|
||||
if ( StyleSheetSpecification::instance()->isShorthand( property.getName() ) ) {
|
||||
auto properties = StyleSheetSpecification::instance()
|
||||
->getShorthand( property.getName() )
|
||||
->parse( property.getValue() );
|
||||
for ( const auto& prop : properties ) {
|
||||
applyProperty( prop );
|
||||
}
|
||||
} else
|
||||
applyProperty( property );
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <eepp/ui/css/stylesheetparser.hpp>
|
||||
#include <eepp/ui/tools/uidiffview.hpp>
|
||||
#include <eepp/ui/tools/uiimageviewer.hpp>
|
||||
#include <eepp/ui/tools/uitextureviewer.hpp>
|
||||
@@ -66,6 +67,124 @@ UIWidgetCreator::WidgetCallbackMap UIWidgetCreator::widgetCallback =
|
||||
UIWidgetCreator::RegisteredWidgetCallbackMap UIWidgetCreator::registeredWidget =
|
||||
UIWidgetCreator::RegisteredWidgetCallbackMap();
|
||||
|
||||
static const std::string_view getHTMLBaseDefaultsCSS() {
|
||||
return R"css(
|
||||
body { color: black; margin: 0.67em; }
|
||||
|
||||
h1 { font-size: 2em; font-weight: bold; margin: 0.67em 0; }
|
||||
h2 { font-size: 1.5em; font-weight: bold; margin: 0.83em 0; }
|
||||
h3 { font-size: 1.17em; font-weight: bold; margin: 1em 0; }
|
||||
h4 { font-size: 1em; font-weight: bold; margin: 1.33em 0; }
|
||||
h5 { font-size: 0.83em; font-weight: bold; margin: 1.67em 0; }
|
||||
h6 { font-size: 0.67em; font-weight: bold; margin: 2.33em 0; }
|
||||
|
||||
p { margin: 1em 0; }
|
||||
pre { margin: 1em 0; }
|
||||
blockquote { margin: 1em 0; }
|
||||
hr { margin: 0.5em 0; background-color: gray; height: 1dp; }
|
||||
ul, ol, dl { margin: 1em 0; }
|
||||
|
||||
b, strong { font-weight: bold; }
|
||||
i, em, cite { font-style: italic; }
|
||||
small { font-size: smaller; }
|
||||
u, ins { text-decoration: underline; }
|
||||
s, del { text-decoration: line-through; }
|
||||
code, kbd { font-family: monospace; }
|
||||
sub, sup { font-size: smaller; }
|
||||
mark { background-color: yellow; }
|
||||
|
||||
a, a:link { color: #0000EE; text-decoration: none; cursor: arrow; }
|
||||
a:hover { text-decoration: underline; cursor: hand; }
|
||||
a:visited { color: #551A8B; }
|
||||
|
||||
ul { padding-left: 40dp; list-style-type: disc; }
|
||||
ol { padding-left: 40dp; list-style-type: decimal; }
|
||||
dd { margin-left: 40dp; }
|
||||
|
||||
summary { cursor: pointer; padding-left: 20dp; list-style-type: disclosure-closed; }
|
||||
|
||||
textarea { border-width: 1dp; border-color: #767676; background-color: white; color: black; padding: 2dp; selection-back-color: lightgray; }
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="number"] {
|
||||
border-width: 1dp;
|
||||
border-color: #767676;
|
||||
background-color: white;
|
||||
color: black;
|
||||
padding-top: 1dp;
|
||||
padding-bottom: 1dp;
|
||||
padding-left: 2dp;
|
||||
padding-right: 2dp;
|
||||
hint-color: #767676;
|
||||
}
|
||||
|
||||
input[type="submit"],
|
||||
input[type="button"],
|
||||
input[type="reset"] {
|
||||
border-width: 1dp;
|
||||
border-color: #767676;
|
||||
background-color: #f0f0f0;
|
||||
color: black;
|
||||
padding-top: 2dp;
|
||||
padding-bottom: 3dp;
|
||||
padding-left: 6dp;
|
||||
padding-right: 6dp;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input[type="submit"]:hover,
|
||||
input[type="button"]:hover,
|
||||
input[type="reset"]:hover {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
CheckBox::active,
|
||||
CheckBox::inactive {
|
||||
width: 12dp;
|
||||
height: 12dp;
|
||||
border-width: 1dp;
|
||||
}
|
||||
|
||||
CheckBox::inactive {
|
||||
border-color: #767676;
|
||||
}
|
||||
|
||||
CheckBox::active {
|
||||
border-color: black;
|
||||
foreground-image: rectangle(solid, black);
|
||||
foreground-size: 8dpru 8dpru;
|
||||
foreground-position: center;
|
||||
}
|
||||
|
||||
RadioButton::active,
|
||||
RadioButton::inactive {
|
||||
width: 12dp;
|
||||
height: 12dp;
|
||||
border-width: 1dp;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
RadioButton::inactive {
|
||||
border-color: #767676;
|
||||
}
|
||||
|
||||
RadioButton::active {
|
||||
border-color: black;
|
||||
foreground-image: circle(solid, black);
|
||||
foreground-size: 8dp 8dp;
|
||||
foreground-position: 6dp 6dp;
|
||||
}
|
||||
|
||||
)css";
|
||||
|
||||
}
|
||||
|
||||
void UIWidgetCreator::createBaseWidgetList() {
|
||||
if ( !sBaseListCreated ) {
|
||||
registeredWidget["widget"] = UIWidget::New;
|
||||
@@ -171,35 +290,11 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["h6"] = UIRichText::NewH6;
|
||||
registeredWidget["br"] = UIRichText::NewBr;
|
||||
registeredWidget["hr"] = UIRichText::NewHr;
|
||||
registeredWidget["ul"] = [] {
|
||||
auto* w = UIRichText::NewWithTag( "ul" );
|
||||
w->setFlags( UI_CREATING_NODE );
|
||||
w->applyProperty( StyleSheetProperty( "padding-left", "40dp" ) );
|
||||
w->applyProperty( StyleSheetProperty( "list-style-type", "disc" ) );
|
||||
w->getUIStyle()->setStyleSheetProperty(
|
||||
StyleSheetProperty( "list-style-type", "disc" ) );
|
||||
w->unsetFlags( UI_CREATING_NODE );
|
||||
return w;
|
||||
};
|
||||
registeredWidget["ol"] = [] {
|
||||
auto* w = UIRichText::NewWithTag( "ol" );
|
||||
w->setFlags( UI_CREATING_NODE );
|
||||
w->applyProperty( StyleSheetProperty( "padding-left", "40dp" ) );
|
||||
w->applyProperty( StyleSheetProperty( "list-style-type", "decimal" ) );
|
||||
w->getUIStyle()->setStyleSheetProperty(
|
||||
StyleSheetProperty( "list-style-type", "decimal" ) );
|
||||
w->unsetFlags( UI_CREATING_NODE );
|
||||
return w;
|
||||
};
|
||||
registeredWidget["ul"] = [] { return UIRichText::NewWithTag( "ul" ); };
|
||||
registeredWidget["ol"] = [] { return UIRichText::NewWithTag( "ol" ); };
|
||||
registeredWidget["dl"] = [] { return UIRichText::NewWithTag( "dl" ); };
|
||||
registeredWidget["dt"] = [] { return UIRichText::NewWithTag( "dt" ); };
|
||||
registeredWidget["dd"] = [] {
|
||||
auto* w = UIRichText::NewWithTag( "dd" );
|
||||
w->setFlags( UI_CREATING_NODE );
|
||||
w->applyProperty( StyleSheetProperty( "margin-left", "40dp" ) );
|
||||
w->unsetFlags( UI_CREATING_NODE );
|
||||
return w;
|
||||
};
|
||||
registeredWidget["dd"] = [] { return UIRichText::NewWithTag( "dd" ); };
|
||||
registeredWidget["li"] = UIHTMLListItem::New;
|
||||
registeredWidget["details"] = UIHTMLDetails::New;
|
||||
registeredWidget["summary"] = UIHTMLSummary::New;
|
||||
@@ -250,10 +345,8 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["textarea"] = UIHTMLTextArea::New;
|
||||
registeredWidget["button"] = [] {
|
||||
auto but = UIPushButton::NewWithTag( "button" );
|
||||
but->setFlags( UI_CREATING_NODE );
|
||||
but->setFlags( UI_HTML_ELEMENT );
|
||||
but->setLayoutSizePolicy( SizePolicy::WrapContent, SizePolicy::WrapContent );
|
||||
but->unsetFlags( UI_CREATING_NODE );
|
||||
return but;
|
||||
};
|
||||
registeredWidget["webview"] = UIWebView::New;
|
||||
@@ -322,4 +415,16 @@ std::vector<std::string> UIWidgetCreator::getWidgetNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
void UIWidgetCreator::loadHTMLBaseDefaults( CSS::StyleSheet& styleSheet, Uint32 marker ) {
|
||||
if ( styleSheet.markerExists( marker ) )
|
||||
return;
|
||||
CSS::StyleSheetParser parser;
|
||||
if ( parser.loadFromString( getHTMLBaseDefaultsCSS() ) ) {
|
||||
CSS::StyleSheet baseDefaults = parser.getStyleSheet();
|
||||
baseDefaults.setSelectorSpecificity( 0 );
|
||||
baseDefaults.setMarker( marker );
|
||||
styleSheet.combineStyleSheet( baseDefaults );
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "compareimages.hpp"
|
||||
#include "eepp/ui/uiwindow.hpp"
|
||||
#include "utest.hpp"
|
||||
|
||||
#include <eepp/graphics/fontfamily.hpp>
|
||||
@@ -782,13 +783,17 @@ UTEST( UIHTMLDetails, summaryListStyleNoneClearsDefaultPadding ) {
|
||||
init_ui_test();
|
||||
auto* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( R"html(
|
||||
<details>
|
||||
<summary id="default">Default marker</summary>
|
||||
<summary id="none" style="list-style-type: none;">No marker</summary>
|
||||
<summary id="explicit" style="list-style-type: none; padding-left: 7px;">
|
||||
Explicit padding
|
||||
</summary>
|
||||
</details>
|
||||
<html>
|
||||
<body>
|
||||
<details>
|
||||
<summary id="default">Default marker</summary>
|
||||
<summary id="none" style="list-style-type: none;">No marker</summary>
|
||||
<summary id="explicit" style="list-style-type: none; padding-left: 7px;">
|
||||
Explicit padding
|
||||
</summary>
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
||||
)html" ) );
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user