diff --git a/airspy_source/src/main.cpp b/airspy_source/src/main.cpp index 87d38aaa..6d529a61 100644 --- a/airspy_source/src/main.cpp +++ b/airspy_source/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -405,7 +406,7 @@ private: ImGui::Text("Gain"); ImGui::SameLine(); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) { + if (ImGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21) || ImGui::AllowScrollwheelStSz(_this->sensitiveGain, 1, 0, 21)) { if (_this->running) { airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); } @@ -420,7 +421,7 @@ private: ImGui::Text("Gain"); ImGui::SameLine(); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) { + if (ImGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21) || ImGui::AllowScrollwheelStSz(_this->linearGain, 1, 0, 21)) { if (_this->running) { airspy_set_linearity_gain(_this->openDev, _this->linearGain); } @@ -440,7 +441,7 @@ private: ImGui::SameLine(); ImGui::SetCursorPosX(pos); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { + if (ImGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15) || ImGui::AllowScrollwheelStSz(_this->lnaGain, 1, 0, 15)) { if (_this->running) { airspy_set_lna_gain(_this->openDev, _this->lnaGain); } @@ -457,7 +458,7 @@ private: ImGui::SameLine(); ImGui::SetCursorPosX(pos); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) { + if (ImGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15) || ImGui::AllowScrollwheelStSz(_this->mixerGain, 1, 0, 15)) { if (_this->running) { airspy_set_mixer_gain(_this->openDev, _this->mixerGain); } @@ -473,7 +474,7 @@ private: ImGui::SameLine(); ImGui::SetCursorPosX(pos); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { + if (ImGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15) || ImGui::AllowScrollwheelStSz(_this->vgaGain, 1, 0, 15)) { if (_this->running) { airspy_set_vga_gain(_this->openDev, _this->vgaGain); } diff --git a/airspyhf_source/src/main.cpp b/airspyhf_source/src/main.cpp index 9be478f2..d290eab8 100644 --- a/airspyhf_source/src/main.cpp +++ b/airspyhf_source/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -225,7 +226,7 @@ private: if (_this->agcMode > 0) { airspyhf_set_hf_agc_threshold(_this->openDev, _this->agcMode - 1); } - airspyhf_set_hf_att(_this->openDev, _this->atten / 6); + airspyhf_set_hf_att(_this->openDev, _this->atten / 6.0f); airspyhf_set_hf_lna(_this->openDev, _this->hfLNA); airspyhf_start(_this->openDev, callback, _this); @@ -328,10 +329,9 @@ private: ImGui::Text("Attenuation"); ImGui::SameLine(); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); - if (ImGui::SliderInt(CONCAT("##_airspyhf_attn_", _this->name), &_this->atten, 0, 48, "%d dB")) { - _this->atten = (_this->atten / 6) * 6; + if (ImGui::SliderFloatWithSteps(CONCAT("##_airspyhf_attn_", _this->name), &_this->atten, 0, 48, 6, "%.0f dB")) { if (_this->running) { - airspyhf_set_hf_att(_this->openDev, _this->atten / 6); + airspyhf_set_hf_att(_this->openDev, _this->atten / 6.0f); } if (_this->selectedSerStr != "") { config.aquire(); @@ -361,7 +361,7 @@ private: int srId = 0; int agcMode = AGC_MODE_OFF; bool hfLNA = false; - int atten = 0; + float atten = 0.0f; std::string selectedSerStr = ""; std::vector devList; diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 8e5e7ef1..0c2393fd 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -32,6 +32,10 @@ #include #include #include +#include + +#include +FileSelect fileSelect("%ROOT/recordings", "Alllll\0*.*\0Text\0*.TXT\0"); // const int FFTSizes[] = { // 65536, @@ -553,6 +557,7 @@ void drawWindow() { } ImGui::Checkbox("Show demo window", &demoWindow); ImGui::Checkbox("Experimental zoom", &experimentalZoom); + fileSelect.render("##_testfile"); ImGui::Spacing(); } @@ -584,7 +589,8 @@ void drawWindow() { ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0)); ImGui::Text("Zoom"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); - if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, gui::waterfall.getBandwidth(), 1000.0, "", (experimentalZoom ? 2.0 : 1.0))) { + if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, gui::waterfall.getBandwidth(), 1000.0, "", (experimentalZoom ? 2.0 : 1.0)) || + ImGui::AllowScrollwheel(bw, 20, gui::waterfall.getBandwidth(), 1000.0)) { gui::waterfall.setViewBandwidth(bw); if (vfo != NULL) { gui::waterfall.setViewOffset(vfo->centerOffset); // center vfo on screen @@ -596,7 +602,7 @@ void drawWindow() { ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0)); ImGui::Text("Max"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); - if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -100.0, "")) { + if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -100.0, "") || ImGui::AllowScrollwheel(fftMax, 20, -100, 0)) { fftMax = std::max(fftMax, fftMin + 10); core::configManager.aquire(); core::configManager.conf["max"] = fftMax; @@ -608,7 +614,7 @@ void drawWindow() { ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0)); ImGui::Text("Min"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10); - if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -100.0, "")) { + if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -100.0, "") || ImGui::AllowScrollwheel(fftMin, 20, -100, 0)) { fftMin = std::min(fftMax - 10, fftMin); core::configManager.aquire(); core::configManager.conf["min"] = fftMin; diff --git a/core/src/gui/widgets/file_select.cpp b/core/src/gui/widgets/file_select.cpp new file mode 100644 index 00000000..98f97626 --- /dev/null +++ b/core/src/gui/widgets/file_select.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +FileSelect::FileSelect(std::string defaultPath, char* filter) { + path = defaultPath; + strcpy(_filter, filter); + pathValid = std::filesystem::is_regular_file(path); + strcpy(strPath, path.c_str()); +} + +void FileSelect::render(std::string id) { +#ifdef _WIN32 + float menuColumnWidth = ImGui::GetContentRegionAvailWidth(); + float buttonWidth = ImGui::CalcTextSize("...").x + 20.0f; + bool lastPathValid = pathValid; + if (!lastPathValid) { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + } + ImGui::SetNextItemWidth(menuColumnWidth - buttonWidth); + if (ImGui::InputText(id.c_str(), strPath, 2047)) { + path = std::string(strPath); + std::string expandedPath = expandString(strPath); + if (!std::filesystem::is_regular_file(expandedPath)) { + pathValid = false; + } + else { + pathValid = true; + _pathChanged = true; + } + } + if (!lastPathValid) { + ImGui::PopStyleColor(); + } + ImGui::SameLine(); + if (ImGui::Button(("..." + id + "_winselect").c_str(), ImVec2(buttonWidth - 8.0f, 0)) && !dialogOpen) { + dialogOpen = true; + if (workerThread.joinable()) { workerThread.join(); } + workerThread = std::thread(&FileSelect::windowsWorker, this); + } +#else + bool lastPathValid = pathValid; + if (!lastPathValid) { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); + } + if (ImGui::InputText(id.c_str(), strPath, 2047)) { + path = std::string(strPath); + std::string expandedPath = expandString(strPath); + if (!std::filesystem::is_regular_file(expandedPath)) { + pathValid = false; + } + else { + pathValid = true; + _pathChanged = true; + } + } + if (!lastPathValid) { + ImGui::PopStyleColor(); + } +#endif +} + +std::string FileSelect::expandString(std::string input) { + input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root); + return std::regex_replace(input, std::regex("//"), "/"); +} + +bool FileSelect::pathIsValid() { + return pathValid; +} + +bool FileSelect::pathChanged() { + if (_pathChanged) { + _pathChanged = false; + return true; + } + return false; +} + +#ifdef _WIN32 +void FileSelect::windowsWorker() { + OPENFILENAMEA ofn; // common dialog box structure + char szFile[2048]; // buffer for file name + HWND hwnd; // owner window + HANDLE hf; // file handle + + // Initialize OPENFILENAME + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = GetFocus(); + ofn.lpstrFile = (LPSTR)szFile; + // Set lpstrFile[0] to '\0' so that GetOpenFileName does not + // use the contents of szFile to initialize itself. + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = (LPCSTR)_filter; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXTENSIONDIFFERENT; + + // Display the Open dialog box. + + if (GetOpenFileNameA(&ofn)==TRUE) { + strcpy(strPath, szFile); + _pathChanged = true; + } + + pathValid = std::filesystem::is_regular_file(strPath); + dialogOpen = false; +} +#endif \ No newline at end of file diff --git a/core/src/gui/widgets/file_select.h b/core/src/gui/widgets/file_select.h new file mode 100644 index 00000000..b97673f7 --- /dev/null +++ b/core/src/gui/widgets/file_select.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +class FileSelect { +public: + FileSelect(std::string defaultPath, char* filter = "All\0*.*"); + void render(std::string id); + bool pathIsValid(); + bool pathChanged(); + + std::string expandString(std::string input); + + std::string path = ""; + +private: +#ifdef _WIN32 + void windowsWorker(); + std::thread workerThread; +#endif + + char _filter[2048]; + bool pathValid = false; + bool dialogOpen = false; + char strPath[2048]; + bool _pathChanged = false; +}; \ No newline at end of file diff --git a/core/src/gui/widgets/scroll_behavior.h b/core/src/gui/widgets/scroll_behavior.h new file mode 100644 index 00000000..339fe32a --- /dev/null +++ b/core/src/gui/widgets/scroll_behavior.h @@ -0,0 +1,21 @@ +#pragma once +#include + +namespace ImGui { + template + inline bool AllowScrollwheel(T& value, int stepCount, T min, T max) { + if(!ImGui::IsItemHovered()) { return false; } + T lastVal = value; + T step = (max - min) / (T)stepCount; + value = std::clamp(value + ((T)ImGui::GetIO().MouseWheel * step), (min < max) ? min : max, (max > min) ? max : min); + return (value != lastVal); + } + + template + inline bool AllowScrollwheelStSz(T& value, T step, T min, T max) { + if(!ImGui::IsItemHovered()) { return false; } + T lastVal = value; + value = std::clamp(value + ((T)ImGui::GetIO().MouseWheel * step), (min < max) ? min : max, (max > min) ? max : min); + return (value != lastVal); + } +} diff --git a/core/src/gui/widgets/stepped_slider.cpp b/core/src/gui/widgets/stepped_slider.cpp index be2f7c23..d0fed8e9 100644 --- a/core/src/gui/widgets/stepped_slider.cpp +++ b/core/src/gui/widgets/stepped_slider.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace ImGui { bool SliderFloatWithSteps(const char* label, float* v, float v_min, float v_max, float v_step, const char* display_format) { @@ -14,7 +15,8 @@ namespace ImGui { // Map from [v_min,v_max] to [0,N] const int countValues = int((v_max-v_min)/v_step); int v_i = int((*v - v_min)/v_step); - const bool value_changed = ImGui::SliderInt(label, &v_i, 0, countValues, text_buf); + bool value_changed = ImGui::SliderInt(label, &v_i, 0, countValues, text_buf); + value_changed |= ImGui::AllowScrollwheelStSz(v_i, 1, 0, countValues); // Remap from [0,N] to [v_min,v_max] *v = v_min + float(v_i) * v_step; diff --git a/core/src/signal_path/sink.cpp b/core/src/signal_path/sink.cpp index 90361ab4..563af9a8 100644 --- a/core/src/signal_path/sink.cpp +++ b/core/src/signal_path/sink.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -216,7 +217,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w ImGui::SetNextItemWidth(width - height - 8); ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder); - if (ImGui::SliderFloat((prefix + name).c_str(), &stream->guiVolume, 0.0f, 1.0f, "")) { + if (ImGui::SliderFloat((prefix + name).c_str(), &stream->guiVolume, 0.0f, 1.0f, "") || ImGui::AllowScrollwheel(stream->guiVolume, 20, 0, 1)) { stream->setVolume(stream->guiVolume); core::configManager.aquire(); saveStreamConfig(name); diff --git a/plutosdr_source/src/main.cpp b/plutosdr_source/src/main.cpp index e4a8222f..71e4568e 100644 --- a/plutosdr_source/src/main.cpp +++ b/plutosdr_source/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -192,7 +193,7 @@ private: ImGui::SameLine(); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); if (_this->gainMode) { style::beginDisabled(); } - if (ImGui::SliderFloat(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 76)) { + if (ImGui::SliderFloat(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 76) || ImGui::AllowScrollwheel(_this->gain, 19, 0, 76)) { if (_this->running) { iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false),"hardwaregain", round(_this->gain)); } diff --git a/readme.md b/readme.md index c20c8b0c..35c58ad0 100644 --- a/readme.md +++ b/readme.md @@ -183,6 +183,8 @@ Then, you need to edit the `root_dev/config` file to point to the modules that w ... ``` +Note: You can generate this list automatically by running `find . | grep '\.so' | sed 's/^/"/' | sed 's/$/",/' | sed '/sdrpp_core.so/d'` in the build directory. + You also need to change the location of the resource and module directories, for development, I recommend: ```json ...