From 6282844faca93a8cab6c0ce6f45b33509dbbf73d Mon Sep 17 00:00:00 2001 From: Miklos Marton Date: Tue, 10 Dec 2024 17:15:54 +0100 Subject: [PATCH] Support striketrhoug text style --- CMakeLists.txt | 4 +- cmake/FindCAN_Stack.cmake | 2 +- include/InputNumberComponent.hpp | 9 +- include/InputStringComponent.hpp | 9 +- include/NumberComponent.hpp | 31 +++ include/OutputNumberComponent.hpp | 9 +- include/OutputStringComponent.hpp | 5 +- include/TextDrawingComponent.hpp | 39 ++++ src/InputNumberComponent.cpp | 150 +------------- src/InputStringComponent.cpp | 255 +----------------------- src/NumberComponent.cpp | 73 +++++++ src/OutputNumberComponent.cpp | 146 +------------- src/OutputStringComponent.cpp | 138 +------------ src/TextDrawingComponent.cpp | 315 ++++++++++++++++++++++++++++++ 14 files changed, 483 insertions(+), 702 deletions(-) create mode 100644 include/NumberComponent.hpp create mode 100644 include/TextDrawingComponent.hpp create mode 100644 src/NumberComponent.cpp create mode 100644 src/TextDrawingComponent.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a09c7a4..75ac96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ target_sources( "src/OutputLinearBarGraphComponent.cpp" "src/OutputPolygonComponent.cpp" "src/InputStringComponent.cpp" + "src/NumberComponent.cpp" "src/AlarmMaskAudio.cpp" "src/AppImages.cpp" "src/ASCIILogFile.cpp" @@ -96,7 +97,8 @@ target_sources( "src/ConfigureHardwareComponent.cpp" "src/StringEncodingConversions.cpp" "src/InputListComponent.cpp" - "src/ShortcutsWindow.cpp") + "src/ShortcutsWindow.cpp" + "src/TextDrawingComponent.cpp") target_include_directories(AgISOVirtualTerminal PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/cmake/FindCAN_Stack.cmake b/cmake/FindCAN_Stack.cmake index 0e092c1..eb20bab 100644 --- a/cmake/FindCAN_Stack.cmake +++ b/cmake/FindCAN_Stack.cmake @@ -3,6 +3,6 @@ if(NOT TARGET isobus::isobus) FetchContent_Declare( CAN_Stack GIT_REPOSITORY https://github.com/Open-Agriculture/AgIsoStack-plus-plus.git - GIT_TAG 67e231298f2ca8367d437d903e396aa3bcefe080) + GIT_TAG 4ad80207b5e44d041d8e0fba8ef044367dfbfe86) FetchContent_MakeAvailable(CAN_Stack) endif() diff --git a/include/InputNumberComponent.hpp b/include/InputNumberComponent.hpp index 9bfb236..1d1661f 100644 --- a/include/InputNumberComponent.hpp +++ b/include/InputNumberComponent.hpp @@ -11,22 +11,17 @@ #include "isobus/isobus/isobus_virtual_terminal_objects.hpp" #include "isobus/isobus/isobus_virtual_terminal_server_managed_working_set.hpp" +#include "NumberComponent.hpp" #include "JuceHeader.h" class InputNumberComponent : public isobus::InputNumber - , public Component + , public NumberComponent { public: InputNumberComponent(std::shared_ptr workingSet, isobus::InputNumber sourceObject); - void paint(Graphics &g) override; - - static Justification convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification); - private: - std::shared_ptr parentWorkingSet; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InputNumberComponent) }; diff --git a/include/InputStringComponent.hpp b/include/InputStringComponent.hpp index dbb9c98..8f98e54 100644 --- a/include/InputStringComponent.hpp +++ b/include/InputStringComponent.hpp @@ -11,23 +11,20 @@ #include "isobus/isobus/isobus_virtual_terminal_objects.hpp" #include "isobus/isobus/isobus_virtual_terminal_server_managed_working_set.hpp" +#include "TextDrawingComponent.hpp" #include "JuceHeader.h" class InputStringComponent : public isobus::InputString - , public Component + , public TextDrawingComponent { public: InputStringComponent(std::shared_ptr workingSet, isobus::InputString sourceObject); void paint(Graphics &g) override; - static Justification convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification); - private: - std::shared_ptr parentWorkingSet; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InputStringComponent) }; -#endif // INPUT_STRING_COMPONENT_HPP \ No newline at end of file +#endif // INPUT_STRING_COMPONENT_HPP diff --git a/include/NumberComponent.hpp b/include/NumberComponent.hpp new file mode 100644 index 0000000..458a9ef --- /dev/null +++ b/include/NumberComponent.hpp @@ -0,0 +1,31 @@ +//================================================================================================ +/// @file NumberComponent.hpp +/// +/// @brief Common functions for drawing numbers +/// @author Miklos Marton +/// +/// @copyright 2024 Adrian Del Grosso +//================================================================================================ +#ifndef NUMBER_COMPONENT_HPP +#define NUMBER_COMPONENT_HPP + +#include "isobus/isobus/isobus_virtual_terminal_objects.hpp" +#include "isobus/isobus/isobus_virtual_terminal_server_managed_working_set.hpp" +#include "TextDrawingComponent.hpp" + +#include "JuceHeader.h" + +class NumberComponent : public TextDrawingComponent +{ +public: + NumberComponent(std::shared_ptr workingSet); + + void paint(Graphics &g) override; + +protected: + std::shared_ptr parentWorkingSet; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NumberComponent) +}; + +#endif // NUMBER_COMPONENT_HPP diff --git a/include/OutputNumberComponent.hpp b/include/OutputNumberComponent.hpp index d32fa17..8d8e573 100644 --- a/include/OutputNumberComponent.hpp +++ b/include/OutputNumberComponent.hpp @@ -11,22 +11,17 @@ #include "isobus/isobus/isobus_virtual_terminal_objects.hpp" #include "isobus/isobus/isobus_virtual_terminal_server_managed_working_set.hpp" +#include "NumberComponent.hpp" #include "JuceHeader.h" class OutputNumberComponent : public isobus::OutputNumber - , public Component + , public NumberComponent { public: OutputNumberComponent(std::shared_ptr workingSet, isobus::OutputNumber sourceObject); - void paint(Graphics &g) override; - - static Justification convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification); - private: - std::shared_ptr parentWorkingSet; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OutputNumberComponent) }; diff --git a/include/OutputStringComponent.hpp b/include/OutputStringComponent.hpp index d3539e5..f400f45 100644 --- a/include/OutputStringComponent.hpp +++ b/include/OutputStringComponent.hpp @@ -11,11 +11,12 @@ #include "isobus/isobus/isobus_virtual_terminal_objects.hpp" #include "isobus/isobus/isobus_virtual_terminal_server_managed_working_set.hpp" +#include "TextDrawingComponent.hpp" #include "JuceHeader.h" class OutputStringComponent : public isobus::OutputString - , public Component + , public TextDrawingComponent { public: OutputStringComponent(std::shared_ptr workingSet, isobus::OutputString sourceObject); @@ -25,8 +26,6 @@ class OutputStringComponent : public isobus::OutputString static Justification convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification); private: - std::shared_ptr parentWorkingSet; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OutputStringComponent) }; diff --git a/include/TextDrawingComponent.hpp b/include/TextDrawingComponent.hpp new file mode 100644 index 0000000..c5c3bfc --- /dev/null +++ b/include/TextDrawingComponent.hpp @@ -0,0 +1,39 @@ +//================================================================================================ +/// @file TextDawingComponent.hpp +/// +/// @brief Common functions for drawing numbers +/// @author Miklos Marton +/// +//================================================================================================ +#ifndef TEXTDRAWING_COMPONENT_HPP +#define TEXTDRAWING_COMPONENT_HPP + +#include "isobus/isobus/isobus_virtual_terminal_objects.hpp" +#include "isobus/isobus/isobus_virtual_terminal_server_managed_working_set.hpp" + +#include "JuceHeader.h" + +class TextDrawingComponent : public Component +{ +public: + TextDrawingComponent(std::shared_ptr workingSet); + + void setSourceObject(isobus::VTObject *newSourceObject); + +protected: + static Justification convert_justification(isobus::TextualVTObject::HorizontalJustification horizontalJustification, + isobus::TextualVTObject::VerticalJustification verticalJustification); + uint8_t prepare_text_painting(Graphics &g, + std::shared_ptr font_attributes, + char referenceCharForWidthCalc); + + void paintText(Graphics &g, const std::string &text, bool enabled = true); + std::shared_ptr parentWorkingSet; + isobus::VTObject *sourceObject; + + void drawStrikeThrough(Graphics &g, int w, int h, const String &str, isobus::TextualVTObject::HorizontalJustification justification); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TextDrawingComponent) +}; + +#endif // TEXTDRAWING_COMPONENT_HPP diff --git a/src/InputNumberComponent.cpp b/src/InputNumberComponent.cpp index 015cebb..b9b7a4a 100644 --- a/src/InputNumberComponent.cpp +++ b/src/InputNumberComponent.cpp @@ -10,11 +10,12 @@ InputNumberComponent::InputNumberComponent(std::shared_ptr workingSet, isobus::InputNumber sourceObject) : isobus::InputNumber(sourceObject), - parentWorkingSet(workingSet) + NumberComponent(workingSet) { + setSourceObject(this); setSize(get_width(), get_height()); - if (get_option(Options::Transparent)) + if (get_option(isobus::NumberVTObject::Options::Transparent)) { setOpaque(false); } @@ -24,148 +25,3 @@ InputNumberComponent::InputNumberComponent(std::shared_ptrget_colour(backgroundColor); - g.fillAll(Colour::fromFloatRGBA(vtColour.r, vtColour.g, vtColour.b, 1.0f)); - } - - float scaledValue = (get_value() + get_offset()) * get_scale(); - g.setColour(getLookAndFeel().findColour(ListBox::textColourId)); - - // Get font data - if (isobus::NULL_OBJECT_ID != get_font_attributes()) - { - auto child = get_object_by_id(get_font_attributes(), parentWorkingSet->get_object_tree()); - - if ((nullptr != child) && - (isobus::VirtualTerminalObjectType::FontAttributes == child->get_object_type())) - { - auto font = std::static_pointer_cast(child); - auto colour = parentWorkingSet->get_colour(font->get_colour()); - Font juceFont(Font::getDefaultMonospacedFontName(), font->get_font_height_pixels(), Font::FontStyleFlags::plain); - auto fontWidth = juceFont.getStringWidthFloat("1"); - juceFont.setHorizontalScale(static_cast(font->get_font_width_pixels()) / fontWidth); - g.setColour(Colour::fromFloatRGBA(colour.r, colour.g, colour.b, 1.0f)); - g.setFont(juceFont); - } - } - - if (isobus::NULL_OBJECT_ID != get_variable_reference()) - { - auto child = get_object_by_id(get_variable_reference(), parentWorkingSet->get_object_tree()); - - if ((nullptr != child) && - (isobus::VirtualTerminalObjectType::NumberVariable == child->get_object_type())) - { - scaledValue = (std::static_pointer_cast(child)->get_value() + get_offset()) * get_scale(); - } - } - - std::ostringstream valueText; - valueText << std::fixed << std::setprecision(get_number_of_decimals()) << scaledValue; - g.drawText(valueText.str(), 0, 0, get_width(), get_height(), convert_justification(get_horizontal_justification(), get_vertical_justification()), false); -} - -Justification InputNumberComponent::convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification) -{ - Justification retVal = Justification::topLeft; - - switch (horizontalJustification) - { - case HorizontalJustification::PositionLeft: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::topLeft; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centredLeft; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::bottomLeft; - } - break; - - case VerticalJustification::Reserved: - default: - break; - } - } - break; - - case HorizontalJustification::PositionMiddle: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::centredTop; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centred; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::centredBottom; - } - break; - - case VerticalJustification::Reserved: - default: - break; - } - } - break; - - case HorizontalJustification::PositionRight: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::topRight; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centredRight; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::bottomRight; - } - break; - - case VerticalJustification::Reserved: - default: - break; - } - } - break; - - default: - { - } - break; - } - return retVal; -} diff --git a/src/InputStringComponent.cpp b/src/InputStringComponent.cpp index 6a211a5..082c5c8 100644 --- a/src/InputStringComponent.cpp +++ b/src/InputStringComponent.cpp @@ -9,8 +9,9 @@ InputStringComponent::InputStringComponent(std::shared_ptr workingSet, isobus::InputString sourceObject) : isobus::InputString(sourceObject), - parentWorkingSet(workingSet) + TextDrawingComponent(workingSet) { + setSourceObject(this); setSize(get_width(), get_height()); setOpaque(false); } @@ -18,255 +19,5 @@ InputStringComponent::InputStringComponent(std::shared_ptrget_colour(get_background_color()); - g.fillAll(Colour::fromFloatRGBA(vtColour.r, vtColour.g, vtColour.b, 1.0f)); - } - - g.setColour(getLookAndFeel().findColour(ListBox::textColourId)); - - // Get font data - if (isobus::NULL_OBJECT_ID != get_font_attributes()) - { - auto child = get_object_by_id(get_font_attributes(), parentWorkingSet->get_object_tree()); - - if (nullptr != child) - { - if (isobus::VirtualTerminalObjectType::FontAttributes == child->get_object_type()) - { - auto font = std::static_pointer_cast(child); - fontType = font->get_type(); - - auto colour = parentWorkingSet->get_colour(font->get_colour()); - Font juceFont; - int fontStyleFlags = Font::FontStyleFlags::plain; - - if (font->get_style(isobus::FontAttributes::FontStyleBits::Bold)) - { - fontStyleFlags |= Font::FontStyleFlags::bold; - } - - if (font->get_style(isobus::FontAttributes::FontStyleBits::Italic)) - { - fontStyleFlags |= Font::FontStyleFlags::italic; - } - - if (font->get_style(isobus::FontAttributes::FontStyleBits::Underlined)) - { - fontStyleFlags |= Font::FontStyleFlags::underlined; - } - - juceFont = Font(Font::getDefaultMonospacedFontName(), font->get_font_height_pixels(), fontStyleFlags); - - auto fontWidth = juceFont.getStringWidthFloat("a"); - fontHeight = font->get_font_width_pixels(); - juceFont.setHorizontalScale(static_cast(font->get_font_width_pixels()) / fontWidth); - g.setColour(Colour::fromFloatRGBA(colour.r, colour.g, colour.b, 1.0f)); - g.setFont(juceFont); - } - } - } - - if (isobus::NULL_OBJECT_ID != get_variable_reference()) - { - auto child = get_object_by_id(get_variable_reference(), parentWorkingSet->get_object_tree()); - - if ((nullptr != child) && (isobus::VirtualTerminalObjectType::StringVariable == child->get_object_type())) - { - value = std::static_pointer_cast(child)->get_value(); - } - } - - if (0 == fontHeight) - { - fontHeight = 8; - } - - String decodedValue(value); - - if ((value.length() >= 2) && - (0xFF == static_cast(value.at(0))) && - (0xFE == static_cast(value.at(1)))) - { - // String is UTF-16 encoded. - if (0 != (value.length() % 2)) - { - // If the length attribute does not indicate an even number of bytes the last byte is ignored - value.pop_back(); - } - decodedValue = String::createStringFromData(value.c_str(), value.size()); - } - else - { - switch (fontType) - { - case isobus::FontAttributes::FontType::ISO8859_1: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_1, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_15: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_15, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_2: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_2, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_4: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_4, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_5: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_5, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_7: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_7, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - } - } - - if (get_option(Options::AutoWrap)) // TODO need to figure out proper font clipping - { - g.drawFittedText(decodedValue, 0, 0, get_width(), get_height(), convert_justification(get_horizontal_justification(), get_vertical_justification()), static_cast(std::floor((static_cast(get_height()) + 0.1f) / fontHeight)), 0.8f); - } - else - { - g.drawFittedText(decodedValue, 0, 0, get_width(), get_height(), convert_justification(get_horizontal_justification(), get_vertical_justification()), static_cast(std::floor((static_cast(get_height()) + 0.1f) / fontHeight)), 0.8f); - } - - // If disabled, try and show that by drawing some semi-transparent grey - if (!get_enabled()) - { - g.fillAll(Colour::fromFloatRGBA(0.5f, 0.5f, 0.5f, 0.5f)); - } -} - -Justification InputStringComponent::convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification) -{ - Justification retVal = Justification::topLeft; - - switch (horizontalJustification) - { - case HorizontalJustification::PositionLeft: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::topLeft; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centredLeft; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::bottomLeft; - } - break; - - case VerticalJustification::Reserved: - default: - break; - } - } - break; - - case HorizontalJustification::PositionMiddle: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::centredTop; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centred; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::centredBottom; - } - break; - - case VerticalJustification::Reserved: - default: - break; - } - } - break; - - case HorizontalJustification::PositionRight: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::topRight; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centredRight; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::bottomRight; - } - break; - - case VerticalJustification::Reserved: - default: - break; - } - } - break; - - default: - { - } - break; - } - return retVal; + paintText(g, get_value(), get_enabled()); } diff --git a/src/NumberComponent.cpp b/src/NumberComponent.cpp new file mode 100644 index 0000000..7af9263 --- /dev/null +++ b/src/NumberComponent.cpp @@ -0,0 +1,73 @@ +/******************************************************************************* +** @file NumberComponent.cpp +** @author Miklos Marton +** @copyright The Open-Agriculture Developers +*******************************************************************************/ +#include "NumberComponent.hpp" + +#include +#include + +NumberComponent::NumberComponent( + std::shared_ptr workingSet) : + parentWorkingSet(workingSet), + TextDrawingComponent(workingSet) +{ + +} + +void NumberComponent::paint(Graphics &g) +{ + if (!sourceObject) + { + return; + } + + auto sourceNumber = static_cast(sourceObject); + + if (isOpaque()) + { + auto vtColour = parentWorkingSet->get_colour(sourceNumber->get_background_color()); + g.fillAll(Colour::fromFloatRGBA(vtColour.r, vtColour.g, vtColour.b, 1.0f)); + } + + bool strikeThrough = false; + float scaledValue = (sourceNumber->get_value() + sourceNumber->get_offset()) * sourceNumber->get_scale(); + g.setColour(getLookAndFeel().findColour(ListBox::textColourId)); + + // Get font data + if (isobus::NULL_OBJECT_ID != sourceNumber->get_font_attributes()) + { + auto child = sourceNumber->get_object_by_id(sourceNumber->get_font_attributes(), parentWorkingSet->get_object_tree()); + + if ((nullptr != child) && + (isobus::VirtualTerminalObjectType::FontAttributes == child->get_object_type())) + { + auto font = std::static_pointer_cast(child); + strikeThrough = font->get_style(isobus::FontAttributes::FontStyleBits::CrossedOut); + prepare_text_painting(g, font, '1'); + } + } + + if (isobus::NULL_OBJECT_ID != sourceNumber->get_variable_reference()) + { + auto child = sourceNumber->get_object_by_id(sourceNumber->get_variable_reference(), parentWorkingSet->get_object_tree()); + + if ((nullptr != child) && + (isobus::VirtualTerminalObjectType::NumberVariable == child->get_object_type())) + { + scaledValue = (std::static_pointer_cast(child)->get_value() + sourceNumber->get_offset()) * sourceNumber->get_scale(); + } + } + + std::ostringstream valueText; + valueText << std::fixed << std::setprecision(sourceNumber->get_number_of_decimals()) << scaledValue; + g.drawText(valueText.str(), 0, 0, sourceNumber->get_width(), sourceNumber->get_height(), + convert_justification(sourceNumber->get_horizontal_justification(), sourceNumber->get_vertical_justification()), false); + + if (strikeThrough) + { + drawStrikeThrough(g, sourceNumber->get_width(), sourceNumber->get_height(), valueText.str(), sourceNumber->get_horizontal_justification()); + } +} + diff --git a/src/OutputNumberComponent.cpp b/src/OutputNumberComponent.cpp index 88e0b07..f1a4b7a 100644 --- a/src/OutputNumberComponent.cpp +++ b/src/OutputNumberComponent.cpp @@ -10,11 +10,12 @@ OutputNumberComponent::OutputNumberComponent(std::shared_ptr workingSet, isobus::OutputNumber sourceObject) : isobus::OutputNumber(sourceObject), - parentWorkingSet(workingSet) + NumberComponent(workingSet) { + setSourceObject(this); setSize(get_width(), get_height()); - if (get_option(Options::Transparent)) + if (get_option(isobus::NumberVTObject::Options::Transparent)) { setOpaque(false); } @@ -24,145 +25,4 @@ OutputNumberComponent::OutputNumberComponent(std::shared_ptrget_colour(backgroundColor); - g.fillAll(Colour::fromFloatRGBA(vtColour.r, vtColour.g, vtColour.b, 1.0f)); - } - - float scaledValue = (get_value() + get_offset()) * get_scale(); - g.setColour(getLookAndFeel().findColour(ListBox::textColourId)); - - // Get font data - if (isobus::NULL_OBJECT_ID != get_font_attributes()) - { - auto child = get_object_by_id(get_font_attributes(), parentWorkingSet->get_object_tree()); - - if ((nullptr != child) && (isobus::VirtualTerminalObjectType::FontAttributes == child->get_object_type())) - { - auto font = std::static_pointer_cast(child); - - auto colour = parentWorkingSet->get_colour(font->get_colour()); - Font juceFont(Font::getDefaultMonospacedFontName(), font->get_font_height_pixels(), Font::FontStyleFlags::plain); - auto fontWidth = juceFont.getStringWidthFloat("1"); - juceFont.setHorizontalScale(static_cast(font->get_font_width_pixels()) / fontWidth); - g.setColour(Colour::fromFloatRGBA(colour.r, colour.g, colour.b, 1.0f)); - g.setFont(juceFont); - } - } - - if (isobus::NULL_OBJECT_ID != get_variable_reference()) - { - auto child = get_object_by_id(get_variable_reference(), parentWorkingSet->get_object_tree()); - - if ((nullptr != child) && (isobus::VirtualTerminalObjectType::NumberVariable == child->get_object_type())) - { - std::int64_t offsetValue = static_cast(std::static_pointer_cast(child)->get_value()) + get_offset(); - scaledValue = offsetValue * get_scale(); - } - } - - std::ostringstream valueText; - valueText << std::fixed << std::setprecision(get_number_of_decimals()) << scaledValue; - g.drawText(valueText.str(), 0, 0, get_width(), get_height(), convert_justification(get_horizontal_justification(), get_vertical_justification()), false); -} - -Justification OutputNumberComponent::convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification) -{ - Justification retVal = Justification::topLeft; - - switch (horizontalJustification) - { - case HorizontalJustification::PositionLeft: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::topLeft; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centredLeft; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::bottomLeft; - } - break; - - default: - break; - } - } - break; - - case HorizontalJustification::PositionMiddle: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::centredTop; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centred; - } - break; - case VerticalJustification::PositionBottom: - { - retVal = Justification::centredBottom; - } - break; - - default: - break; - } - } - break; - - case HorizontalJustification::PositionRight: - { - switch (verticalJustification) - { - case VerticalJustification::PositionTop: - { - retVal = Justification::topRight; - } - break; - - case VerticalJustification::PositionMiddle: - { - retVal = Justification::centredRight; - } - break; - - case VerticalJustification::PositionBottom: - { - retVal = Justification::bottomRight; - } - break; - - default: - break; - } - } - break; - - default: - { - } - break; - } - return retVal; -} diff --git a/src/OutputStringComponent.cpp b/src/OutputStringComponent.cpp index c978b2c..ef203ee 100644 --- a/src/OutputStringComponent.cpp +++ b/src/OutputStringComponent.cpp @@ -9,148 +9,16 @@ OutputStringComponent::OutputStringComponent(std::shared_ptr workingSet, isobus::OutputString sourceObject) : isobus::OutputString(sourceObject), - parentWorkingSet(workingSet) + TextDrawingComponent(workingSet) { + setSourceObject(this); setSize(get_width(), get_height()); setOpaque(false); } void OutputStringComponent::paint(Graphics &g) { - std::string value = displayed_value(parentWorkingSet); - std::uint8_t fontHeight = 0; - auto fontType = isobus::FontAttributes::FontType::ISO8859_1; - - if (!get_option(Options::Transparent)) - { - auto vtColour = parentWorkingSet->get_colour(get_background_color()); - g.fillAll(Colour::fromFloatRGBA(vtColour.r, vtColour.g, vtColour.b, 1.0f)); - } - - g.setColour(getLookAndFeel().findColour(ListBox::textColourId)); - - // Get font data - auto fontAttrID = get_font_attributes(); - - if (isobus::NULL_OBJECT_ID != fontAttrID) - { - auto child = get_object_by_id(fontAttrID, parentWorkingSet->get_object_tree()); - - if (isobus::VirtualTerminalObjectType::FontAttributes == child->get_object_type()) - { - auto font = std::static_pointer_cast(child); - - fontType = font->get_type(); - auto colour = parentWorkingSet->get_colour(font->get_colour()); - Font juceFont; - int fontStyleFlags = Font::FontStyleFlags::plain; - - if (font->get_style(isobus::FontAttributes::FontStyleBits::Bold)) - { - fontStyleFlags |= Font::FontStyleFlags::bold; - } - - if (font->get_style(isobus::FontAttributes::FontStyleBits::Italic)) - { - fontStyleFlags |= Font::FontStyleFlags::italic; - } - - if (font->get_style(isobus::FontAttributes::FontStyleBits::Underlined)) - { - fontStyleFlags |= Font::FontStyleFlags::underlined; - } - - juceFont = Font(Font::getDefaultMonospacedFontName(), font->get_font_height_pixels(), fontStyleFlags); - - auto fontWidth = juceFont.getStringWidthFloat("a"); - fontHeight = font->get_font_width_pixels(); - juceFont.setHorizontalScale(static_cast(font->get_font_width_pixels()) / fontWidth); - g.setColour(Colour::fromFloatRGBA(colour.r, colour.g, colour.b, 1.0f)); - g.setFont(juceFont); - } - } - - if (0 == fontHeight) - { - fontHeight = 8; - } - - String decodedValue(value); - - if ((value.length() >= 2) && - (0xFF == static_cast(value.at(0))) && - (0xFE == static_cast(value.at(1)))) - { - // String is UTF-16 encoded, font type is ignored. - if (0 != (value.length() % 2)) - { - // If the length attribute does not indicate an even number of bytes the last byte is ignored - value.pop_back(); - } - decodedValue = String::createStringFromData(value.c_str(), value.size()); - } - else - { - switch (fontType) - { - case isobus::FontAttributes::FontType::ISO8859_1: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_1, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_15: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_15, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_2: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_2, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_4: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_4, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_5: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_5, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - - case isobus::FontAttributes::FontType::ISO8859_7: - { - std::string utf8String; - convert_string_to_utf_8(SourceEncoding::ISO8859_7, value, utf8String, get_option(Options::AutoWrap)); - decodedValue = utf8String; - } - break; - } - } - - if (get_option(Options::AutoWrap)) // TODO need to figure out proper font clipping - { - g.drawFittedText(decodedValue, 0, 0, get_width(), get_height(), convert_justification(get_horizontal_justification(), get_vertical_justification()), static_cast(std::floor((static_cast(get_height()) + 0.1f) / fontHeight)), 0.8f); - } - else - { - g.drawFittedText(decodedValue, 0, 0, get_width(), get_height(), convert_justification(get_horizontal_justification(), get_vertical_justification()), static_cast(std::floor((static_cast(get_height()) + 0.1f) / fontHeight)), 0.8f); - } + paintText(g, displayed_value(parentWorkingSet)); } Justification OutputStringComponent::convert_justification(HorizontalJustification horizontalJustification, VerticalJustification verticalJustification) diff --git a/src/TextDrawingComponent.cpp b/src/TextDrawingComponent.cpp new file mode 100644 index 0000000..87c47d0 --- /dev/null +++ b/src/TextDrawingComponent.cpp @@ -0,0 +1,315 @@ +/******************************************************************************* +** @file NumberComponent.cpp +** @author Miklos Marton +** @copyright The Open-Agriculture Developers +*******************************************************************************/ +#include "TextDrawingComponent.hpp" + +#include "StringEncodingConversions.hpp" + +#include +#include + +TextDrawingComponent::TextDrawingComponent( + std::shared_ptr workingSet) : + parentWorkingSet(workingSet) +{ +} + +void TextDrawingComponent::setSourceObject(isobus::VTObject *newSourceObject) +{ + sourceObject = newSourceObject; +} + +Justification TextDrawingComponent::convert_justification( + isobus::TextualVTObject::HorizontalJustification horizontalJustification, + isobus::TextualVTObject::VerticalJustification verticalJustification) +{ + Justification retVal = Justification::topLeft; + + switch (horizontalJustification) + { + case isobus::TextualVTObject::HorizontalJustification::PositionLeft: + { + switch (verticalJustification) + { + case isobus::TextualVTObject::VerticalJustification::PositionTop: + { + retVal = Justification::topLeft; + } + break; + + case isobus::TextualVTObject::VerticalJustification::PositionMiddle: + { + retVal = Justification::centredLeft; + } + break; + + case isobus::TextualVTObject::VerticalJustification::PositionBottom: + { + retVal = Justification::bottomLeft; + } + break; + + case isobus::TextualVTObject::VerticalJustification::Reserved: + default: + break; + } + } + break; + + case isobus::TextualVTObject::HorizontalJustification::PositionMiddle: + { + switch (verticalJustification) + { + case isobus::TextualVTObject::VerticalJustification::PositionTop: + { + retVal = Justification::centredTop; + } + break; + + case isobus::TextualVTObject::VerticalJustification::PositionMiddle: + { + retVal = Justification::centred; + } + break; + + case isobus::TextualVTObject::VerticalJustification::PositionBottom: + { + retVal = Justification::centredBottom; + } + break; + + case isobus::TextualVTObject::VerticalJustification::Reserved: + default: + break; + } + } + break; + + case isobus::TextualVTObject::HorizontalJustification::PositionRight: + { + switch (verticalJustification) + { + case isobus::TextualVTObject::VerticalJustification::PositionTop: + { + retVal = Justification::topRight; + } + break; + + case isobus::TextualVTObject::VerticalJustification::PositionMiddle: + { + retVal = Justification::centredRight; + } + break; + + case isobus::TextualVTObject::VerticalJustification::PositionBottom: + { + retVal = Justification::bottomRight; + } + break; + + case isobus::TextualVTObject::VerticalJustification::Reserved: + default: + break; + } + } + break; + + default: + { + } + break; + } + return retVal; +} + +std::uint8_t TextDrawingComponent::prepare_text_painting(Graphics &g, + std::shared_ptr font, + char referenceCharForWidthCalc) +{ + Font juceFont = Font(Font::getDefaultMonospacedFontName(), 0, 0); + std::uint8_t fontHeight; + auto fontType = font->get_type(); + auto colour = parentWorkingSet->get_colour(font->get_colour()); + int fontStyleFlags = Font::FontStyleFlags::plain; + + if (font->get_style(isobus::FontAttributes::FontStyleBits::Bold)) + { + fontStyleFlags |= Font::FontStyleFlags::bold; + } + + if (font->get_style(isobus::FontAttributes::FontStyleBits::Italic)) + { + fontStyleFlags |= Font::FontStyleFlags::italic; + } + + if (font->get_style(isobus::FontAttributes::FontStyleBits::Underlined)) + { + fontStyleFlags |= Font::FontStyleFlags::underlined; + } + + juceFont.setStyleFlags(fontStyleFlags); + juceFont.setHeight(font->get_font_height_pixels()); + + auto fontWidth = juceFont.getStringWidthFloat(juce::String::fromUTF8(&referenceCharForWidthCalc, 1)); + fontHeight = font->get_font_width_pixels(); + if (fontHeight == 0) + { + fontHeight = 8; + } + juceFont.setHorizontalScale(static_cast(font->get_font_width_pixels()) / fontWidth); + + g.setColour(Colour::fromFloatRGBA(colour.r, colour.g, colour.b, 1.0f)); + g.setFont(juceFont); + + return fontHeight; +} + +void TextDrawingComponent::drawStrikeThrough(Graphics &g, int w, int h, const String &str, isobus::TextualVTObject::HorizontalJustification justification) +{ + auto font = g.getCurrentFont(); + auto textWidth = font.getStringWidth(str); + auto lineThickness = h * 0.05; // set the thickness to 5% of the total text height + if (lineThickness < 1.0) { + lineThickness = 1.0; + } + + auto y = h / 2; + + switch (justification) + { + case isobus::TextualVTObject::HorizontalJustification::PositionLeft: + g.drawLine(0, y, textWidth, y, lineThickness); + break; + case isobus::TextualVTObject::HorizontalJustification::PositionMiddle: + g.drawLine(w / 2 - textWidth / 2, y, w / 2 + textWidth / 2, y, lineThickness); + break; + case isobus::TextualVTObject::HorizontalJustification::PositionRight: + g.drawLine(w - textWidth, y, w, y, lineThickness); + break; + default: + break; + } +} + +void TextDrawingComponent::paintText(Graphics &g, const std::string &text, bool enabled) +{ + std::string value = text; + std::uint8_t fontHeight = 8; + bool strikeThrough = false; + auto fontType = isobus::FontAttributes::FontType::ISO8859_1; + auto sourceString = static_cast(sourceObject); + + if (!sourceString->get_option(isobus::StringVTObject::Options::Transparent)) + { + auto vtColour = parentWorkingSet->get_colour(sourceString->get_background_color()); + g.fillAll(Colour::fromFloatRGBA(vtColour.r, vtColour.g, vtColour.b, 1.0f)); + } + + g.setColour(getLookAndFeel().findColour(ListBox::textColourId)); + + // Get font data + auto fontAttrID = sourceString->get_font_attributes(); + + if (isobus::NULL_OBJECT_ID != fontAttrID) + { + auto child = sourceString->get_object_by_id(fontAttrID, parentWorkingSet->get_object_tree()); + + if (child != nullptr && isobus::VirtualTerminalObjectType::FontAttributes == child->get_object_type()) + { + auto font = std::static_pointer_cast(child); + fontHeight = prepare_text_painting(g, font, 'a'); + strikeThrough = font->get_style(isobus::FontAttributes::FontStyleBits::CrossedOut); + } + } + + String decodedValue(value); + + if ((value.length() >= 2) && + (0xFF == static_cast(value.at(0))) && + (0xFE == static_cast(value.at(1)))) + { + // String is UTF-16 encoded, font type is ignored. + if (0 != (value.length() % 2)) + { + // If the length attribute does not indicate an even number of bytes the last byte is ignored + value.pop_back(); + } + decodedValue = String::createStringFromData(value.c_str(), value.size()); + } + else + { + switch (fontType) + { + case isobus::FontAttributes::FontType::ISO8859_1: + { + std::string utf8String; + convert_string_to_utf_8(SourceEncoding::ISO8859_1, value, utf8String, sourceString->get_option(isobus::StringVTObject::Options::AutoWrap)); + decodedValue = utf8String; + } + break; + + case isobus::FontAttributes::FontType::ISO8859_15: + { + std::string utf8String; + convert_string_to_utf_8(SourceEncoding::ISO8859_15, value, utf8String, sourceString->get_option(isobus::StringVTObject::Options::AutoWrap)); + decodedValue = utf8String; + } + break; + + case isobus::FontAttributes::FontType::ISO8859_2: + { + std::string utf8String; + convert_string_to_utf_8(SourceEncoding::ISO8859_2, value, utf8String, sourceString->get_option(isobus::StringVTObject::Options::AutoWrap)); + decodedValue = utf8String; + } + break; + + case isobus::FontAttributes::FontType::ISO8859_4: + { + std::string utf8String; + convert_string_to_utf_8(SourceEncoding::ISO8859_4, value, utf8String, sourceString->get_option(isobus::StringVTObject::Options::AutoWrap)); + decodedValue = utf8String; + } + break; + + case isobus::FontAttributes::FontType::ISO8859_5: + { + std::string utf8String; + convert_string_to_utf_8(SourceEncoding::ISO8859_5, value, utf8String, sourceString->get_option(isobus::StringVTObject::Options::AutoWrap)); + decodedValue = utf8String; + } + break; + + case isobus::FontAttributes::FontType::ISO8859_7: + { + std::string utf8String; + convert_string_to_utf_8(SourceEncoding::ISO8859_7, value, utf8String, sourceString->get_option(isobus::StringVTObject::Options::AutoWrap)); + decodedValue = utf8String; + } + break; + } + } + + if (sourceString->get_option(isobus::StringVTObject::Options::AutoWrap)) // TODO need to figure out proper font clipping + { + g.drawFittedText(decodedValue, 0, 0, sourceString->get_width(), sourceString->get_height(), convert_justification(sourceString->get_horizontal_justification(), sourceString->get_vertical_justification()), static_cast(std::floor((static_cast(sourceString->get_height()) + 0.1f) / fontHeight)), 0.8f); + } + else + { + g.drawFittedText(decodedValue, 0, 0, sourceString->get_width(), sourceString->get_height(), convert_justification(sourceString->get_horizontal_justification(), sourceString->get_vertical_justification()), static_cast(std::floor((static_cast(sourceString->get_height()) + 0.1f) / fontHeight)), 0.8f); + } + + if (strikeThrough) + { + // Juce does not support Strikethrough text drawing, draw the line manually + drawStrikeThrough(g, sourceString->get_width(), sourceString->get_height(), decodedValue, sourceString->get_horizontal_justification()); + } + + // If disabled, try and show that by drawing some semi-transparent grey + if (!enabled) + { + g.fillAll(Colour::fromFloatRGBA(0.5f, 0.5f, 0.5f, 0.5f)); + } +}