From d93b03f624dfe884b7ad2bf981e51bd343e6b112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 26 Apr 2026 01:12:42 -0300 Subject: [PATCH] Pump versions. Add keybindings to the ecode markdown viewer. Optimization in BlockLayouter. Hack to display code in
 HTML
 elements.

---
 include/eepp/ui/css/propertydefinition.hpp  |  1 +
 include/eepp/ui/uihtmlwidget.hpp            |  1 +
 include/eepp/version.hpp                    |  8 ++--
 src/eepp/ui/blocklayouter.cpp               |  4 ++
 src/eepp/ui/css/stylesheetspecification.cpp |  2 +
 src/eepp/ui/uicodeeditor.cpp                | 47 ++++++++++++++++++---
 src/eepp/ui/uirichtext.cpp                  | 16 ++++++-
 src/tools/ecode/ecode.cpp                   |  7 ++-
 src/tools/ecode/version.hpp                 |  6 +--
 src/tools/ecode/widgetcommandexecuter.hpp   | 11 +++++
 10 files changed, 88 insertions(+), 15 deletions(-)

diff --git a/include/eepp/ui/css/propertydefinition.hpp b/include/eepp/ui/css/propertydefinition.hpp
index 4525ef866..8205dee39 100644
--- a/include/eepp/ui/css/propertydefinition.hpp
+++ b/include/eepp/ui/css/propertydefinition.hpp
@@ -247,6 +247,7 @@ enum class PropertyId : Uint32 {
 	Bottom = String::hash( "bottom" ),
 	Left = String::hash( "left" ),
 	ZIndex = String::hash( "z-index" ),
+	DataLanguage = String::hash( "data-language" ), // Minor hack
 };
 
 enum class PropertyType : Uint32 {
diff --git a/include/eepp/ui/uihtmlwidget.hpp b/include/eepp/ui/uihtmlwidget.hpp
index 1e9e2e04a..9bdf461f3 100644
--- a/include/eepp/ui/uihtmlwidget.hpp
+++ b/include/eepp/ui/uihtmlwidget.hpp
@@ -56,6 +56,7 @@ class EE_API UIHTMLWidget : public UILayout {
 	Rectf mOffsets{ 0, 0, 0, 0 };
 	int mZIndex{ 0 };
 	UILayouter* mLayouter{ nullptr };
+	UnorderedMap mDataProperties;
 };
 
 }} // namespace EE::UI
diff --git a/include/eepp/version.hpp b/include/eepp/version.hpp
index 6fe91c64e..cb5f61ff3 100644
--- a/include/eepp/version.hpp
+++ b/include/eepp/version.hpp
@@ -4,10 +4,10 @@
 #include 
 #include 
 
-#define EEPP_MAJOR_VERSION 2
-#define EEPP_MINOR_VERSION 9
-#define EEPP_PATCH_LEVEL 2
-#define EEPP_CODENAME "Sādhanā"
+#define EEPP_MAJOR_VERSION 3
+#define EEPP_MINOR_VERSION 0
+#define EEPP_PATCH_LEVEL 0
+#define EEPP_CODENAME "Khaya"
 
 /** The compiled version of the library */
 #define EEPP_VERSION( x )             \
diff --git a/src/eepp/ui/blocklayouter.cpp b/src/eepp/ui/blocklayouter.cpp
index 75945eeea..28baa9e46 100644
--- a/src/eepp/ui/blocklayouter.cpp
+++ b/src/eepp/ui/blocklayouter.cpp
@@ -54,6 +54,8 @@ void BlockLayouter::updateLayout() {
 	mResizedCount = 0;
 	mPacking = true;
 
+	mContainer->beginAttributesTransaction();
+
 	setMatchParentIfNeededVerticalGrowth();
 
 	const StyleSheetProperty* prop = nullptr;
@@ -94,6 +96,8 @@ void BlockLayouter::updateLayout() {
 		 mContainer->getLayoutHeightPolicy() == SizePolicy::WrapContent )
 		mContainer->setInternalPixelsHeight( totH );
 
+	mContainer->endAttributesTransaction();
+
 	if ( mResizedCount > 0 )
 		positionRichTextChildren( rt );
 
diff --git a/src/eepp/ui/css/stylesheetspecification.cpp b/src/eepp/ui/css/stylesheetspecification.cpp
index 378b01d54..e0b6f043c 100644
--- a/src/eepp/ui/css/stylesheetspecification.cpp
+++ b/src/eepp/ui/css/stylesheetspecification.cpp
@@ -456,6 +456,8 @@ void StyleSheetSpecification::registerDefaultProperties() {
 	registerProperty( "display-options", "" ).setType( PropertyType::String );
 	registerProperty( "menu-width-mode", "" ).setType( PropertyType::String );
 
+	registerProperty( "data-language", "" ).setType( PropertyType::String );
+
 	// Shorthands
 	registerShorthand( "margin", { "margin-top", "margin-right", "margin-bottom", "margin-left" },
 					   "box" );
diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp
index 8755f2c04..f6673687f 100644
--- a/src/eepp/ui/uicodeeditor.cpp
+++ b/src/eepp/ui/uicodeeditor.cpp
@@ -3055,6 +3055,10 @@ bool UICodeEditor::applyProperty( const StyleSheetProperty& attribute ) {
 		case PropertyId::Text:
 			mDoc->textInput( attribute.asString() );
 			break;
+		case PropertyId::DataLanguage:
+			setSyntaxDefinition(
+				SyntaxDefinitionManager::instance()->findFromString( attribute.asString() ) );
+			break;
 		default:
 			return UIWidget::applyProperty( attribute );
 	}
@@ -5697,13 +5701,46 @@ void UICodeEditor::loadFromXmlNode( const pugi::xml_node& node ) {
 
 	UIWidget::loadFromXmlNode( node );
 
-	if ( !node.text().empty() ) {
-		std::string_view str{ node.text().as_string() };
-		if ( '\n' == str.back() )
-			str = str.substr( 0, str.size() - 1 );
-		mDoc->textInput( str );
+	std::string text;
+	bool hasElementChildren = false;
+	for ( pugi::xml_node child : node.children() ) {
+		if ( child.type() == pugi::node_element ) {
+			hasElementChildren = true;
+			break;
+		}
 	}
 
+	auto collectText = []( const pugi::xml_node& n, std::string& out, auto& self ) -> void {
+		for ( pugi::xml_node c : n.children() ) {
+			if ( c.type() == pugi::node_pcdata || c.type() == pugi::node_cdata ) {
+				out += c.value();
+			} else if ( c.type() == pugi::node_element ) {
+				self( c, out, self );
+			}
+		}
+	};
+
+	if ( hasElementChildren ) {
+		for ( pugi::xml_node child : node.children() ) {
+			if ( child.type() == pugi::node_element ) {
+				if ( !text.empty() )
+					text += "\n";
+				collectText( child, text, collectText );
+			}
+		}
+	} else {
+		for ( pugi::xml_node child : node.children() ) {
+			if ( child.type() == pugi::node_pcdata || child.type() == pugi::node_cdata ) {
+				text += child.value();
+			}
+		}
+		if ( !text.empty() && text.back() == '\n' )
+			text.pop_back();
+	}
+
+	if ( !text.empty() )
+		mDoc->textInput( text );
+
 	endAttributesTransaction();
 }
 
diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp
index 08108ef1b..b0d424401 100644
--- a/src/eepp/ui/uirichtext.cpp
+++ b/src/eepp/ui/uirichtext.cpp
@@ -50,7 +50,9 @@ UIHTMLBody* UIHTMLBody::New( const std::string& tag ) {
 	return eeNew( UIHTMLBody, ( tag ) );
 }
 
-UIHTMLBody::UIHTMLBody( const std::string& tag ) : UIRichText( tag ) {}
+UIHTMLBody::UIHTMLBody( const std::string& tag ) : UIRichText( tag ) {
+	enableReportSizeChangeToChildren();
+}
 
 Uint32 UIHTMLBody::getType() const {
 	return UI_TYPE_HTML_BODY;
@@ -228,6 +230,13 @@ bool UIRichText::applyProperty( const StyleSheetProperty& attribute ) {
 				setTextAlign( TEXT_ALIGN_RIGHT );
 			break;
 		}
+		case PropertyId::DataLanguage: {
+			if ( mTag == "pre" && mChild && mChild->isType( UI_TYPE_CODEEDITOR ) ) {
+				mChild->asType()->applyProperty( attribute );
+			} else
+				mDataProperties["data-language"] = attribute;
+			break;
+		}
 		default:
 			return UILayout::applyProperty( attribute );
 	}
@@ -479,6 +488,11 @@ void UIRichText::loadFromXmlNode( const pugi::xml_node& node ) {
 					editor->setShowLineNumber( false );
 					editor->setShowFoldingRegion( false );
 					editor->setLocked( true );
+
+					auto langIt = mDataProperties.find( "data-language" );
+					if ( langIt != mDataProperties.end() ) {
+						editor->applyProperty( langIt->second );
+					}
 				}
 			} else {
 				// Let parent logic load standard child widget
diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp
index 8b0761408..21028d268 100644
--- a/src/tools/ecode/ecode.cpp
+++ b/src/tools/ecode/ecode.cpp
@@ -3236,7 +3236,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) {
 					auto splitter = getSplitter();
 					auto editor = static_cast( client );
 					auto doc = editor->getDocumentRef();
-					auto scrollView = UIScrollView::New();
+					auto scrollView = UIScrollViewCommandExecuter::New();
 					auto mdView = UIMarkdownView::New();
 					mdView->setParent( scrollView );
 
@@ -3274,11 +3274,14 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) {
 
 					mdView->loadFromString( doc->toUtf8String() );
 					auto title =
-						i18n( "markdown_live_preview_ellipsis", "Markdown Live Preview:" ) + " " +
+						i18n( "markdown_live_preview_colon", "Markdown Live Preview:" ) + " " +
 						doc->getFilename();
 					auto [tab, _] =
 						getSplitter()->createWidgetInTabWidget( tabWidget, scrollView, title );
 					tab->setIcon( findIcon( "filetype-md" ) );
+					tab->setTooltipText( title );
+
+					registerUnlockedCommands( *scrollView );
 
 					if ( removeUnusedEditor )
 						splitter->removeUnusedTab( tabWidget );
diff --git a/src/tools/ecode/version.hpp b/src/tools/ecode/version.hpp
index 500ca8d33..f318c30f0 100644
--- a/src/tools/ecode/version.hpp
+++ b/src/tools/ecode/version.hpp
@@ -7,13 +7,13 @@
 using namespace EE;
 
 #define ECODE_MAJOR_VERSION 0
-#define ECODE_MINOR_VERSION 7
-#define ECODE_PATCH_LEVEL 5
+#define ECODE_MINOR_VERSION 8
+#define ECODE_PATCH_LEVEL 0
 /* ECODE_COMMIT_NUMBER 9999 is used for official releases, nightly builds (pre-releases) will
  * contain the number of commits after the last official release
  */
 #define ECODE_COMMIT_NUMBER 9999
-#define ECODE_CODENAME "Vastiva"
+#define ECODE_CODENAME "Kappa"
 
 /** The compiled version of the library */
 #define ECODE_VERSION( x )              \
diff --git a/src/tools/ecode/widgetcommandexecuter.hpp b/src/tools/ecode/widgetcommandexecuter.hpp
index 9d2fbe60c..ac1c7d0f8 100644
--- a/src/tools/ecode/widgetcommandexecuter.hpp
+++ b/src/tools/ecode/widgetcommandexecuter.hpp
@@ -97,6 +97,17 @@ class UIVLinearLayoutCommandExecuter : public UILinearLayout, public WidgetComma
 	}
 };
 
+class UIScrollViewCommandExecuter : public UIScrollView, public WidgetCommandExecuter {
+  public:
+	static UIScrollViewCommandExecuter* New() { return eeNew( UIScrollViewCommandExecuter, () ); }
+
+	UIScrollViewCommandExecuter() : UIScrollView(), WidgetCommandExecuter( getInput() ) {}
+
+	virtual Uint32 onKeyDown( const KeyEvent& event ) {
+		return WidgetCommandExecuter::onKeyDown( event ) || UIScrollView::onKeyDown( event );
+	}
+};
+
 } // namespace ecode
 
 #endif // ECODE_WIDGETCOMMANDEXECUTER_HPP