mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Improved RichText support, added more support for basic HTML elements.
This commit is contained in:
@@ -112,6 +112,11 @@
|
||||
"max_tokens": 1000000,
|
||||
"cheapest": true
|
||||
},
|
||||
{
|
||||
"name": "gemini-3.1-flash-lite-preview",
|
||||
"display_name": "Gemini 3.1 Flash Lite Preview",
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"name": "gemini-2.5-pro",
|
||||
"display_name": "Gemini 2.5 Pro",
|
||||
|
||||
@@ -52,6 +52,93 @@
|
||||
droppable-hovering-color: #FFFFFF20;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-style: bold;
|
||||
}
|
||||
|
||||
u {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
s {
|
||||
text-decoration: strikethrough;
|
||||
}
|
||||
|
||||
i,
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32dp;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24dp;
|
||||
margin: 0.83em 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 18dp;
|
||||
margin: 1.00em 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 16dp;
|
||||
margin: 1.33em 0;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 13dp;
|
||||
margin: 1.67em 0;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 11dp;
|
||||
margin: 1.67em 0;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
p, ol, ul, pre {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-left: 2em;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='currentColor'/></svg>");
|
||||
background-tint: var(--font);
|
||||
background-position: 0.6em 0.5em;
|
||||
background-size: 0.8em 0.8em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary);
|
||||
selection-color: var(--font-selected-pressed);
|
||||
selection-back-color: var(--primary);
|
||||
cursor: arrow;
|
||||
text-decoration: none;
|
||||
gravity: bottom;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--font-highlight);
|
||||
cursor: hand;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
img {
|
||||
scale-type: fit-inside;
|
||||
layout-width: match_parent;
|
||||
layout-height: wrap_content;
|
||||
max-height: 100vh;
|
||||
clip: true;
|
||||
}
|
||||
|
||||
pushbutton,
|
||||
selectbutton,
|
||||
tableview::cell,
|
||||
|
||||
BIN
bin/unit_tests/assets/fontrendering/eepp-ui-richtext.webp
Normal file
BIN
bin/unit_tests/assets/fontrendering/eepp-ui-richtext.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -15,6 +15,8 @@ class EE_API UILinearLayout : public UILayout {
|
||||
|
||||
static UILinearLayout* NewHorizontal();
|
||||
|
||||
static UILinearLayout* NewVerticalWidthMatchParent();
|
||||
|
||||
virtual Uint32 getType() const;
|
||||
|
||||
virtual bool isType( const Uint32& type ) const;
|
||||
|
||||
@@ -83,12 +83,15 @@ class EE_API UIRichText : public UILayout {
|
||||
|
||||
UIRichText* setTextAlign( const Uint32& align );
|
||||
|
||||
virtual void updateLayout();
|
||||
|
||||
protected:
|
||||
RichText mRichText;
|
||||
|
||||
virtual Uint32 onMessage( const NodeMessage* Msg );
|
||||
|
||||
virtual void onSizeChange();
|
||||
virtual void onPaddingChange();
|
||||
virtual void onLayoutUpdate();
|
||||
virtual void onChildCountChange( Node* child, const bool& removed );
|
||||
virtual void onFontChanged();
|
||||
virtual void onFontStyleChanged();
|
||||
|
||||
@@ -84,9 +84,9 @@ class EE_API UIScrollBar : public UIWidget {
|
||||
|
||||
protected:
|
||||
ScrollBarType mScrollBarStyle;
|
||||
UISlider* mSlider;
|
||||
UIWidget* mBtnUp;
|
||||
UIWidget* mBtnDown;
|
||||
UISlider* mSlider{ nullptr };
|
||||
UIWidget* mBtnUp{ nullptr };
|
||||
UIWidget* mBtnDown{ nullptr };
|
||||
|
||||
virtual void onSizeChange();
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ class EE_API UITextSpan : public UIWidget {
|
||||
|
||||
static UITextSpan* NewMark() { return NewWithTag( "mark" ); }
|
||||
|
||||
static UITextSpan* NewCode() { return NewWithTag( "code" ); }
|
||||
|
||||
virtual ~UITextSpan();
|
||||
|
||||
virtual Uint32 getType() const;
|
||||
|
||||
@@ -200,6 +200,8 @@ class EE_API UIAnchor : public UITextView {
|
||||
public:
|
||||
static UIAnchor* New();
|
||||
|
||||
static UIAnchor* NewA();
|
||||
|
||||
virtual bool applyProperty( const StyleSheetProperty& attribute );
|
||||
|
||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||
@@ -212,7 +214,7 @@ class EE_API UIAnchor : public UITextView {
|
||||
const std::string& getHref() const;
|
||||
|
||||
protected:
|
||||
UIAnchor();
|
||||
UIAnchor( const std::string& tag = "anchor" );
|
||||
|
||||
std::string mHref;
|
||||
|
||||
|
||||
@@ -93,13 +93,15 @@ void UIImage::onAutoSize() {
|
||||
|
||||
Sizef size( getPixelsSize() );
|
||||
|
||||
if ( mWidthPolicy == SizePolicy::WrapContent )
|
||||
if ( mWidthPolicy == SizePolicy::WrapContent ) {
|
||||
size.x =
|
||||
( (int)mDrawable->getPixelsSize().getWidth() + mPaddingPx.Left + mPaddingPx.Right );
|
||||
}
|
||||
|
||||
if ( mHeightPolicy == SizePolicy::WrapContent )
|
||||
if ( mHeightPolicy == SizePolicy::WrapContent ) {
|
||||
size.y = ( (int)mDrawable->getPixelsSize().getHeight() + mPaddingPx.Top +
|
||||
mPaddingPx.Bottom );
|
||||
}
|
||||
|
||||
setPixelsSize( size );
|
||||
}
|
||||
@@ -210,7 +212,15 @@ void UIImage::safeDeleteDrawable() {
|
||||
|
||||
void UIImage::onDrawableResourceEvent( DrawableResource::Event event, DrawableResource* ) {
|
||||
if ( event == DrawableResource::Change ) {
|
||||
invalidateDraw();
|
||||
runOnMainThread( [this] {
|
||||
auto s = mSize;
|
||||
onAutoSize();
|
||||
calcDestSize();
|
||||
if ( mSize != s ) {
|
||||
invalidateDraw();
|
||||
notifyLayoutAttrChangeParent();
|
||||
}
|
||||
} );
|
||||
} else if ( event == DrawableResource::Unload ) {
|
||||
mDrawable = NULL;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ UILinearLayout* UILinearLayout::NewHorizontal() {
|
||||
return ( eeNew( UILinearLayout, () ) )->setOrientation( UIOrientation::Horizontal );
|
||||
}
|
||||
|
||||
UILinearLayout* UILinearLayout::NewVerticalWidthMatchParent() {
|
||||
return ( eeNew( UILinearLayout, () ) )
|
||||
->setLayoutWidthPolicy( SizePolicy::MatchParent )
|
||||
->asType<UILinearLayout>();
|
||||
}
|
||||
|
||||
UILinearLayout::UILinearLayout() :
|
||||
UILayout( "linearlayout" ), mOrientation( UIOrientation::Vertical ) {
|
||||
mFlags |= UI_OWNS_CHILDREN_POSITION;
|
||||
|
||||
@@ -176,7 +176,8 @@ UIRichText* UIRichText::setFont( Graphics::Font* font ) {
|
||||
if ( NULL != font && mRichText.getFontStyleConfig().Font != font ) {
|
||||
mRichText.getFontStyleConfig().Font = font;
|
||||
mRichText.invalidate();
|
||||
setLayoutDirty();
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
updateDefaultSpansStyle();
|
||||
}
|
||||
return this;
|
||||
@@ -190,7 +191,9 @@ UIRichText* UIRichText::setFontSize( const Uint32& characterSize ) {
|
||||
if ( mRichText.getFontStyleConfig().CharacterSize != characterSize ) {
|
||||
mRichText.getFontStyleConfig().CharacterSize = characterSize;
|
||||
mRichText.invalidate();
|
||||
setLayoutDirty();
|
||||
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
updateDefaultSpansStyle();
|
||||
}
|
||||
return this;
|
||||
@@ -204,7 +207,9 @@ UIRichText* UIRichText::setFontStyle( const Uint32& fontStyle ) {
|
||||
if ( mRichText.getFontStyleConfig().Style != fontStyle ) {
|
||||
mRichText.getFontStyleConfig().Style = fontStyle;
|
||||
mRichText.invalidate();
|
||||
setLayoutDirty();
|
||||
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
updateDefaultSpansStyle();
|
||||
}
|
||||
return this;
|
||||
@@ -261,7 +266,9 @@ UIRichText* UIRichText::setOutlineThickness( const Float& outlineThickness ) {
|
||||
if ( mRichText.getFontStyleConfig().OutlineThickness != outlineThickness ) {
|
||||
mRichText.getFontStyleConfig().OutlineThickness = outlineThickness;
|
||||
mRichText.invalidate();
|
||||
setLayoutDirty();
|
||||
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
updateDefaultSpansStyle();
|
||||
}
|
||||
return this;
|
||||
@@ -287,7 +294,9 @@ Uint32 UIRichText::getTextAlign() const {
|
||||
UIRichText* UIRichText::setTextAlign( const Uint32& align ) {
|
||||
if ( mRichText.getAlign() != align ) {
|
||||
mRichText.setAlign( align );
|
||||
setLayoutDirty();
|
||||
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -343,17 +352,18 @@ void UIRichText::loadFromXmlNode( const pugi::xml_node& node ) {
|
||||
}
|
||||
|
||||
endAttributesTransaction();
|
||||
setLayoutDirty();
|
||||
}
|
||||
|
||||
void UIRichText::onSizeChange() {
|
||||
UILayout::onSizeChange();
|
||||
setLayoutDirty(); // Re-wrap if size changes
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
}
|
||||
|
||||
void UIRichText::onPaddingChange() {
|
||||
UILayout::onPaddingChange();
|
||||
setLayoutDirty();
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
}
|
||||
|
||||
void UIRichText::onChildCountChange( Node* child, const bool& removed ) {
|
||||
@@ -361,15 +371,19 @@ void UIRichText::onChildCountChange( Node* child, const bool& removed ) {
|
||||
if ( !removed && child->isWidget() && child->isType( UI_TYPE_TEXTSPAN ) ) {
|
||||
static_cast<UITextSpan*>( child )->setInheritedStyle( mRichText.getFontStyleConfig() );
|
||||
}
|
||||
setLayoutDirty();
|
||||
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
}
|
||||
|
||||
void UIRichText::onFontChanged() {
|
||||
setLayoutDirty();
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
}
|
||||
|
||||
void UIRichText::onFontStyleChanged() {
|
||||
setLayoutDirty();
|
||||
notifyLayoutAttrChange();
|
||||
notifyLayoutAttrChangeParent();
|
||||
}
|
||||
|
||||
void UIRichText::onAlphaChange() {
|
||||
@@ -397,6 +411,11 @@ void UIRichText::rebuildRichText() {
|
||||
UITextSpan* span = static_cast<UITextSpan*>( widget );
|
||||
mRichText.addSpan( span->getText(), span->getFontStyleConfig() );
|
||||
} else {
|
||||
if ( mSize.getWidth() != 0 &&
|
||||
widget->getLayoutWidthPolicy() == SizePolicy::MatchParent ) {
|
||||
widget->setPixelsSize( mSize.getWidth(), widget->getPixelsSize().getHeight() );
|
||||
}
|
||||
|
||||
mRichText.addCustomSize( widget->getPixelsSize() );
|
||||
}
|
||||
}
|
||||
@@ -457,14 +476,17 @@ void UIRichText::updateDefaultSpansStyle() {
|
||||
}
|
||||
}
|
||||
|
||||
void UIRichText::onLayoutUpdate() {
|
||||
void UIRichText::updateLayout() {
|
||||
if ( mPacking )
|
||||
return;
|
||||
mPacking = true;
|
||||
|
||||
rebuildRichText();
|
||||
|
||||
mRichText.getSize(); // Forces an updateLayout internally
|
||||
|
||||
positionChildren();
|
||||
|
||||
// Resize logic
|
||||
if ( mWidthPolicy == SizePolicy::WrapContent ) {
|
||||
setInternalPixelsWidth( mRichText.getSize().getWidth() + mPaddingPx.Left +
|
||||
mPaddingPx.Right );
|
||||
@@ -474,7 +496,19 @@ void UIRichText::onLayoutUpdate() {
|
||||
mPaddingPx.Bottom );
|
||||
}
|
||||
|
||||
UILayout::onLayoutUpdate();
|
||||
mPacking = false;
|
||||
mDirtyLayout = false;
|
||||
}
|
||||
|
||||
Uint32 UIRichText::onMessage( const NodeMessage* Msg ) {
|
||||
switch ( Msg->getMsg() ) {
|
||||
case NodeMessage::LayoutAttributeChange: {
|
||||
tryUpdateLayout();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -121,6 +121,9 @@ void UIScrollBar::setTheme( UITheme* Theme ) {
|
||||
}
|
||||
|
||||
void UIScrollBar::onAutoSize() {
|
||||
if ( mSlider == nullptr )
|
||||
return;
|
||||
|
||||
Sizef size;
|
||||
UISkin* tSkin = mSlider->getBackSlider()->getSkin();
|
||||
|
||||
|
||||
@@ -406,8 +406,9 @@ void UITextView::alignFix() {
|
||||
break;
|
||||
}
|
||||
case UI_VALIGN_BOTTOM:
|
||||
mRealAlignOffset.y = ( (Float)mSize.y - mPaddingPx.Top - mPaddingPx.Bottom -
|
||||
(Float)mTextCache.getTextHeight() );
|
||||
mRealAlignOffset.y =
|
||||
( (Float)mSize.y - mPaddingPx.Top - mPaddingPx.Bottom -
|
||||
(Float)mTextCache.getFont()->getAscent( mTextCache.getCharacterSize() ) );
|
||||
break;
|
||||
case UI_VALIGN_TOP:
|
||||
mRealAlignOffset.y = 0;
|
||||
@@ -950,7 +951,11 @@ UIAnchor* UIAnchor::New() {
|
||||
return eeNew( UIAnchor, () );
|
||||
}
|
||||
|
||||
UIAnchor::UIAnchor() : UITextView( "anchor" ) {
|
||||
UIAnchor* UIAnchor::NewA() {
|
||||
return eeNew( UIAnchor, ( "a" ) );
|
||||
}
|
||||
|
||||
UIAnchor::UIAnchor( const std::string& tag ) : UITextView( tag ) {
|
||||
onClick(
|
||||
[this]( const MouseEvent* ) {
|
||||
if ( !mHref.empty() )
|
||||
|
||||
@@ -125,13 +125,17 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["rlay"] = UIRelativeLayout::New;
|
||||
registeredWidget["tooltip"] = UITooltip::New;
|
||||
registeredWidget["tv"] = UITextView::New;
|
||||
registeredWidget["a"] = UIAnchor::New;
|
||||
|
||||
// HTML elements
|
||||
registeredWidget["a"] = UIAnchor::NewA;
|
||||
registeredWidget["span"] = UITextSpan::New;
|
||||
registeredWidget["em"] = UITextSpan::NewEmphasis;
|
||||
registeredWidget["b"] = UITextSpan::NewBold;
|
||||
registeredWidget["strong"] = UITextSpan::NewBold;
|
||||
registeredWidget["i"] = UITextSpan::NewItalics;
|
||||
registeredWidget["u"] = UITextSpan::NewUnderline;
|
||||
registeredWidget["s"] = UITextSpan::NewStrikethrough;
|
||||
registeredWidget["code"] = UITextSpan::NewCode;
|
||||
registeredWidget["mark"] = UITextSpan::NewMark;
|
||||
registeredWidget["div"] = UIRichText::New;
|
||||
registeredWidget["p"] = UIRichText::NewParagraph;
|
||||
@@ -141,9 +145,11 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["h4"] = UIRichText::NewH4;
|
||||
registeredWidget["h5"] = UIRichText::NewH5;
|
||||
registeredWidget["h6"] = UIRichText::NewH6;
|
||||
registeredWidget["ul"] = UILinearLayout::NewVertical;
|
||||
registeredWidget["ol"] = UILinearLayout::NewVertical;
|
||||
registeredWidget["ul"] = UILinearLayout::NewVerticalWidthMatchParent;
|
||||
registeredWidget["ol"] = UILinearLayout::NewVerticalWidthMatchParent;
|
||||
registeredWidget["li"] = UIRichText::NewListItem;
|
||||
registeredWidget["pre"] = UITextSpan::New;
|
||||
registeredWidget["img"] = [] { return UIImage::NewWithTag( "img" ); };
|
||||
|
||||
sBaseListCreated = true;
|
||||
}
|
||||
|
||||
@@ -3,20 +3,29 @@
|
||||
EE_MAIN_FUNC int main( int, char** ) {
|
||||
UIApplication app( { 800, 600, "eepp - UIRichText Example" } );
|
||||
app.getUI()->loadLayoutFromString( R"xml(
|
||||
<LinearLayout layout_width="match_parent"
|
||||
layout_height="match_parent"
|
||||
orientation="vertical">
|
||||
<vbox layout_width="match_parent" layout_height="match_parent">
|
||||
<ScrollView lw="mp" lh="mp">
|
||||
<vbox layout_width="match_parent" layout_height="wrap_content" padding="8dp">
|
||||
<RichText font-size="12dp"
|
||||
font-color="#cecece">Welcome to the <span color="#FFD700" font-style="bold">UIRichText</span> example!
|
||||
color="white">Welcome to the <span color="#FFD700" font-style="bold">UIRichText</span> example!
|
||||
This component supports <span color="#00FF00" font-style="italic">styled text</span>,
|
||||
<span color="#00BFFF" font-style="shadow">shadows</span>,
|
||||
and <span color="#FF4500" text-stroke-width="1dp" text-stroke-color="black">outlines</span> using <span font-family="monospace" color="#A9A9A9">HTML-like tags</span>.
|
||||
</RichText>
|
||||
<Image src="file://assets/icon/ee.png" margin="4dp" layout-gravity="center_horizontal" />
|
||||
<RichText font-size="12dp"
|
||||
font-color="#ccc">We can also mix <span color="#FFD700" font-style="bold">contents</span> with more <span color="#00FF00" font-style="italic">text</span>!
|
||||
color="#fefefe">We can also mix <span color="#FFD700" font-style="bold">contents</span> with more <span color="#00FF00" font-style="italic">text</span>!
|
||||
</RichText>
|
||||
</LinearLayout>
|
||||
</vbox>
|
||||
</ScrollView>
|
||||
</vbox>
|
||||
)xml" );
|
||||
|
||||
app.getUI()->on( Event::KeyUp, [&app]( const Event* event ) {
|
||||
if ( event->asKeyEvent()->getKeyCode() == KEY_F11 ) {
|
||||
UIWidgetInspector::create( app.getUI() );
|
||||
}
|
||||
} );
|
||||
|
||||
return app.run();
|
||||
}
|
||||
|
||||
@@ -354,14 +354,14 @@ UTEST( UIRichText, RichTextTest ) {
|
||||
layout_height="match_parent"
|
||||
orientation="vertical">
|
||||
<RichText font-size="12dp"
|
||||
font-color="#cecece">Welcome to the <span color="#FFD700" font-style="bold">UIRichText</span> example!
|
||||
color="white">Welcome to the <span color="#FFD700" font-style="bold">UIRichText</span> example!
|
||||
This component supports <span color="#00FF00" font-style="italic">styled text</span>,
|
||||
<span color="#00BFFF" font-style="shadow">shadows</span>,
|
||||
and <span color="#FF4500" text-stroke-width="1dp" text-stroke-color="black">outlines</span> using <span font-family="monospace" color="#A9A9A9">HTML-like tags</span>.
|
||||
</RichText>
|
||||
<Image src="file://assets/icon/ee.png" margin="4dp" layout-gravity="center_horizontal" />
|
||||
<RichText font-size="12dp"
|
||||
font-color="#ccc">We can also mix <span color="#FFD700" font-style="bold">contents</span> with more <span color="#00FF00" font-style="italic">text</span>!
|
||||
color="#efefef">We can also mix <span color="#FFD700" font-style="bold">contents</span> with more <span color="#00FF00" font-style="italic">text</span>!
|
||||
</RichText>
|
||||
</LinearLayout>
|
||||
)xml" );
|
||||
@@ -370,7 +370,7 @@ UTEST( UIRichText, RichTextTest ) {
|
||||
SceneManager::instance()->draw();
|
||||
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
compareImages( utest_state, utest_result, app.getWindow(), "eepp-uirichtext" );
|
||||
compareImages( utest_state, utest_result, app.getWindow(), "eepp-ui-richtext" );
|
||||
};
|
||||
|
||||
UTEST_PRINT_STEP( "Text Shaper disabled" );
|
||||
|
||||
@@ -169,18 +169,18 @@ static const auto LAYOUT = R"xml(
|
||||
<vbox lw="wc" lh="wc" lg="center">
|
||||
<hbox>
|
||||
<tv text='@string(the_ecode_nbsp, "The ecode ")' />
|
||||
<a id="home_doc" text="@string(documentation, documentation)" href="https://github.com/SpartanJ/ecode" />
|
||||
<Anchor id="home_doc" text="@string(documentation, documentation)" href="https://github.com/SpartanJ/ecode" />
|
||||
</hbox>
|
||||
<hbox>
|
||||
<tv text='@string(the_ecode_nbsp, "The ecode ")' />
|
||||
<a id="home_forum" text="@string(forum, forum)" href="https://github.com/SpartanJ/ecode/discussions" />
|
||||
<Anchor id="home_forum" text="@string(forum, forum)" href="https://github.com/SpartanJ/ecode/discussions" />
|
||||
</hbox>
|
||||
<hbox>
|
||||
<tv text='@string(the_ecode_nbsp, "The ecode ")' />
|
||||
<a id="home_issues" text="@string(issues, issues)" href="https://github.com/SpartanJ/ecode/issues" />
|
||||
<Anchor id="home_issues" text="@string(issues, issues)" href="https://github.com/SpartanJ/ecode/issues" />
|
||||
</hbox>
|
||||
<hbox>
|
||||
<a id="check-for-updates" text="@string(check_for_updates, Check for Updates)" />
|
||||
<Anchor id="check-for-updates" text="@string(check_for_updates, Check for Updates)" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
</vbox>
|
||||
|
||||
Reference in New Issue
Block a user