UI: Relocate tas menu and add brief description
This commit is contained in:
		| @@ -117,7 +117,7 @@ struct InputSubsystem::Impl { | |||||||
|             Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}}, |             Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}}, | ||||||
|         }; |         }; | ||||||
|         if (Settings::values.tas_enable) { |         if (Settings::values.tas_enable) { | ||||||
|             devices.push_back( |             devices.emplace_back( | ||||||
|                 Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}}); |                 Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}}); | ||||||
|         } |         } | ||||||
| #ifdef HAVE_SDL2 | #ifdef HAVE_SDL2 | ||||||
|   | |||||||
| @@ -40,12 +40,15 @@ constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_but | |||||||
|  |  | ||||||
| Tas::Tas() { | Tas::Tas() { | ||||||
|     if (!Settings::values.tas_enable) { |     if (!Settings::values.tas_enable) { | ||||||
|  |         needs_reset = true; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     LoadTasFiles(); |     LoadTasFiles(); | ||||||
| } | } | ||||||
|  |  | ||||||
| Tas::~Tas() = default; | Tas::~Tas() { | ||||||
|  |     Stop(); | ||||||
|  | }; | ||||||
|  |  | ||||||
| void Tas::LoadTasFiles() { | void Tas::LoadTasFiles() { | ||||||
|     script_length = 0; |     script_length = 0; | ||||||
| @@ -184,6 +187,9 @@ std::string Tas::ButtonsToString(u32 button) const { | |||||||
|  |  | ||||||
| void Tas::UpdateThread() { | void Tas::UpdateThread() { | ||||||
|     if (!Settings::values.tas_enable) { |     if (!Settings::values.tas_enable) { | ||||||
|  |         if (is_running) { | ||||||
|  |             Stop(); | ||||||
|  |         } | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -196,34 +202,35 @@ void Tas::UpdateThread() { | |||||||
|         LoadTasFiles(); |         LoadTasFiles(); | ||||||
|         LOG_DEBUG(Input, "tas_reset done"); |         LOG_DEBUG(Input, "tas_reset done"); | ||||||
|     } |     } | ||||||
|     if (is_running) { |  | ||||||
|         if (current_command < script_length) { |     if (!is_running) { | ||||||
|             LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length); |         tas_data.fill({}); | ||||||
|             size_t frame = current_command++; |         return; | ||||||
|             for (size_t i = 0; i < commands.size(); i++) { |     } | ||||||
|                 if (frame < commands[i].size()) { |     if (current_command < script_length) { | ||||||
|                     TASCommand command = commands[i][frame]; |         LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length); | ||||||
|                     tas_data[i].buttons = command.buttons; |         size_t frame = current_command++; | ||||||
|                     auto [l_axis_x, l_axis_y] = command.l_axis; |         for (size_t i = 0; i < commands.size(); i++) { | ||||||
|                     tas_data[i].axis[0] = l_axis_x; |             if (frame < commands[i].size()) { | ||||||
|                     tas_data[i].axis[1] = l_axis_y; |                 TASCommand command = commands[i][frame]; | ||||||
|                     auto [r_axis_x, r_axis_y] = command.r_axis; |                 tas_data[i].buttons = command.buttons; | ||||||
|                     tas_data[i].axis[2] = r_axis_x; |                 auto [l_axis_x, l_axis_y] = command.l_axis; | ||||||
|                     tas_data[i].axis[3] = r_axis_y; |                 tas_data[i].axis[0] = l_axis_x; | ||||||
|                 } else { |                 tas_data[i].axis[1] = l_axis_y; | ||||||
|                     tas_data[i] = {}; |                 auto [r_axis_x, r_axis_y] = command.r_axis; | ||||||
|                 } |                 tas_data[i].axis[2] = r_axis_x; | ||||||
|             } |                 tas_data[i].axis[3] = r_axis_y; | ||||||
|         } else { |             } else { | ||||||
|             is_running = Settings::values.tas_loop.GetValue(); |                 tas_data[i] = {}; | ||||||
|             current_command = 0; |  | ||||||
|             tas_data.fill({}); |  | ||||||
|             if (!is_running) { |  | ||||||
|                 SwapToStoredController(); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|  |         is_running = Settings::values.tas_loop.GetValue(); | ||||||
|  |         current_command = 0; | ||||||
|         tas_data.fill({}); |         tas_data.fill({}); | ||||||
|  |         if (!is_running) { | ||||||
|  |             SwapToStoredController(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data)); |     LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data)); | ||||||
| } | } | ||||||
| @@ -237,8 +244,8 @@ TasAnalog Tas::ReadCommandAxis(const std::string& line) const { | |||||||
|         seglist.push_back(segment); |         seglist.push_back(segment); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const float x = std::stof(seglist.at(0)) / 32767.f; |     const float x = std::stof(seglist.at(0)) / 32767.0f; | ||||||
|     const float y = std::stof(seglist.at(1)) / 32767.f; |     const float y = std::stof(seglist.at(1)) / 32767.0f; | ||||||
|  |  | ||||||
|     return {x, y}; |     return {x, y}; | ||||||
| } | } | ||||||
| @@ -293,12 +300,20 @@ std::string Tas::WriteCommandButtons(u32 data) const { | |||||||
| } | } | ||||||
|  |  | ||||||
| void Tas::StartStop() { | void Tas::StartStop() { | ||||||
|     is_running = !is_running; |     if (!Settings::values.tas_enable) { | ||||||
|     if (is_running) { |         return; | ||||||
|         SwapToTasController(); |  | ||||||
|     } else { |  | ||||||
|         SwapToStoredController(); |  | ||||||
|     } |     } | ||||||
|  |     if (is_running) { | ||||||
|  |         Stop(); | ||||||
|  |     } else { | ||||||
|  |         is_running = true; | ||||||
|  |         SwapToTasController(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Tas::Stop() { | ||||||
|  |     is_running = false; | ||||||
|  |     SwapToStoredController(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Tas::SwapToTasController() { | void Tas::SwapToTasController() { | ||||||
| @@ -315,7 +330,8 @@ void Tas::SwapToTasController() { | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         auto tas_param = Common::ParamPackage{{"pad", static_cast<u8>(index)}}; |         Common::ParamPackage tas_param; | ||||||
|  |         tas_param.Set("pad", static_cast<u8>(index)); | ||||||
|         auto button_mapping = GetButtonMappingForDevice(tas_param); |         auto button_mapping = GetButtonMappingForDevice(tas_param); | ||||||
|         auto analog_mapping = GetAnalogMappingForDevice(tas_param); |         auto analog_mapping = GetAnalogMappingForDevice(tas_param); | ||||||
|         auto& buttons = player.buttons; |         auto& buttons = player.buttons; | ||||||
| @@ -328,25 +344,33 @@ void Tas::SwapToTasController() { | |||||||
|             analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize(); |             analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     is_old_input_saved = true; | ||||||
|     Settings::values.is_device_reload_pending.store(true); |     Settings::values.is_device_reload_pending.store(true); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Tas::SwapToStoredController() { | void Tas::SwapToStoredController() { | ||||||
|     if (!Settings::values.tas_swap_controllers) { |     if (!is_old_input_saved) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     auto& players = Settings::values.players.GetValue(); |     auto& players = Settings::values.players.GetValue(); | ||||||
|     for (std::size_t index = 0; index < players.size(); index++) { |     for (std::size_t index = 0; index < players.size(); index++) { | ||||||
|         players[index] = player_mappings[index]; |         players[index] = player_mappings[index]; | ||||||
|     } |     } | ||||||
|  |     is_old_input_saved = false; | ||||||
|     Settings::values.is_device_reload_pending.store(true); |     Settings::values.is_device_reload_pending.store(true); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Tas::Reset() { | void Tas::Reset() { | ||||||
|  |     if (!Settings::values.tas_enable) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|     needs_reset = true; |     needs_reset = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool Tas::Record() { | bool Tas::Record() { | ||||||
|  |     if (!Settings::values.tas_enable) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|     is_recording = !is_recording; |     is_recording = !is_recording; | ||||||
|     return is_recording; |     return is_recording; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,8 +13,8 @@ | |||||||
|  |  | ||||||
| /* | /* | ||||||
| To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below | To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below | ||||||
| Emulation -> Configure TAS. The file itself has normal text format and has to be called | Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt | ||||||
| script0-1.txt for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players). | for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players). | ||||||
|  |  | ||||||
| A script file has the same format as TAS-nx uses, so final files will look like this: | A script file has the same format as TAS-nx uses, so final files will look like this: | ||||||
|  |  | ||||||
| @@ -56,26 +56,26 @@ enum class TasState { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| enum class TasButton : u32 { | enum class TasButton : u32 { | ||||||
|     BUTTON_A = 0x000001, |     BUTTON_A = 1U << 0, | ||||||
|     BUTTON_B = 0x000002, |     BUTTON_B = 1U << 1, | ||||||
|     BUTTON_X = 0x000004, |     BUTTON_X = 1U << 2, | ||||||
|     BUTTON_Y = 0x000008, |     BUTTON_Y = 1U << 3, | ||||||
|     STICK_L = 0x000010, |     STICK_L = 1U << 4, | ||||||
|     STICK_R = 0x000020, |     STICK_R = 1U << 5, | ||||||
|     TRIGGER_L = 0x000040, |     TRIGGER_L = 1U << 6, | ||||||
|     TRIGGER_R = 0x000080, |     TRIGGER_R = 1U << 7, | ||||||
|     TRIGGER_ZL = 0x000100, |     TRIGGER_ZL = 1U << 8, | ||||||
|     TRIGGER_ZR = 0x000200, |     TRIGGER_ZR = 1U << 9, | ||||||
|     BUTTON_PLUS = 0x000400, |     BUTTON_PLUS = 1U << 10, | ||||||
|     BUTTON_MINUS = 0x000800, |     BUTTON_MINUS = 1U << 11, | ||||||
|     BUTTON_LEFT = 0x001000, |     BUTTON_LEFT = 1U << 12, | ||||||
|     BUTTON_UP = 0x002000, |     BUTTON_UP = 1U << 13, | ||||||
|     BUTTON_RIGHT = 0x004000, |     BUTTON_RIGHT = 1U << 14, | ||||||
|     BUTTON_DOWN = 0x008000, |     BUTTON_DOWN = 1U << 15, | ||||||
|     BUTTON_SL = 0x010000, |     BUTTON_SL = 1U << 16, | ||||||
|     BUTTON_SR = 0x020000, |     BUTTON_SR = 1U << 17, | ||||||
|     BUTTON_HOME = 0x040000, |     BUTTON_HOME = 1U << 18, | ||||||
|     BUTTON_CAPTURE = 0x080000, |     BUTTON_CAPTURE = 1U << 19, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum class TasAxes : u8 { | enum class TasAxes : u8 { | ||||||
| @@ -105,6 +105,9 @@ public: | |||||||
|     //  Sets the flag to start or stop the TAS command excecution and swaps controllers profiles |     //  Sets the flag to start or stop the TAS command excecution and swaps controllers profiles | ||||||
|     void StartStop(); |     void StartStop(); | ||||||
|  |  | ||||||
|  |     //  Stop the TAS and reverts any controller profile | ||||||
|  |     void Stop(); | ||||||
|  |  | ||||||
|     // Sets the flag to reload the file and start from the begining in the next update |     // Sets the flag to reload the file and start from the begining in the next update | ||||||
|     void Reset(); |     void Reset(); | ||||||
|  |  | ||||||
| @@ -219,6 +222,7 @@ private: | |||||||
|  |  | ||||||
|     size_t script_length{0}; |     size_t script_length{0}; | ||||||
|     std::array<TasData, PLAYER_NUMBER> tas_data; |     std::array<TasData, PLAYER_NUMBER> tas_data; | ||||||
|  |     bool is_old_input_saved{false}; | ||||||
|     bool is_recording{false}; |     bool is_recording{false}; | ||||||
|     bool is_running{false}; |     bool is_running{false}; | ||||||
|     bool needs_reset{false}; |     bool needs_reset{false}; | ||||||
|   | |||||||
| @@ -55,7 +55,7 @@ void ConfigureTasDialog::SetDirectory(DirectoryTarget target, QLineEdit* edit) { | |||||||
|  |  | ||||||
|     QString str = QFileDialog::getExistingDirectory(this, caption, edit->text()); |     QString str = QFileDialog::getExistingDirectory(this, caption, edit->text()); | ||||||
|  |  | ||||||
|     if (str.isNull() || str.isEmpty()) { |     if (str.isEmpty()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,50 @@ | |||||||
|           <item> |           <item> | ||||||
|             <widget class="QGroupBox" name="groupBox"> |             <widget class="QGroupBox" name="groupBox"> | ||||||
|               <property name="title"> |               <property name="title"> | ||||||
|                 <string>TAS Settings</string> |                 <string>TAS</string> | ||||||
|  |               </property> | ||||||
|  |               <layout class="QGridLayout" name="gridLayout"> | ||||||
|  |                 <item row="0" column="0" colspan="1"> | ||||||
|  |                   <widget class="QLabel" name="label_1"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>Reads controller input from scripts in the same format as TAS-nx scripts. For a more detailed explanation please consult the FAQ on the yuzu website.</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="wordWrap"> | ||||||
|  |                       <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item row="1" column="0" colspan="1"> | ||||||
|  |                   <widget class="QLabel" name="label_2"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>To check which hotkeys control the playback/recording, please refer to the Hotkey settings (General -> Hotkeys).</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="wordWrap"> | ||||||
|  |                       <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item row="2" column="0" colspan="1"> | ||||||
|  |                   <widget class="QLabel" name="label_2"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>WARNING: This is an experimental feature. It will not play back scripts frame perfectly with the current, imperfect syncing method.</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="wordWrap"> | ||||||
|  |                       <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </widget> | ||||||
|  |           </item> | ||||||
|  |         </layout> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |         <layout class="QHBoxLayout" name="horizontalLayout"> | ||||||
|  |           <item> | ||||||
|  |             <widget class="QGroupBox" name="groupBox"> | ||||||
|  |               <property name="title"> | ||||||
|  |                 <string>Settings</string> | ||||||
|               </property> |               </property> | ||||||
|               <layout class="QGridLayout" name="gridLayout"> |               <layout class="QGridLayout" name="gridLayout"> | ||||||
|                 <item row="0" column="0" colspan="4"> |                 <item row="0" column="0" colspan="4"> | ||||||
| @@ -63,7 +106,7 @@ | |||||||
|           <item> |           <item> | ||||||
|             <widget class="QGroupBox" name="groupBox"> |             <widget class="QGroupBox" name="groupBox"> | ||||||
|               <property name="title"> |               <property name="title"> | ||||||
|                 <string>TAS Directories</string> |                 <string>Script Directory</string> | ||||||
|               </property> |               </property> | ||||||
|               <layout class="QGridLayout" name="gridLayout"> |               <layout class="QGridLayout" name="gridLayout"> | ||||||
|                 <item row="0" column="0"> |                 <item row="0" column="0"> | ||||||
|   | |||||||
| @@ -99,7 +99,7 @@ void ConfigureVibration::SetVibrationDevices(std::size_t player_index) { | |||||||
|         const auto guid = param.Get("guid", ""); |         const auto guid = param.Get("guid", ""); | ||||||
|         const auto port = param.Get("port", ""); |         const auto port = param.Get("port", ""); | ||||||
|  |  | ||||||
|         if (engine.empty() || engine == "keyboard" || engine == "mouse") { |         if (engine.empty() || engine == "keyboard" || engine == "mouse" || engine == "tas") { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -78,7 +78,7 @@ void ControllerDialog::InputController(ControllerInput input) { | |||||||
|     u32 buttons = 0; |     u32 buttons = 0; | ||||||
|     int index = 0; |     int index = 0; | ||||||
|     for (bool btn : input.button_values) { |     for (bool btn : input.button_values) { | ||||||
|         buttons += (btn ? 1 : 0) << index; |         buttons |= (btn ? 1U : 0U) << index; | ||||||
|         index++; |         index++; | ||||||
|     } |     } | ||||||
|     input_subsystem->GetTas()->RecordInput(buttons, input.axis_values); |     input_subsystem->GetTas()->RecordInput(buttons, input.axis_values); | ||||||
|   | |||||||
| @@ -1022,18 +1022,25 @@ void GMainWindow::InitializeHotkeys() { | |||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Start/Stop"), this), |     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Start/Stop"), this), | ||||||
|             &QShortcut::activated, this, [&] { input_subsystem->GetTas()->StartStop(); }); |             &QShortcut::activated, this, [&] { | ||||||
|  |                 if (!emulation_running) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 input_subsystem->GetTas()->StartStop(); | ||||||
|  |             }); | ||||||
|     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Reset"), this), |     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Reset"), this), | ||||||
|             &QShortcut::activated, this, [&] { input_subsystem->GetTas()->Reset(); }); |             &QShortcut::activated, this, [&] { input_subsystem->GetTas()->Reset(); }); | ||||||
|     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Record"), this), |     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Record"), this), | ||||||
|             &QShortcut::activated, this, [&] { |             &QShortcut::activated, this, [&] { | ||||||
|  |                 if (!emulation_running) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|                 bool is_recording = input_subsystem->GetTas()->Record(); |                 bool is_recording = input_subsystem->GetTas()->Record(); | ||||||
|                 if (!is_recording) { |                 if (!is_recording) { | ||||||
|                     QMessageBox::StandardButton reply; |                     const auto res = QMessageBox::question(this, tr("TAS Recording"), | ||||||
|                     reply = QMessageBox::question(this, tr("TAS Recording"), |                                                            tr("Overwrite file of player 1?"), | ||||||
|                                                   tr("Overwrite file of player 1?"), |                                                            QMessageBox::Yes | QMessageBox::No); | ||||||
|                                                   QMessageBox::Yes | QMessageBox::No); |                     input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); | ||||||
|                     input_subsystem->GetTas()->SaveRecording(reply == QMessageBox::Yes); |  | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
| } | } | ||||||
| @@ -1487,6 +1494,8 @@ void GMainWindow::ShutdownGame() { | |||||||
|         game_list->show(); |         game_list->show(); | ||||||
|     } |     } | ||||||
|     game_list->SetFilterFocus(); |     game_list->SetFilterFocus(); | ||||||
|  |     tas_label->clear(); | ||||||
|  |     input_subsystem->GetTas()->Stop(); | ||||||
|  |  | ||||||
|     render_window->removeEventFilter(render_window); |     render_window->removeEventFilter(render_window); | ||||||
|     render_window->setAttribute(Qt::WA_Hover, false); |     render_window->setAttribute(Qt::WA_Hover, false); | ||||||
|   | |||||||
| @@ -320,7 +320,7 @@ private: | |||||||
|     QLabel* emu_speed_label = nullptr; |     QLabel* emu_speed_label = nullptr; | ||||||
|     QLabel* game_fps_label = nullptr; |     QLabel* game_fps_label = nullptr; | ||||||
|     QLabel* emu_frametime_label = nullptr; |     QLabel* emu_frametime_label = nullptr; | ||||||
|     QLabel* TASlabel; |     QLabel* tas_label = nullptr; | ||||||
|     QPushButton* gpu_accuracy_button = nullptr; |     QPushButton* gpu_accuracy_button = nullptr; | ||||||
|     QPushButton* renderer_status_button = nullptr; |     QPushButton* renderer_status_button = nullptr; | ||||||
|     QPushButton* dock_status_button = nullptr; |     QPushButton* dock_status_button = nullptr; | ||||||
|   | |||||||
| @@ -72,7 +72,6 @@ | |||||||
|     <addaction name="action_Restart"/> |     <addaction name="action_Restart"/> | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|     <addaction name="action_Configure"/> |     <addaction name="action_Configure"/> | ||||||
|     <addaction name="action_Configure_Tas"/> |  | ||||||
|     <addaction name="action_Configure_Current_Game"/> |     <addaction name="action_Configure_Current_Game"/> | ||||||
|    </widget> |    </widget> | ||||||
|    <widget class="QMenu" name="menu_View"> |    <widget class="QMenu" name="menu_View"> | ||||||
| @@ -101,6 +100,7 @@ | |||||||
|     <addaction name="action_Rederive"/> |     <addaction name="action_Rederive"/> | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|     <addaction name="action_Capture_Screenshot"/> |     <addaction name="action_Capture_Screenshot"/> | ||||||
|  |     <addaction name="action_Configure_Tas"/> | ||||||
|    </widget> |    </widget> | ||||||
|    <widget class="QMenu" name="menu_Help"> |    <widget class="QMenu" name="menu_Help"> | ||||||
|     <property name="title"> |     <property name="title"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 german77
					german77