Parse Text - Render Controller Button Glyphs
Hi team,
We’re having a problem replicating in Noesis a feature we had in our old Font Renderer.
In our old system, we could parse a piece of text. Using certain code, we could display a glyph of a controller button (like an emoji). These glyphs would be part of our fonts. The glyphs would change depending on what platform you were playing the game on. Text example:
What would be the best way to replicate this in Noesis?
Thanks,
R
We’re having a problem replicating in Noesis a feature we had in our old Font Renderer.
In our old system, we could parse a piece of text. Using certain code, we could display a glyph of a controller button (like an emoji). These glyphs would be part of our fonts. The glyphs would change depending on what platform you were playing the game on. Text example:
Code: Select all
Some text here. Here is a controller button: |CROSS|. And another: |SQUARE|
What would be the best way to replicate this in Noesis?
Thanks,
R
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Parse Text - Render Controller Button Glyphs
Are those controller button glyphs from a specific font or images?
You can use TextBlock inlines to build a text containing images like this:
You can use TextBlock inlines to build a text containing images like this:
Re: Parse Text - Render Controller Button Glyphs
If they are glyphs you can also use the fontfallback mechanism, for example:
Code: Select all
TextBlock FontFamily="Fonts/#Tahona, Fonts/#GamepadButtons" />
Re: Parse Text - Render Controller Button Glyphs
Hey team,
We've previously had them as glyphs in the font. However, I'm trying to implement this using sfernandez's approach with inline images. We have them stored in a control template at the moment, so I've written some code to put them in a Content Control.
I was able to get it parsing the text and displaying the image correctly in Blend using C# with the following code:
However, I'm struggling to convert this to C++. I don't know what paTextBlockTooltipEx's base class should be. I thought it may need need to be a Noesis::MarkupExtension but I couldn't find any sample code to point me in the right direction.
This is what I have so far:
Any ideas?
Thanks,
R
We've previously had them as glyphs in the font. However, I'm trying to implement this using sfernandez's approach with inline images. We have them stored in a control template at the moment, so I've written some code to put them in a Content Control.
I was able to get it parsing the text and displaying the image correctly in Blend using C# with the following code:
Code: Select all
namespace Extensions
{
// Source: https://stackoverflow.com/questions/14058814/format-part-of-the-text-of-textblock-using-ivalueconverter
public class paTextBlockTooltipEx
{
public static Inline GetFormattedText(DependencyObject obj)
{
return (Inline)obj.GetValue(FormattedTextProperty);
}
public static void SetFormattedText(DependencyObject obj, Inline value)
{
obj.SetValue(FormattedTextProperty, value);
}
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(Inline),
typeof(paTextBlockTooltipEx),
new PropertyMetadata(null, OnFormattedTextChanged)
);
private static void OnFormattedTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
if (!(o is TextBlock textBlock))
{
return;
}
InlineCollection inlines = textBlock.Inlines;
inlines.Clear();
Inline newInlineValue = (Inline)e.NewValue;
if (newInlineValue != null)
{
inlines.Add(newInlineValue);
}
}
}
public class InlineInputTypeConverter : IValueConverter
{
private static readonly Dictionary<string, TooltipsVM.InputType> SymbolsPaths = new Dictionary<string, TooltipsVM.InputType> {
{ "CROSS", TooltipsVM.InputType.Cross},
};
public TooltipConverter TooltipConverterProp { get; private set; }
public InlineInputTypeConverter()
{
TooltipConverterProp = new TooltipConverter();
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string valueStr = value != null ? (string)value : (string)parameter;
string[] splitStrings = valueStr.Split('|');
Span textBlock = new Span();
InlineCollection inlines = textBlock.Inlines;
foreach(string splitStr in splitStrings)
{
if (SymbolsPaths.ContainsKey(splitStr))
{
var content = new ContentControl();
content.Template = (ControlTemplate)TooltipConverterProp.ConvertInputType(SymbolsPaths[splitStr]);
var viewbox = new Viewbox();
viewbox.Child = content;
viewbox.MaxHeight = 60;
inlines.Add(new InlineUIContainer(viewbox));
}
else
{
inlines.Add(new Run(splitStr));
}
}
return textBlock;
}
}
}
Code: Select all
<TextBlock
Foreground="Black"
paEx:paTextBlockTooltipEx.FormattedText="{Binding ConverterParameter='Cross = |CROSS|',
Converter={StaticResource inlineInputTypeConverter}}"
/>
This is what I have so far:
Code: Select all
class paTextBlockTooltipEx : public Noesis::MarkupExtension
{
public:
static const Noesis::Inline& GetFormattedText(const Noesis::DependencyObject& obj);
static void SetFormattedText(Noesis::DependencyObject* obj, const Noesis::Inline& value);
static const Noesis::DependencyProperty* m_FormattedTextProperty;
virtual Noesis::Ptr<Noesis::BaseComponent> ProvideValue(const Noesis::ValueTargetProvider* provider) override { return nullptr; };
NS_DECLARE_REFLECTION(paTextBlockTooltipEx, Noesis::MarkupExtension)
LUA_BIND_CLASS_HEADER();
private:
static void OnFormattedTextChanged(Noesis::DependencyObject*, const Noesis::DependencyPropertyChangedEventArgs& e);
};
Code: Select all
const Noesis::Inline& paTextBlockTooltipEx::GetFormattedText(const Noesis::DependencyObject& obj)
{
return obj.GetValue<Noesis::Inline>(m_FormattedTextProperty);
}
void paTextBlockTooltipEx::SetFormattedText(Noesis::DependencyObject* obj, const Noesis::Inline& value)
{
obj->SetValue<Noesis::Inline>(m_FormattedTextProperty, value);
}
void paTextBlockTooltipEx::OnFormattedTextChanged(Noesis::DependencyObject* obj, const Noesis::DependencyPropertyChangedEventArgs& e)
{
auto textBlock = (Noesis::TextBlock*)obj;
if (!textBlock)
{
return;
}
auto inlines = textBlock->GetInlines();
if (!inlines)
{
return;
}
inlines->Clear();
const auto newInlineValue = (Noesis::Inline*)e.newValue;
if (!newInlineValue)
{
return;
}
inlines->Add(newInlineValue);
}
NS_BEGIN_COLD_REGION
NS_IMPLEMENT_REFLECTION(paTextBlockTooltipEx)
{
NsMeta("FormattedText", &paTextBlockTooltipEx::m_FormattedTextProperty);
}
Thanks,
R
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Parse Text - Render Controller Button Glyphs
It should be similar to Blend, the class just needs to define the FormattedText dependency property. And looking at your code I think you can simplify it, avoiding the converter part and just writing that conversion inside the attached property changed handler:
Code: Select all
struct paTextBlockTooltipEx
{
static const DependencyProperty* FormattedTextProperty;
static void OnFormattedTextChanged(DependencyObject* d, const DependencyPropertyChangedEventArgs& e)
{
TextBlock* textBlock = DynamicCast<TextBlock*>(d);
if (textBlock != nullptr)
{
InlineCollection* inlines = textBlock->GetInlines();
inlines->Clear();
const String& newValue = *static_cast<const String*>(e.newValue);
// convert value string tokens to the appropriate inline
// and directly add them to the TextBlock.Inlines collection
// ...
}
}
NS_IMPLEMENT_INLINE_REFLECTION(paTextBlockTooltipEx, NoParent, "paEx.paTextBlockTooltipEx")
{
DependencyData* data = NsMeta<DependencyData>(TypeOf<paTextBlockTooltipEx>());
data->RegisterProperty<String>(FormattedTextProperty, "FormattedText",
PropertyMetadata::Create(String(), OnFormattedTextChanged));
}
};
Code: Select all
<TextBlock paEx:paTextBlockTooltipEx.FormattedText="{Binding}"/>
Re: Parse Text - Render Controller Button Glyphs
It says NS_IMPLEMENT_INLINE_REFLECTION only takes two arguments: classType and parentType.
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Parse Text - Render Controller Button Glyphs
Sorry, that code is how it would look in NoesisGUI 3.0, we did a few changes to reflection macros to simplify them.
The third parameter (the type name), corresponds to NsMeta<TypeId> in 2.2 version:
The third parameter (the type name), corresponds to NsMeta<TypeId> in 2.2 version:
Code: Select all
NS_IMPLEMENT_INLINE_REFLECTION(paTextBlockTooltipEx, NoParent)
{
NsMeta<TypeId>("paEx.paTextBlockTooltipEx");
DependencyData* data = NsMeta<DependencyData>(TypeOf<paTextBlockTooltipEx>());
data->RegisterProperty<String>(FormattedTextProperty, "FormattedText",
PropertyMetadata::Create(String(), OnFormattedTextChanged));
}
Re: Parse Text - Render Controller Button Glyphs
I'm still not getting anything to show up in the C++. I must be missing something here.
I'm getting these errors in the output:
So I tried changing it to:
But that doesn't change anything. I still don't see anything and I still get the errors.
I've been reading through this because I think it's the relevant documentation:
https://www.noesisengine.com/docs/Gui.D ... Index.html
Questions I have:
- Does my class have to derive from Dependency Object to use a Dependency Property? I ask because of this line here from the documentation:
- Do I have to construct and/or register the class anywhere? I presume not because it uses a static.
I'm getting these errors in the output:
Code: Select all
ERROR: noesis/Workspace/Views/InlineInputTypeTest.xaml(25): Unknown type 'paNoesisExtensions.paTextBlockTooltipEx'.
Code: Select all
NsMeta<TypeId>("paNoesisExtensions.paTextBlockTooltipEx");
I've been reading through this because I think it's the relevant documentation:
https://www.noesisengine.com/docs/Gui.D ... Index.html
Questions I have:
- Does my class have to derive from Dependency Object to use a Dependency Property? I ask because of this line here from the documentation:
- Do I still need Getters and Setters for my property? Your simplification got rid of them.Your attached property can then be set on any class that derives from DependencyObject.
- Do I have to construct and/or register the class anywhere? I presume not because it uses a static.
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Parse Text - Render Controller Button Glyphs
Hi,
The xaml parser uses the information stored in the Reflection to find available classes and properties.
Hope this helps.
That is correct, the TypeId must be the same you use in xaml.So I tried changing it to: NsMeta<TypeId>("paNoesisExtensions.paTextBlockTooltipEx");
I think you just missed to register the extension class in the Reflection. You can force that by calling TypeOf<T>() when you register the rest of components of your application in the component Factory:But that doesn't change anything. I still don't see anything and I still get the errors.
Code: Select all
void RegisterComponents()
{
TypeOf<paTextBlockTooltipEx>();
NsRegisterComponent<SomeUserControl>();
NsRegisterComponent<OtherControl>();
...
}
If your class only defines attached properties it does not have to inherit from DependencyObject, because those properties won't be set in the class itself, but on other objects.Does my class have to derive from Dependency Object to use a Dependency Property?
Getters and Setters are not required, but you could add them for convenience if you plan to modify the property in code.Do I still need Getters and Setters for my property?
In this case the class (just defines an attached property) does not need to be created anywhere, but as I said before, in order to be available to the xaml parser its reflection must be created and registered. When calling the template function TypeOf it automatically creates the corresponding reflection type and registers it in our Reflection Registry.Do I have to construct and/or register the class anywhere?
Hope this helps.
Re: Parse Text - Render Controller Button Glyphs
You were correct, I had to register it. It's working now. Thank you very much for your help. :)
Do you know of any good documentation for general WPF Extension Classes like this? I fumbled my way through this one but it would be good to read up and have a better idea what I'm doing next time.
Do you know of any good documentation for general WPF Extension Classes like this? I fumbled my way through this one but it would be good to read up and have a better idea what I'm doing next time.
Who is online
Users browsing this forum: Ahrefs [Bot], Semrush [Bot] and 63 guests