Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap Viewer/DQT error pop up text #7781

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,18 @@ set(COMMON_SRC
"${CMAKE_CURRENT_LIST_DIR}/reflectivity/reflectivity.cpp"
)

set(UTILITIES
set(UTILITIES_FILES
"${CMAKE_CURRENT_LIST_DIR}/utilities/number/stabilized-value.h"
"${CMAKE_CURRENT_LIST_DIR}/utilities/string/trim-newlines.h"
"${CMAKE_CURRENT_LIST_DIR}/utilities/string/split.h"
"${CMAKE_CURRENT_LIST_DIR}/utilities/imgui/wrap.h"
"${CMAKE_CURRENT_LIST_DIR}/utilities/imgui/wrap.cpp"
)

set(COMMON_SRC
${COMMON_SRC}
${SW_UPDATE_FILES}
${REFLECTIVITY_FILES}
${UTILITIES})
${UTILITIES_FILES}
)

122 changes: 122 additions & 0 deletions common/utilities/imgui/wrap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.

#include <string>
#include <vector>
#include <sstream>
#include "wrap.h"
#include "../common/utilities/string/split.h"
#include "../third-party/imgui/imgui.h"

namespace utilities {
namespace imgui {

void trim_leading_spaces( std::string &remaining_paragraph )
{
auto non_space_index = remaining_paragraph.find_first_not_of( ' ' );

if( non_space_index != std::string::npos )
{
// Remove trailing spaces
remaining_paragraph = remaining_paragraph.substr( non_space_index );
}
}

std::string wrap_paragraph( const std::string & paragraph, int wrap_pixels_width )
{
float space_width = ImGui::CalcTextSize( " " ).x; // Calculate space width in pixels
std::string wrapped_line; // The line that is wrapped in this iteration
std::string wrapped_paragraph; // The output wrapped paragraph
auto remaining_paragraph
= paragraph; // Holds the remaining unwrapped part of the input paragraph

// Handle a case when the paragraph starts with spaces
trim_leading_spaces(remaining_paragraph);

auto next_word = remaining_paragraph.substr(
0,
remaining_paragraph.find( ' ' ) ); // The next word to add to the current line
bool first_word = true;

while( ! next_word.empty() )
{
float next_x = 0.0f;
// If this is the first word we try to place it first in line,
// if not we concatenate it to the last word after adding a space
if( ! first_word )
{
next_x = ImGui::CalcTextSize( wrapped_line.c_str() ).x + space_width;
}

if( next_x + ImGui::CalcTextSize( next_word.c_str() ).x <= wrap_pixels_width )
{
if( ! first_word )
wrapped_line += " "; // First word should not start with " "
wrapped_line += next_word;
}
else
{ // Current line cannot feat new word so we wrap the line and
// start building the new line

wrapped_paragraph += wrapped_line; // copy line build by now
if( ! first_word )
wrapped_paragraph += '\n'; // break the previous line if exist
wrapped_line = next_word; // add next work to new line
}

first_word = false;

// If we have more characters left other then the current word, prepare inputs for next
// iteration, If not add the current line built so far to the output and finish current
// line wrap.
if( remaining_paragraph.size() > next_word.size() )
{
remaining_paragraph = remaining_paragraph.substr( next_word.size() + 1 );

// Handle a case when the paragraph starts with spaces
trim_leading_spaces(remaining_paragraph);

next_word = remaining_paragraph.substr( 0, remaining_paragraph.find( ' ' ) );

// If no more words exist, copy the current wrapped line to output and stop
if( next_word.empty() )
{
wrapped_paragraph += wrapped_line;
break;
}
}
else
{
wrapped_paragraph += wrapped_line;
break;
}
}

return wrapped_paragraph;
}

std::string wrap( const std::string & text, int wrap_pixels_width )
{
// Do not wrap if the wrap is smaller then 32 pixels (~2 characters on font 16)
if( wrap_pixels_width < 32 )
return text;

// Split text into paragraphs
auto paragraphs_vector = string::split( text, '\n' );

std::string wrapped_text;
size_t line_number = 1;
// Wrap each line according to the requested wrap width
for( auto paragraph : paragraphs_vector )
{
wrapped_text += wrap_paragraph( paragraph, wrap_pixels_width );
// Each paragraph except the last one ends with a new line
if( line_number++ != paragraphs_vector.size() )
wrapped_text += '\n';
}

return wrapped_text;
}

} // namespace imgui
} // namespace utilities
23 changes: 23 additions & 0 deletions common/utilities/imgui/wrap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.

#pragma once

namespace utilities {
namespace imgui {

// Wrap text according to input width
// - Input: - text as a string
// - wrapping width
// - Output: - on success - wrapped text
// - on failure - an empty string
// Example:
// Input:
// this is the first line\nthis is the second line\nthis is the last line , wrap_width = 150 [pixels]
// Output:
// this is the\nfirst line\nthis is the\nsecond line\nthis is the last\nline
// Note: If the paragraph contain multiple spaces, it will be trimmed into a single space.
std::string wrap( const std::string & text, int wrap_pixels_width );

} // namespace imgui
} // namespace utilities
38 changes: 38 additions & 0 deletions common/utilities/string/split.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.

#pragma once

#include <string>
#include <vector>
#include <sstream>

namespace utilities {
namespace string {


// Split input text to vector of strings according to input delimiter
// - Input: string to be split
// - delimiter
// - Output: a vector of strings
// Example:
// Input:
// Text: This is the first line\nThis is the second line\nThis is the last line
// Delimiter : '\n'
// Output:
// [0] this is the first line
// [1] this is the second line
// [2] this is the last line
inline std::vector< std::string > split( const std::string & str , char delimiter)
{
auto result = std::vector< std::string >{};
auto ss = std::stringstream{ str };

for( std::string line; std::getline( ss, line, delimiter); )
result.push_back( line );

return result;
}

} // namespace string
} // namespace utilities
19 changes: 18 additions & 1 deletion common/viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#define ARCBALL_CAMERA_IMPLEMENTATION
#include <arcball_camera.h>
#include "../common/utilities/string/trim-newlines.h"
#include "../common/utilities/imgui/wrap.h"

namespace rs2
{
Expand Down Expand Up @@ -1067,9 +1069,24 @@ namespace rs2
auto custom_command = [&]()
{
auto msg = _active_popups.front().message;

// Wrap the text to feet the error pop-up window
std::string wrapped_msg;
try
{
auto trimmed_msg = utilities::string::trim_newlines(msg);
wrapped_msg = utilities::imgui::wrap(trimmed_msg, 500);
}
catch (...)
{
wrapped_msg = msg; // Revert to original text on wrapping failure
not_model->output.add_log(RS2_LOG_SEVERITY_WARN, __FILE__, __LINE__,
to_string() << "Wrapping of error message text failed!");
}

ImGui::Text("RealSense error calling:");
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, regular_blue);
ImGui::InputTextMultiline("##error", const_cast<char*>(msg.c_str()),
ImGui::InputTextMultiline("##error", const_cast<char*>(wrapped_msg.c_str()),
msg.size() + 1, { 500,95 }, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor();

Expand Down
15 changes: 15 additions & 0 deletions unit-tests/utilities/imgui/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.

#define CATCH_CONFIG_MAIN

#include "../../catch.h"

#include <easylogging++.h>
#ifdef BUILD_SHARED_LIBS
// With static linkage, ELPP is initialized by librealsense, so doing it here will
// create errors. When we're using the shared .so/.dll, the two are separate and we have
// to initialize ours if we want to use the APIs!
INITIALIZE_EASYLOGGINGPP
#endif

90 changes: 90 additions & 0 deletions unit-tests/utilities/imgui/test-wrap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.

//#cmake:add-file ../../../common/utilities/imgui/wrap.h
//#cmake:add-file ../../../common/utilities/imgui/wrap.cpp
//#cmake:add-file ../../../third-party/imgui/imgui.h

#include "common.h"
#include "../../../common/utilities/imgui/wrap.h"
#include "../../../third-party/imgui/imgui.h"

namespace ImGui {
// Mock ImGui function for test
// all characters are at length of 10 pixels except 'i' and 'j' which is 5 pixels
ImVec2 CalcTextSize( const char * text,
const char * text_end,
bool hide_text_after_double_hash,
float wrap_width )
{
if (!text) return ImVec2(0.0f, 0.0f);

float total_size = 0.0f;
while( *text )
{
if( *text == 'i' || *text == 'j' )
total_size += 5.0f;
else
total_size += 10.0f;
text++;
}
return ImVec2( total_size, 0.0f );
}
} // namespace ImGui

using namespace utilities::imgui;

TEST_CASE( "wrap-text", "[string]" )
{
// Verify illegal inputs return empty string
CHECK( wrap( "", 0 ) == "" );
CHECK( wrap( "", 10 ) == "" );
CHECK( wrap( "abc", 0 ) == "abc" );
CHECK( wrap( "abc", 10 ) == "abc" );
CHECK( wrap( "abc\nabc", 0 ) == "abc\nabc" );
CHECK( wrap( "abc abc", 5 ) == "abc abc" );
CHECK( wrap( "", 10 ) == "" );

// Verify no wrap if not needed
CHECK( wrap( "abc", 100 ) == "abc" );
CHECK( wrap( "abc\nabc", 100 ) == "abc\nabc" );
CHECK( wrap( "abc abc a", 100 ) == "abc abc a" );

// No wrapping possible, copy line until first space and continue wrapping
CHECK( wrap( "abcdefgh", 40 ) == "abcdefgh" );
CHECK( wrap( "aabbccddff das ds fr", 50 ) == "aabbccddff\ndas\nds fr" );
CHECK( wrap( "das aabbccddff ds fr", 50 ) == "das\naabbccddff\nds fr" );

// Exact wrap position test
CHECK( wrap( "abcde abcde", 50 ) == "abcde\nabcde" );

// Short letters test
CHECK(wrap("aaaa bbbb cc", 100) == "aaaa bbbb\ncc");
// i and j are only 5 pixels so we get more characters inside the wrap
CHECK(wrap("aaaa iijj cc", 100) == "aaaa iijj cc");


// Check wrapping of 3 paragraphs
CHECK( wrap( "this is the first line\nthis is the second line\nthis is the last line", 150 )
== "this is the\nfirst line\nthis is the\nsecond line\nthis is the last\nline" );

CHECK( wrap( "this is the first line\nthis is the second line\nthis is the last line", 60 )
== "this is\nthe\nfirst\nline\nthis is\nthe\nsecond\nline\nthis is\nthe\nlast\nline" );

// Spaces checks
CHECK(wrap("ab cd ", 32) == "ab\ncd"); // Ending spaces
CHECK(wrap("ab cd", 32) == "ab\ncd"); // Middle spaces
CHECK(wrap(" ab cd ", 32) == "ab\ncd"); // Mixed multiple spaces
CHECK(wrap(" ab cd ", 32) == "ab\ncd"); // Mixed multiple spaces
CHECK(wrap(" ab ", 33) == "ab"); // Mixed multiple spaces
CHECK(wrap("ab ", 33) == "ab"); // Ending multiple spaces
CHECK(wrap(" ab", 33) == "ab");

// Only spaces
CHECK(wrap(" ", 33) == "");
CHECK(wrap(" ", 33) == "");

CHECK(wrap("ab cd ", 100) == "ab cd");
CHECK(wrap("ab cd", 100) == "ab cd"); // Known corner case - we trim multiple spaces

}
31 changes: 31 additions & 0 deletions unit-tests/utilities/string/test-split.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.

//#cmake:add-file ../../../common/utilities/string/split.h

#include "common.h"
#include "../../../common/utilities/string/split.h"

using namespace utilities::string;

TEST_CASE("split_string_by_newline", "[string]")
{
// split size check
CHECK(split("" , '\n').size() == 0);
CHECK(split("abc", '\n').size() == 1);
CHECK(split("abc\nabc", '\n').size() == 2);
CHECK(split("a\nbc\nabc", '\n').size() == 3);
CHECK(split("a\nbc\nabc\n", '\n').size() == 3);
CHECK(split("1-12-123-1234", '-').size() == 4);

CHECK(split("a\nbc\nabc", '\n')[0] == "a");
CHECK(split("a\nbc\nabc", '\n')[1] == "bc");
CHECK(split("a\nbc\nabc", '\n')[2] == "abc");
CHECK(split("a\nbc\nabc\n", '\n')[2] == "abc");


CHECK(split("1-12-123-1234", '-')[0] == "1");
CHECK(split("1-12-123-1234", '-')[1] == "12");
CHECK(split("1-12-123-1234", '-')[2] == "123");
CHECK(split("1-12-123-1234", '-')[3] == "1234");
}