4343using namespace std ::chrono_literals;
4444using namespace spdlog ;
4545
46+ static constexpr ImVec4 k_error_color (1 .0f , 0 .0f , 0 .0f , 1 .0f );
4647static constexpr ImVec2 origin (0 , 0 );
4748static void * logo_texture = nullptr ;
4849
@@ -1111,35 +1112,41 @@ void ScreenshotTool::DrawOcrTools()
11111112
11121113 auto refresh_models = [&]() {
11131114 RefreshOcrModels ();
1114- const auto & it = std::find (m_ocr_models_list.begin (), m_ocr_models_list.end (), m_inputs. ocr_model );
1115+ const auto & it = std::find (m_ocr_models_list.begin (), m_ocr_models_list.end (), ocr_model);
11151116 item_selected_idx = (it != m_ocr_models_list.end ()) ? std::distance (m_ocr_models_list.begin (), it) : 0 ;
11161117 };
11171118
1119+ auto push_error_style = [](bool cond) {
1120+ if (cond)
1121+ ImGui::PushStyleColor (ImGuiCol_Text, k_error_color);
1122+ };
1123+ auto pop_error_label = [](bool cond, const char * label) {
1124+ if (cond)
1125+ {
1126+ ImGui::PopStyleColor ();
1127+ ImGui::SameLine ();
1128+ ImGui::TextColored (k_error_color, " %s" , label);
1129+ }
1130+ };
1131+
11181132 ImGui::PushID (" OcrTools" );
11191133 ImGui::SeparatorText (" OCR" );
11201134
1121- // Snapshot of the error for not crashing the program
1122- bool invalid_path = HasError (InvalidPath);
1123- if (invalid_path)
1124- ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (255 , 0 , 0 , 255 ));
1135+ const bool invalid_path = HasError (InvalidPath);
1136+ const bool invalid_model = HasError (InvalidModel);
1137+ const bool has_errors = invalid_path || invalid_model;
11251138
1139+ // --- Path input ---
1140+ push_error_style (invalid_path);
11261141 draw_input_text_folder (" Path" , " ##ocr_path" , refresh_models, ocr_path);
1127-
1128- if (invalid_path)
1129- {
1130- ImGui::SameLine ();
1131- ImGui::Text (" Invalid!" );
1132- ImGui::PopStyleColor ();
1133- }
1142+ pop_error_label (invalid_path, " Invalid path!" );
11341143 ImGui::SameLine ();
1135- HelpMarker (" Full-Path to the OCR models (.traineddata). Supports drag-and-drop too " );
1144+ HelpMarker (" Full path to the OCR models (.traineddata). Supports drag-and-drop" );
11361145
1146+ // --- Model combo (only shown when path is valid) ---
11371147 if (!invalid_path)
11381148 {
1139- bool invalid_model = HasError (InvalidModel);
1140- if (invalid_model)
1141- ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (255 , 0 , 0 , 255 ));
1142-
1149+ push_error_style (invalid_model);
11431150 if (ImGui::BeginCombo (" Model" , ocr_model.c_str (), ImGuiComboFlags_HeightLarge))
11441151 {
11451152 static ImGuiTextFilter filter;
@@ -1154,9 +1161,9 @@ void ScreenshotTool::DrawOcrTools()
11541161
11551162 for (size_t i = 0 ; i < m_ocr_models_list.size (); ++i)
11561163 {
1157- bool is_selected = (item_selected_idx == i);
11581164 if (filter.PassFilter (m_ocr_models_list[i].c_str ()))
11591165 {
1166+ const bool is_selected = (item_selected_idx == i);
11601167 if (ImGui::Selectable (m_ocr_models_list[i].c_str (), is_selected))
11611168 {
11621169 item_selected_idx = i;
@@ -1167,50 +1174,45 @@ void ScreenshotTool::DrawOcrTools()
11671174 }
11681175 ImGui::EndCombo ();
11691176 }
1177+ pop_error_label (invalid_model, " Invalid model!" );
1178+ }
11701179
1171- if (invalid_model)
1180+ // --- Extract button + result details ---
1181+ if (!has_errors)
1182+ {
1183+ if (ImGui::Button (" Extract Text" ))
11721184 {
1173- ImGui::PopStyleColor ();
1174- ImGui::SameLine ();
1175- ImGui::TextColored (ImVec4 (1 , 0 , 0 , 1 ), " Invalid!" );
1185+ const Result<>& res = m_ocr_api.Configure (ocr_path.c_str (), ocr_model.c_str ());
1186+ if (!res.ok ())
1187+ SetError (FailedToScanOcr, res.error_v ());
1188+ else
1189+ {
1190+ ClearError (FailedToScanOcr);
1191+ Result<ocr_result_t > result = m_ocr_api.ExtractTextCapture (GetFinalImage (true ));
1192+ if (result.ok ())
1193+ m_inputs.ocr_results = std::move (result.get ());
1194+ }
11761195 }
1177- }
11781196
1179- if (!HasError (InvalidModel) && !HasError (InvalidPath) && ImGui::Button (" Extract Text" ))
1180- {
1181- const Result<>& res = m_ocr_api.Configure (ocr_path.c_str (), ocr_model.c_str ());
1182- if (!res.ok ())
1197+ if (HasError (FailedToScanOcr))
11831198 {
1184- SetError (FailedToScanOcr, res.error_v ());
1199+ ImGui::SameLine ();
1200+ ImGui::TextColored (k_error_color, " Failed to scan: %s" , GetError (FailedToScanOcr).c_str ());
11851201 }
11861202 else
11871203 {
1188- ClearError (FailedToScanOcr);
1189- Result<ocr_result_t > result = m_ocr_api.ExtractTextCapture (GetFinalImage (true ));
1190- if (result.ok ())
1191- m_inputs.ocr_results = std::move (result.get ());
1204+ ImGui::SameLine ();
1205+ HelpMarker (" If results seem off, try Edit > Optimize OCR for..." );
11921206 }
1193- }
1194-
1195- HelpMarker (" If the result seems off, you could try selecting an option in Edit > Optimize OCR for..." );
11961207
1197- if (HasError (FailedToScanOcr))
1198- {
1199- ImGui::SameLine ();
1200- ImGui::TextColored (ImVec4 (1 .0f , 0 .0f , 0 .0f , 1 .0f ), " Failed to scan image: %s" , GetError (FailedToScanOcr).c_str ());
1201- }
1202-
1203- if (!HasError (InvalidModel) && !HasError (InvalidPath))
1204- {
1205- ImGui::SameLine ();
1206- if (m_inputs.ocr_results .confidence && ImGui::TreeNode (" Details" ))
1208+ const auto & results = m_inputs.ocr_results ;
1209+ if (results.confidence > 0 && ImGui::TreeNode (" Details" ))
12071210 {
1208- ImGui::Text (" Confidence score :" );
1211+ ImGui::Text (" Confidence:" );
12091212 ImGui::SameLine ();
1210- ImGui::TextColored (
1211- get_confidence_color (m_inputs.ocr_results .confidence ), " %d%%" , m_inputs.ocr_results .confidence );
1212-
1213- ImGui::Text (" Using PSM for: %s" , m_inputs.ocr_results .psm_str .c_str ());
1213+ ImGui::TextColored (get_confidence_color (results.confidence ), " %d%%" , results.confidence );
1214+ ImGui::SameLine ();
1215+ ImGui::Text (" PSM: %s" , results.psm_str .c_str ());
12141216 ImGui::TreePop ();
12151217 }
12161218 }
@@ -1220,20 +1222,8 @@ void ScreenshotTool::DrawOcrTools()
12201222 ImVec2 (-1 , ImGui::GetTextLineHeight () * 10 ),
12211223 g_config->File .allow_out_edit ? 0 : ImGuiInputTextFlags_ReadOnly);
12221224
1223- if (m_inputs.ocr_results .data .empty ())
1224- {
1225- ImGui::PopID ();
1226- return ;
1227- }
1228-
1229- if (ImGui::Button (" Copy Text" ))
1230- {
1231- if (m_inputs.ocr_results .data .back () == ' \n ' )
1232- m_inputs.ocr_results .data .pop_back ();
1233- const Result<>& res = g_clipboard->CopyText (m_inputs.ocr_results .data );
1234- if (!res.ok ())
1235- error (" Failed to copy text: {}" , res.error_v ());
1236- }
1225+ if (!m_inputs.ocr_results .data .empty ())
1226+ CreateCopyTextButton (m_inputs.ocr_results .data );
12371227
12381228 ImGui::PopID ();
12391229}
@@ -1252,45 +1242,34 @@ void ScreenshotTool::DrawBarDecodeTools()
12521242 }
12531243 else
12541244 {
1245+ ClearError (FailedToScanBarCode);
12551246 m_inputs.zbar_scan_result = std::move (scan.get ());
1247+ m_inputs.barcode_text .clear ();
12561248 for (const auto & data : m_inputs.zbar_scan_result .datas )
12571249 m_inputs.barcode_text += data + " \n\n " ;
1258- ClearError (FailedToScanBarCode);
12591250 }
12601251 }
12611252
12621253 if (HasError (FailedToScanBarCode))
12631254 {
1264- ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (255 , 0 , 0 , 255 ));
12651255 ImGui::SameLine ();
1266- ImGui::TextColored (
1267- ImVec4 (1 .0f , 0 .0f , 0 .0f , 1 .0f ), " Failed to decode scan: %s" , GetError (FailedToScanBarCode).c_str ());
1268- ImGui::PopStyleColor ();
1256+ ImGui::TextColored (k_error_color, " Failed to decode: %s" , GetError (FailedToScanBarCode).c_str ());
12691257 }
1270- else
1258+ else if (!m_inputs. zbar_scan_result . datas . empty () && ImGui::TreeNode ( " Details " ))
12711259 {
1272- if (!m_inputs.zbar_scan_result .datas .empty () && ImGui::TreeNode (" Details" ))
1273- {
1274- ImGui::Text (" Detected barcodes:" );
1275- for (const auto & [sym, count] : m_inputs.zbar_scan_result .symbologies )
1276- ImGui::BulletText (" %s (x%d)" , sym.c_str (), count);
1277- ImGui::TreePop ();
1278- }
1260+ ImGui::Text (" Detected barcodes:" );
1261+ for (const auto & [sym, count] : m_inputs.zbar_scan_result .symbologies )
1262+ ImGui::BulletText (" %s (x%d)" , sym.c_str (), count);
1263+ ImGui::TreePop ();
12791264 }
12801265
12811266 ImGui::InputTextMultiline (" ##barcode" ,
12821267 &m_inputs.barcode_text ,
12831268 ImVec2 (-1 , ImGui::GetTextLineHeight () * 10 ),
12841269 g_config->File .allow_out_edit ? 0 : ImGuiInputTextFlags_ReadOnly);
12851270
1286- if (!m_inputs.barcode_text .empty () && ImGui::Button (" Copy Text" ))
1287- {
1288- if (m_inputs.barcode_text .back () == ' \n ' )
1289- m_inputs.barcode_text .pop_back ();
1290- const Result<>& res = g_clipboard->CopyText (m_inputs.barcode_text );
1291- if (!res.ok ())
1292- error (" Failed to copy text: {}" , res.error_v ());
1293- }
1271+ if (!m_inputs.barcode_text .empty ())
1272+ CreateCopyTextButton (m_inputs.barcode_text );
12941273
12951274 ImGui::PopID ();
12961275}
@@ -1961,6 +1940,37 @@ ImFont* ScreenshotTool::CacheAndGetFont(const std::string& font_path, const floa
19611940 return font;
19621941}
19631942
1943+ void ScreenshotTool::CreateCopyTextButton (const std::string& text_copy)
1944+ {
1945+ static bool just_copied = false ;
1946+ static double copy_time = 0.0 ;
1947+
1948+ const double now = ImGui::GetTime ();
1949+ if (just_copied && now - copy_time > 1.5 )
1950+ just_copied = false ;
1951+
1952+ if (ImGui::Button (just_copied ? " Copied!" : " Copy Text" ))
1953+ {
1954+ const Result<>& res = g_clipboard->CopyText (text_copy);
1955+ if (!res.ok ())
1956+ {
1957+ SetError (FailedToCopyText, res.error_v ());
1958+ }
1959+ else
1960+ {
1961+ ClearError (FailedToCopyText);
1962+ just_copied = true ;
1963+ copy_time = now;
1964+ }
1965+ }
1966+
1967+ if (HasError (FailedToCopyText))
1968+ {
1969+ ImGui::SameLine ();
1970+ ImGui::TextColored (k_error_color, " Failed to copy text: %s" , GetError (FailedToCopyText).c_str ());
1971+ }
1972+ }
1973+
19641974void ScreenshotTool::RefreshOcrModels ()
19651975{
19661976 m_ocr_models_list = get_training_data_list (m_inputs.ocr_path );
0 commit comments