mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-14 11:49:59 +01:00
Code cleanup. Split code for each workspace windows
Renamed class and split code for compiling the multiple workspace windows as separate files (h and cpp).
This commit is contained in:
@@ -65,6 +65,12 @@ set(VMIX_SRCS
|
|||||||
DelayFilter.cpp
|
DelayFilter.cpp
|
||||||
RenderingManager.cpp
|
RenderingManager.cpp
|
||||||
UserInterfaceManager.cpp
|
UserInterfaceManager.cpp
|
||||||
|
WorkspaceWindow.cpp
|
||||||
|
SourceControlWindow.cpp
|
||||||
|
OutputPreviewWindow.cpp
|
||||||
|
TimerMetronomeWindow.cpp
|
||||||
|
InputMappingWindow.cpp
|
||||||
|
ShaderEditWindow.cpp
|
||||||
PickingVisitor.cpp
|
PickingVisitor.cpp
|
||||||
BoundingBoxVisitor.cpp
|
BoundingBoxVisitor.cpp
|
||||||
DrawVisitor.cpp
|
DrawVisitor.cpp
|
||||||
|
|||||||
@@ -94,6 +94,17 @@ namespace ImGuiToolkit
|
|||||||
bool WindowButton(const char* window_name, ImVec2 window_pos, const char* text);
|
bool WindowButton(const char* window_name, ImVec2 window_pos, const char* text);
|
||||||
void WindowDragFloat(const char* window_name, ImVec2 window_pos, float* v, float v_speed, float v_min, float v_max, const char* format);
|
void WindowDragFloat(const char* window_name, ImVec2 window_pos, float* v, float v_speed, float v_min, float v_max, const char* format);
|
||||||
|
|
||||||
|
// Helper functions for imgui window aspect-ratio constraints
|
||||||
|
struct CustomConstraints
|
||||||
|
{
|
||||||
|
static void AspectRatio(ImGuiSizeCallbackData* data) {
|
||||||
|
float *ar = (float*) data->UserData;
|
||||||
|
data->DesiredSize.y = (data->CurrentSize.x / (*ar)) + 35.f;
|
||||||
|
}
|
||||||
|
static void Square(ImGuiSizeCallbackData* data) {
|
||||||
|
data->DesiredSize.x = data->DesiredSize.y = (data->DesiredSize.x > data->DesiredSize.y ? data->DesiredSize.x : data->DesiredSize.y);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __IMGUI_TOOLKIT_H_
|
#endif // __IMGUI_TOOLKIT_H_
|
||||||
|
|||||||
1303
src/InputMappingWindow.cpp
Normal file
1303
src/InputMappingWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
32
src/InputMappingWindow.h
Normal file
32
src/InputMappingWindow.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef INPUTMAPPINGWINDOW_H
|
||||||
|
#define INPUTMAPPINGWINDOW_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "SourceList.h"
|
||||||
|
#include "WorkspaceWindow.h"
|
||||||
|
|
||||||
|
class SourceCallback;
|
||||||
|
|
||||||
|
class InputMappingWindow : public WorkspaceWindow
|
||||||
|
{
|
||||||
|
std::array< std::string, 4 > input_mode;
|
||||||
|
std::array< uint, 4 > current_input_for_mode;
|
||||||
|
uint current_input_;
|
||||||
|
|
||||||
|
Target ComboSelectTarget(const Target ¤t);
|
||||||
|
uint ComboSelectCallback(uint current, bool imageprocessing);
|
||||||
|
void SliderParametersCallback(SourceCallback *callback, const Target &target);
|
||||||
|
|
||||||
|
public:
|
||||||
|
InputMappingWindow();
|
||||||
|
|
||||||
|
void Render();
|
||||||
|
void setVisible(bool on);
|
||||||
|
|
||||||
|
// from WorkspaceWindow
|
||||||
|
bool Visible() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INPUTMAPPINGWINDOW_H
|
||||||
634
src/OutputPreviewWindow.cpp
Normal file
634
src/OutputPreviewWindow.cpp
Normal file
@@ -0,0 +1,634 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of vimix - video live mixer
|
||||||
|
*
|
||||||
|
* **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// ImGui
|
||||||
|
#include "ImGuiToolkit.h"
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "SystemToolkit.h"
|
||||||
|
#include "NetworkToolkit.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "Mixer.h"
|
||||||
|
#include "Recorder.h"
|
||||||
|
#include "Connection.h"
|
||||||
|
#include "Streamer.h"
|
||||||
|
#include "Loopback.h"
|
||||||
|
#include "VideoBroadcast.h"
|
||||||
|
#include "ShmdataBroadcast.h"
|
||||||
|
|
||||||
|
#include "UserInterfaceManager.h"
|
||||||
|
#include "OutputPreviewWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
OutputPreviewWindow::OutputPreviewWindow() : WorkspaceWindow("OutputPreview"),
|
||||||
|
video_recorder_(nullptr), video_broadcaster_(nullptr), loopback_broadcaster_(nullptr),
|
||||||
|
magnifying_glass(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
recordFolderDialog = new DialogToolkit::OpenFolderDialog("Recording Location");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputPreviewWindow::setVisible(bool on)
|
||||||
|
{
|
||||||
|
magnifying_glass = false;
|
||||||
|
|
||||||
|
// restore workspace to show the window
|
||||||
|
if (WorkspaceWindow::clear_workspace_enabled) {
|
||||||
|
WorkspaceWindow::restoreWorkspace(on);
|
||||||
|
// do not change status if ask to hide (consider user asked to toggle because the window was not visible)
|
||||||
|
if (!on) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::application.widget.preview_view > 0 && Settings::application.widget.preview_view != Settings::application.current_view) {
|
||||||
|
Settings::application.widget.preview_view = -1;
|
||||||
|
on = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::application.widget.preview = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OutputPreviewWindow::Visible() const
|
||||||
|
{
|
||||||
|
return ( Settings::application.widget.preview
|
||||||
|
&& (Settings::application.widget.preview_view < 0 || Settings::application.widget.preview_view == Settings::application.current_view )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputPreviewWindow::Update()
|
||||||
|
{
|
||||||
|
WorkspaceWindow::Update();
|
||||||
|
|
||||||
|
// management of video_recorders
|
||||||
|
if ( !_video_recorders.empty() ) {
|
||||||
|
// check that file dialog thread finished
|
||||||
|
if (_video_recorders.back().wait_for(std::chrono::milliseconds(4)) == std::future_status::ready ) {
|
||||||
|
video_recorder_ = _video_recorders.back().get();
|
||||||
|
FrameGrabbing::manager().add(video_recorder_);
|
||||||
|
_video_recorders.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// verify the video recorder is valid (change to nullptr if invalid)
|
||||||
|
FrameGrabbing::manager().verify( (FrameGrabber**) &video_recorder_);
|
||||||
|
if (video_recorder_ // if there is an ongoing recorder
|
||||||
|
&& Settings::application.record.timeout < RECORD_MAX_TIMEOUT // and if the timeout is valid
|
||||||
|
&& video_recorder_->duration() > Settings::application.record.timeout ) // and the timeout is reached
|
||||||
|
{
|
||||||
|
video_recorder_->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the frame grabbers are valid (change to nullptr if invalid)
|
||||||
|
FrameGrabbing::manager().verify( (FrameGrabber**) &video_broadcaster_);
|
||||||
|
FrameGrabbing::manager().verify( (FrameGrabber**) &shm_broadcaster_);
|
||||||
|
FrameGrabbing::manager().verify( (FrameGrabber**) &loopback_broadcaster_);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoRecorder *delayTrigger(VideoRecorder *g, std::chrono::milliseconds delay) {
|
||||||
|
std::this_thread::sleep_for (delay);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputPreviewWindow::ToggleRecord(bool save_and_continue)
|
||||||
|
{
|
||||||
|
if (video_recorder_) {
|
||||||
|
// prepare for next time user open new source panel to show the recording
|
||||||
|
if (Settings::application.recentRecordings.load_at_start)
|
||||||
|
UserInterface::manager().navigator.setNewMedia(Navigator::MEDIA_RECORDING);
|
||||||
|
// 'save & continue'
|
||||||
|
if ( save_and_continue) {
|
||||||
|
VideoRecorder *rec = new VideoRecorder(SystemToolkit::base_filename( Mixer::manager().session()->filename()));
|
||||||
|
FrameGrabbing::manager().chain(video_recorder_, rec);
|
||||||
|
video_recorder_ = rec;
|
||||||
|
}
|
||||||
|
// normal case: Ctrl+R stop recording
|
||||||
|
else
|
||||||
|
// stop recording
|
||||||
|
video_recorder_->stop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_video_recorders.emplace_back( std::async(std::launch::async, delayTrigger, new VideoRecorder(SystemToolkit::base_filename( Mixer::manager().session()->filename())),
|
||||||
|
std::chrono::seconds(Settings::application.record.delay)) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputPreviewWindow::ToggleVideoBroadcast()
|
||||||
|
{
|
||||||
|
if (video_broadcaster_) {
|
||||||
|
video_broadcaster_->stop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (Settings::application.broadcast_port<1000)
|
||||||
|
Settings::application.broadcast_port = BROADCAST_DEFAULT_PORT;
|
||||||
|
video_broadcaster_ = new VideoBroadcast(Settings::application.broadcast_port);
|
||||||
|
FrameGrabbing::manager().add(video_broadcaster_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputPreviewWindow::ToggleSharedMemory()
|
||||||
|
{
|
||||||
|
if (shm_broadcaster_) {
|
||||||
|
shm_broadcaster_->stop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// find a folder to put the socket for shm
|
||||||
|
std::string _shm_socket_file = Settings::application.shm_socket_path;
|
||||||
|
if (_shm_socket_file.empty() || !SystemToolkit::file_exists(_shm_socket_file))
|
||||||
|
_shm_socket_file = SystemToolkit::home_path();
|
||||||
|
_shm_socket_file = SystemToolkit::full_filename(_shm_socket_file, ".shm_vimix" + std::to_string(Settings::application.instance_id));
|
||||||
|
|
||||||
|
// create shhmdata broadcaster with method
|
||||||
|
shm_broadcaster_ = new ShmdataBroadcast( (ShmdataBroadcast::Method) Settings::application.shm_method, _shm_socket_file);
|
||||||
|
FrameGrabbing::manager().add(shm_broadcaster_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OutputPreviewWindow::ToggleLoopbackCamera()
|
||||||
|
{
|
||||||
|
bool need_initialization = false;
|
||||||
|
if (loopback_broadcaster_) {
|
||||||
|
loopback_broadcaster_->stop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (Settings::application.loopback_camera < 1)
|
||||||
|
Settings::application.loopback_camera = LOOPBACK_DEFAULT_DEVICE;
|
||||||
|
Settings::application.loopback_camera += Settings::application.instance_id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loopback_broadcaster_ = new Loopback(Settings::application.loopback_camera);
|
||||||
|
FrameGrabbing::manager().add(loopback_broadcaster_);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error &e) {
|
||||||
|
need_initialization = true;
|
||||||
|
Log::Info("%s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return need_initialization;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OutputPreviewWindow::Render()
|
||||||
|
{
|
||||||
|
const ImGuiContext& g = *GImGui;
|
||||||
|
bool openInitializeSystemLoopback = false;
|
||||||
|
|
||||||
|
FrameBuffer *output = Mixer::manager().session()->frame();
|
||||||
|
if (output)
|
||||||
|
{
|
||||||
|
// constraint aspect ratio resizing
|
||||||
|
float ar = output->aspectRatio();
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2(300, 200), ImVec2(FLT_MAX, FLT_MAX), ImGuiToolkit::CustomConstraints::AspectRatio, &ar);
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(1180, 20), ImGuiCond_FirstUseEver);
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(400, 260), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
|
// Window named "OutputPreview" at instanciation
|
||||||
|
if ( !ImGui::Begin(name_, &Settings::application.widget.preview,
|
||||||
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse ) )
|
||||||
|
{
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return from thread for folder openning
|
||||||
|
if (recordFolderDialog->closed() && !recordFolderDialog->path().empty())
|
||||||
|
// get the folder from this file dialog
|
||||||
|
Settings::application.record.path = recordFolderDialog->path();
|
||||||
|
|
||||||
|
// menu (no title bar)
|
||||||
|
if (ImGui::BeginMenuBar())
|
||||||
|
{
|
||||||
|
if (ImGuiToolkit::IconButton(4,16))
|
||||||
|
Settings::application.widget.preview = false;
|
||||||
|
|
||||||
|
if (ImGui::BeginMenu(IMGUI_TITLE_PREVIEW))
|
||||||
|
{
|
||||||
|
// Preview and output menu
|
||||||
|
ImGui::MenuItem( MENU_LARGEPREVIEW, SHORTCUT_LARGEPREVIEW, &UserInterface::manager().show_output_fullview);
|
||||||
|
ImGui::MenuItem( MENU_OUTPUTDISABLE, SHORTCUT_OUTPUTDISABLE, &Settings::application.render.disabled);
|
||||||
|
|
||||||
|
// Display window manager menu
|
||||||
|
ImGui::Separator();
|
||||||
|
bool pinned = Settings::application.widget.preview_view == Settings::application.current_view;
|
||||||
|
std::string menutext = std::string( ICON_FA_MAP_PIN " Stick to ") + Settings::application.views[Settings::application.current_view].name + " view";
|
||||||
|
if ( ImGui::MenuItem( menutext.c_str(), nullptr, &pinned) ){
|
||||||
|
if (pinned)
|
||||||
|
Settings::application.widget.preview_view = Settings::application.current_view;
|
||||||
|
else
|
||||||
|
Settings::application.widget.preview_view = -1;
|
||||||
|
}
|
||||||
|
if ( ImGui::MenuItem( MENU_CLOSE, SHORTCUT_OUTPUT) )
|
||||||
|
Settings::application.widget.preview = false;
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginMenu( ICON_FA_ARROW_ALT_CIRCLE_DOWN " Capture"))
|
||||||
|
{
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_CAPTURE, 0.8f));
|
||||||
|
if ( ImGui::MenuItem( MENU_CAPTUREFRAME, SHORTCUT_CAPTURE_DISPLAY) ) {
|
||||||
|
FrameGrabbing::manager().add(new PNGRecorder(SystemToolkit::base_filename( Mixer::manager().session()->filename())));
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
|
||||||
|
// temporary disabled
|
||||||
|
if (!_video_recorders.empty()) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_RECORD, 0.8f));
|
||||||
|
ImGui::MenuItem( MENU_RECORD, SHORTCUT_RECORD, false, false);
|
||||||
|
ImGui::MenuItem( MENU_RECORDCONT, SHORTCUT_RECORDCONT, false, false);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
// Stop recording menu (recorder already exists)
|
||||||
|
else if (video_recorder_) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_RECORD, 0.8f));
|
||||||
|
if ( ImGui::MenuItem( ICON_FA_SQUARE " Stop Record", SHORTCUT_RECORD) ) {
|
||||||
|
// prepare for next time user open new source panel to show the recording
|
||||||
|
if (Settings::application.recentRecordings.load_at_start)
|
||||||
|
UserInterface::manager().navigator.setNewMedia(Navigator::MEDIA_RECORDING);
|
||||||
|
// stop recorder
|
||||||
|
video_recorder_->stop();
|
||||||
|
}
|
||||||
|
// offer the 'save & continue' recording
|
||||||
|
if ( ImGui::MenuItem( MENU_RECORDCONT, SHORTCUT_RECORDCONT) ) {
|
||||||
|
// prepare for next time user open new source panel to show the recording
|
||||||
|
if (Settings::application.recentRecordings.load_at_start)
|
||||||
|
UserInterface::manager().navigator.setNewMedia(Navigator::MEDIA_RECORDING);
|
||||||
|
// create a new recorder chainned to the current one
|
||||||
|
VideoRecorder *rec = new VideoRecorder(SystemToolkit::base_filename( Mixer::manager().session()->filename()));
|
||||||
|
FrameGrabbing::manager().chain(video_recorder_, rec);
|
||||||
|
// swap recorder
|
||||||
|
video_recorder_ = rec;
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
// start recording
|
||||||
|
else {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_RECORD, 0.9f));
|
||||||
|
if ( ImGui::MenuItem( MENU_RECORD, SHORTCUT_RECORD) ) {
|
||||||
|
_video_recorders.emplace_back( std::async(std::launch::async, delayTrigger,
|
||||||
|
new VideoRecorder(SystemToolkit::base_filename( Mixer::manager().session()->filename())),
|
||||||
|
std::chrono::seconds(Settings::application.record.delay)) );
|
||||||
|
}
|
||||||
|
ImGui::MenuItem( MENU_RECORDCONT, SHORTCUT_RECORDCONT, false, false);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
// Options menu
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::MenuItem("Settings", nullptr, false, false);
|
||||||
|
// offer to open config panel from here for more options
|
||||||
|
ImGui::SameLine(ImGui::GetContentRegionAvailWidth() + 1.2f * IMGUI_RIGHT_ALIGN);
|
||||||
|
if (ImGuiToolkit::IconButton(13, 5, "Advanced settings"))
|
||||||
|
UserInterface::manager().navigator.showConfig();
|
||||||
|
|
||||||
|
// BASIC OPTIONS
|
||||||
|
static char* name_path[4] = { nullptr };
|
||||||
|
if ( name_path[0] == nullptr ) {
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
name_path[i] = (char *) malloc( 1024 * sizeof(char));
|
||||||
|
sprintf( name_path[1], "%s", ICON_FA_HOME " Home");
|
||||||
|
sprintf( name_path[2], "%s", ICON_FA_FOLDER " Session location");
|
||||||
|
sprintf( name_path[3], "%s", ICON_FA_FOLDER_PLUS " Select");
|
||||||
|
}
|
||||||
|
if (Settings::application.record.path.empty())
|
||||||
|
Settings::application.record.path = SystemToolkit::home_path();
|
||||||
|
sprintf( name_path[0], "%s", Settings::application.record.path.c_str());
|
||||||
|
int selected_path = 0;
|
||||||
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
|
ImGui::Combo("Path", &selected_path, name_path, 4);
|
||||||
|
if (selected_path > 2)
|
||||||
|
recordFolderDialog->open();
|
||||||
|
else if (selected_path > 1)
|
||||||
|
Settings::application.record.path = SystemToolkit::path_filename( Mixer::manager().session()->filename() );
|
||||||
|
else if (selected_path > 0)
|
||||||
|
Settings::application.record.path = SystemToolkit::home_path();
|
||||||
|
|
||||||
|
// offer to open folder location
|
||||||
|
ImVec2 draw_pos = ImGui::GetCursorPos();
|
||||||
|
ImGui::SetCursorPos(draw_pos + ImVec2(ImGui::GetContentRegionAvailWidth() - 1.2 * ImGui::GetTextLineHeightWithSpacing(), -ImGui::GetFrameHeight()) );
|
||||||
|
if (ImGuiToolkit::IconButton( ICON_FA_FOLDER_OPEN, Settings::application.record.path.c_str()))
|
||||||
|
SystemToolkit::open(Settings::application.record.path);
|
||||||
|
ImGui::SetCursorPos(draw_pos);
|
||||||
|
|
||||||
|
// Naming menu selection
|
||||||
|
static const char* naming_style[2] = { ICON_FA_SORT_NUMERIC_DOWN " Sequential", ICON_FA_CALENDAR " Date prefix" };
|
||||||
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
|
ImGui::Combo("Filename", &Settings::application.record.naming_mode, naming_style, IM_ARRAYSIZE(naming_style));
|
||||||
|
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
|
ImGuiToolkit::SliderTiming ("Duration", &Settings::application.record.timeout, 1000, RECORD_MAX_TIMEOUT, 1000, "Until stopped");
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
|
ImGui::SliderInt("Trigger", &Settings::application.record.delay, 0, 5,
|
||||||
|
Settings::application.record.delay < 1 ? "Immediate" : "After %d s");
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginMenu(ICON_FA_WIFI " Stream"))
|
||||||
|
{
|
||||||
|
// Stream sharing menu
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.9f));
|
||||||
|
if ( ImGui::MenuItem( ICON_FA_SHARE_ALT_SQUARE " P2P Peer-to-peer sharing", NULL, &Settings::application.accept_connections) ) {
|
||||||
|
Streaming::manager().enable(Settings::application.accept_connections);
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
|
||||||
|
// list active streams:
|
||||||
|
std::vector<std::string> ls = Streaming::manager().listStreams();
|
||||||
|
|
||||||
|
// Broadcasting menu
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.9f));
|
||||||
|
if (VideoBroadcast::available()) {
|
||||||
|
if ( ImGui::MenuItem( ICON_FA_GLOBE " SRT Broadcast", NULL, videoBroadcastEnabled()) )
|
||||||
|
ToggleVideoBroadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared Memory menu
|
||||||
|
if (ShmdataBroadcast::available()) {
|
||||||
|
if ( ImGui::MenuItem( ICON_FA_MEMORY " SHM Shared Memory", NULL, sharedMemoryEnabled()) )
|
||||||
|
ToggleSharedMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loopback camera menu
|
||||||
|
if (Loopback::available()) {
|
||||||
|
if ( ImGui::MenuItem( ICON_FA_VIDEO " Loopback Camera", NULL, loopbackCameraEnabled()) )
|
||||||
|
openInitializeSystemLoopback = ToggleLoopbackCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
|
||||||
|
// Display list of active stream
|
||||||
|
if (ls.size()>0 || videoBroadcastEnabled() || sharedMemoryEnabled() || loopbackCameraEnabled()) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::MenuItem("Active streams:", nullptr, false, false);
|
||||||
|
|
||||||
|
// First the list of peer 2 peer
|
||||||
|
for (auto it = ls.begin(); it != ls.end(); ++it)
|
||||||
|
ImGui::Text(" %s ", (*it).c_str() );
|
||||||
|
|
||||||
|
// SRT broadcast description
|
||||||
|
if (videoBroadcastEnabled()) {
|
||||||
|
ImGui::Text(" %s ", video_broadcaster_->info().c_str());
|
||||||
|
// copy text icon to give user the srt link to connect to
|
||||||
|
ImVec2 draw_pos = ImGui::GetCursorPos();
|
||||||
|
ImGui::SetCursorPos(draw_pos + ImVec2(ImGui::GetContentRegionAvailWidth() - 1.2 * ImGui::GetTextLineHeightWithSpacing(), -0.8 * ImGui::GetFrameHeight()) );
|
||||||
|
char msg[256];
|
||||||
|
ImFormatString(msg, IM_ARRAYSIZE(msg), "srt//%s:%d", NetworkToolkit::host_ips()[1].c_str(), Settings::application.broadcast_port );
|
||||||
|
if (ImGuiToolkit::IconButton( ICON_FA_COPY, msg))
|
||||||
|
ImGui::SetClipboardText(msg);
|
||||||
|
ImGui::SetCursorPos(draw_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared memory broadcast description
|
||||||
|
if (sharedMemoryEnabled()) {
|
||||||
|
ImGui::Text(" %s ", shm_broadcaster_->info().c_str());
|
||||||
|
// copy text icon to give user the socket path to connect to
|
||||||
|
ImVec2 draw_pos = ImGui::GetCursorPos();
|
||||||
|
ImGui::SetCursorPos(draw_pos + ImVec2(ImGui::GetContentRegionAvailWidth() - 1.2 * ImGui::GetTextLineHeightWithSpacing(), -0.8 * ImGui::GetFrameHeight()) );
|
||||||
|
if (ImGuiToolkit::IconButton( ICON_FA_COPY, shm_broadcaster_->gst_pipeline().c_str()))
|
||||||
|
ImGui::SetClipboardText(shm_broadcaster_->gst_pipeline().c_str());
|
||||||
|
ImGui::SetCursorPos(draw_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loopback camera description
|
||||||
|
if (loopbackCameraEnabled()) {
|
||||||
|
ImGui::Text(" %s ", loopback_broadcaster_->info().c_str());
|
||||||
|
// copy text icon to give user the device path to connect to
|
||||||
|
ImVec2 draw_pos = ImGui::GetCursorPos();
|
||||||
|
ImGui::SetCursorPos(draw_pos + ImVec2(ImGui::GetContentRegionAvailWidth() - 1.2 * ImGui::GetTextLineHeightWithSpacing(), -0.8 * ImGui::GetFrameHeight()) );
|
||||||
|
if (ImGuiToolkit::IconButton( ICON_FA_COPY, loopback_broadcaster_->device_name().c_str()))
|
||||||
|
ImGui::SetClipboardText(loopback_broadcaster_->device_name().c_str());
|
||||||
|
ImGui::SetCursorPos(draw_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::MenuItem("No active streams", nullptr, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// button to activate the magnifying glass at top right corner
|
||||||
|
ImVec2 p = g.CurrentWindow->Pos;
|
||||||
|
p.x += g.CurrentWindow->Size.x - 2.1f * g.FontSize;
|
||||||
|
if (g.CurrentWindow->DC.CursorPos.x < p.x)
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(p);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f));
|
||||||
|
ImGuiToolkit::ButtonToggle( ICON_FA_SEARCH, &magnifying_glass);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// image takes the available window area
|
||||||
|
ImVec2 imagesize = ImGui::GetContentRegionAvail();
|
||||||
|
// image height respects original aspect ratio but is at most the available window height
|
||||||
|
imagesize.y = MIN( imagesize.x / ar, imagesize.y);
|
||||||
|
// image respects original aspect ratio
|
||||||
|
imagesize.x = imagesize.y * ar;
|
||||||
|
|
||||||
|
// virtual button to show the output window when clic on the preview
|
||||||
|
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
|
||||||
|
// 100% opacity for the image (ensures true colors)
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.f);
|
||||||
|
ImGui::Image((void*)(intptr_t)output->texture(), imagesize);
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
// mouse over the image
|
||||||
|
if ( ImGui::IsItemHovered() ) {
|
||||||
|
// show magnifying glass if active
|
||||||
|
if (magnifying_glass)
|
||||||
|
DrawInspector(output->texture(), imagesize, imagesize, draw_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable magnifying glass if window is deactivated
|
||||||
|
if ( g.NavWindow != g.CurrentWindow )
|
||||||
|
magnifying_glass = false;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Icons overlays
|
||||||
|
///
|
||||||
|
const float r = ImGui::GetTextLineHeightWithSpacing();
|
||||||
|
|
||||||
|
// info indicator
|
||||||
|
bool drawoverlay = false;
|
||||||
|
if (!magnifying_glass) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 0.f, 0.f, 0.8f));
|
||||||
|
ImGui::SetCursorScreenPos(draw_pos + ImVec2(imagesize.x - r, 6));
|
||||||
|
ImGui::Text(ICON_FA_CIRCLE);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
ImGui::SetCursorScreenPos(draw_pos + ImVec2(imagesize.x - r, 6));
|
||||||
|
ImGui::Text(ICON_FA_INFO_CIRCLE);
|
||||||
|
drawoverlay = ImGui::IsItemHovered();
|
||||||
|
}
|
||||||
|
|
||||||
|
// icon indicators
|
||||||
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
|
||||||
|
// recording indicator
|
||||||
|
if (video_recorder_)
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + r, draw_pos.y + r));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_RECORD, 0.8f));
|
||||||
|
ImGui::Text(ICON_FA_CIRCLE " %s", video_recorder_->info().c_str() );
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
else if (!_video_recorders.empty())
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + r, draw_pos.y + r));
|
||||||
|
static double anim = 0.f;
|
||||||
|
double a = sin(anim+=0.104); // 2 * pi / 60fps
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_RECORD, a));
|
||||||
|
ImGui::Text(ICON_FA_CIRCLE);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
// broadcast indicator
|
||||||
|
float vertical = r;
|
||||||
|
if (video_broadcaster_)
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + imagesize.x - 2.5f * r, draw_pos.y + vertical));
|
||||||
|
if (video_broadcaster_->busy())
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.8f));
|
||||||
|
else
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.4f));
|
||||||
|
ImGui::Text(ICON_FA_GLOBE);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
vertical += 2.f * r;
|
||||||
|
}
|
||||||
|
// shmdata indicator
|
||||||
|
if (shm_broadcaster_)
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + imagesize.x - 2.5f * r, draw_pos.y + vertical));
|
||||||
|
if (shm_broadcaster_->busy())
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.8f));
|
||||||
|
else
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.4f));
|
||||||
|
ImGui::Text(ICON_FA_MEMORY);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
vertical += 2.f * r;
|
||||||
|
}
|
||||||
|
// loopback camera indicator
|
||||||
|
if (loopback_broadcaster_)
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + imagesize.x - 2.5f * r, draw_pos.y + vertical));
|
||||||
|
if (loopback_broadcaster_->busy())
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.8f));
|
||||||
|
else
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.4f));
|
||||||
|
ImGui::Text(ICON_FA_VIDEO);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
// streaming indicator
|
||||||
|
if (Settings::application.accept_connections)
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + imagesize.x - 2.4f * r, draw_pos.y + imagesize.y - 2.f*r));
|
||||||
|
if ( Streaming::manager().busy())
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.8f));
|
||||||
|
else
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.4f));
|
||||||
|
ImGui::Text(ICON_FA_SHARE_ALT_SQUARE);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
// OUTPUT DISABLED indicator
|
||||||
|
if (Settings::application.render.disabled)
|
||||||
|
{
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + r, draw_pos.y + imagesize.y - 2.f*r));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_WINDOW, 0.8f));
|
||||||
|
ImGui::Text(ICON_FA_EYE_SLASH);
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
}
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Info overlay over image
|
||||||
|
///
|
||||||
|
if (drawoverlay)
|
||||||
|
{
|
||||||
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
|
float h = 1.f;
|
||||||
|
h += (Settings::application.accept_connections ? 1.f : 0.f);
|
||||||
|
draw_list->AddRectFilled(draw_pos, ImVec2(draw_pos.x + imagesize.x, draw_pos.y + h * r), IMGUI_COLOR_OVERLAY);
|
||||||
|
ImGui::SetCursorScreenPos(draw_pos);
|
||||||
|
ImGui::Text(" " ICON_FA_LAPTOP " %d x %d px, %.d fps", output->width(), output->height(), int(Mixer::manager().fps()) );
|
||||||
|
if (Settings::application.accept_connections)
|
||||||
|
ImGui::Text( " " ICON_FA_SHARE_ALT_SQUARE " Available as %s (%ld peer connected)",
|
||||||
|
Connection::manager().info().name.c_str(),
|
||||||
|
Streaming::manager().listStreams().size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog to explain to the user how to initialize the loopback on the system
|
||||||
|
if (openInitializeSystemLoopback && !ImGui::IsPopupOpen("Initialize System Loopback"))
|
||||||
|
ImGui::OpenPopup("Initialize System Loopback");
|
||||||
|
if (ImGui::BeginPopupModal("Initialize System Loopback", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
#if defined(LINUX)
|
||||||
|
int w = 600;
|
||||||
|
ImGui::Text("In order to enable the video4linux camera loopback,\n"
|
||||||
|
"'v4l2loopack' has to be installed and initialized on your machine");
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGuiToolkit::ButtonOpenUrl("More information online on v4l2loopback", "https://github.com/umlaeute/v4l2loopback");
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Text("To do so, the following commands should be executed\n(with admin rights):");
|
||||||
|
|
||||||
|
static char dummy_str[512];
|
||||||
|
sprintf(dummy_str, "sudo apt install v4l2loopback-dkms");
|
||||||
|
ImGui::NewLine();
|
||||||
|
ImGui::Text("Install v4l2loopack (only once, and reboot):");
|
||||||
|
ImGui::SetNextItemWidth(600-40);
|
||||||
|
ImGui::InputText("##cmd1", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushID(358794);
|
||||||
|
if ( ImGuiToolkit::IconButton(ICON_FA_COPY, "Copy to clipboard") )
|
||||||
|
ImGui::SetClipboardText(dummy_str);
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
sprintf(dummy_str, "sudo modprobe v4l2loopback exclusive_caps=1 video_nr=%d"
|
||||||
|
" card_label=\"vimix loopback\"" , Settings::application.loopback_camera);
|
||||||
|
ImGui::NewLine();
|
||||||
|
ImGui::Text("Initialize v4l2loopack:");
|
||||||
|
ImGui::SetNextItemWidth(600-40);
|
||||||
|
ImGui::InputText("##cmd2", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushID(899872);
|
||||||
|
if ( ImGuiToolkit::IconButton(ICON_FA_COPY, "Copy to clipboard") )
|
||||||
|
ImGui::SetClipboardText(dummy_str);
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
|
||||||
|
ImGui::NewLine();
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
if (ImGui::Button("Ok, I'll do this in a terminal and try again later.", ImVec2(w, 0))
|
||||||
|
|| ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter) ) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
52
src/OutputPreviewWindow.h
Normal file
52
src/OutputPreviewWindow.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef OUTPUTPREVIEWWINDOW_H
|
||||||
|
#define OUTPUTPREVIEWWINDOW_H
|
||||||
|
|
||||||
|
#include "DialogToolkit.h"
|
||||||
|
#include "WorkspaceWindow.h"
|
||||||
|
|
||||||
|
class VideoRecorder;
|
||||||
|
class VideoBroadcast;
|
||||||
|
class ShmdataBroadcast;
|
||||||
|
class Loopback;
|
||||||
|
|
||||||
|
class OutputPreviewWindow : public WorkspaceWindow
|
||||||
|
{
|
||||||
|
// frame grabbers
|
||||||
|
VideoRecorder *video_recorder_;
|
||||||
|
VideoBroadcast *video_broadcaster_;
|
||||||
|
ShmdataBroadcast *shm_broadcaster_;
|
||||||
|
Loopback *loopback_broadcaster_;
|
||||||
|
|
||||||
|
// delayed trigger for recording
|
||||||
|
std::vector< std::future<VideoRecorder *> > _video_recorders;
|
||||||
|
|
||||||
|
// dialog to select record location
|
||||||
|
DialogToolkit::OpenFolderDialog *recordFolderDialog;
|
||||||
|
|
||||||
|
// magnifying glass
|
||||||
|
bool magnifying_glass;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OutputPreviewWindow();
|
||||||
|
|
||||||
|
void ToggleRecord(bool save_and_continue = false);
|
||||||
|
inline bool isRecording() const { return video_recorder_ != nullptr; }
|
||||||
|
|
||||||
|
void ToggleVideoBroadcast();
|
||||||
|
inline bool videoBroadcastEnabled() const { return video_broadcaster_ != nullptr; }
|
||||||
|
|
||||||
|
void ToggleSharedMemory();
|
||||||
|
inline bool sharedMemoryEnabled() const { return shm_broadcaster_ != nullptr; }
|
||||||
|
|
||||||
|
bool ToggleLoopbackCamera();
|
||||||
|
inline bool loopbackCameraEnabled() const { return loopback_broadcaster_!= nullptr; }
|
||||||
|
|
||||||
|
void Render();
|
||||||
|
void setVisible(bool on);
|
||||||
|
|
||||||
|
// from WorkspaceWindow
|
||||||
|
void Update() override;
|
||||||
|
bool Visible() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OUTPUTPREVIEWWINDOW_H
|
||||||
354
src/ShaderEditWindow.cpp
Normal file
354
src/ShaderEditWindow.cpp
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
|
||||||
|
// ImGui
|
||||||
|
#include "ImGuiToolkit.h"
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "TextEditor.h"
|
||||||
|
TextEditor _editor;
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "Mixer.h"
|
||||||
|
#include "SystemToolkit.h"
|
||||||
|
#include "CloneSource.h"
|
||||||
|
|
||||||
|
#include "ShaderEditWindow.h"
|
||||||
|
|
||||||
|
///
|
||||||
|
/// SHADER EDITOR
|
||||||
|
///
|
||||||
|
///
|
||||||
|
ShaderEditWindow::ShaderEditWindow() : WorkspaceWindow("Shader"), current_(nullptr), show_shader_inputs_(false)
|
||||||
|
{
|
||||||
|
auto lang = TextEditor::LanguageDefinition::GLSL();
|
||||||
|
|
||||||
|
static const char* const keywords[] = {
|
||||||
|
"discard", "attribute", "varying", "uniform", "in", "out", "inout", "bvec2", "bvec3", "bvec4", "dvec2",
|
||||||
|
"dvec3", "dvec4", "ivec2", "ivec3", "ivec4", "uvec2", "uvec3", "uvec4", "vec2", "vec3", "vec4", "mat2",
|
||||||
|
"mat3", "mat4", "dmat2", "dmat3", "dmat4", "sampler1D", "sampler2D", "sampler3D", "samplerCUBE", "samplerbuffer",
|
||||||
|
"sampler1DArray", "sampler2DArray", "sampler1DShadow", "sampler2DShadow", "vec4", "vec4", "smooth", "flat",
|
||||||
|
"precise", "coherent", "uint", "struct", "switch", "unsigned", "void", "volatile", "while", "readonly"
|
||||||
|
};
|
||||||
|
for (auto& k : keywords)
|
||||||
|
lang.mKeywords.insert(k);
|
||||||
|
|
||||||
|
static const char* const identifiers[] = {
|
||||||
|
"radians", "degrees", "sin", "cos", "tan", "pow", "exp2", "log2", "sqrt", "inversesqrt",
|
||||||
|
"sign", "floor", "ceil", "fract", "mod", "min", "max", "clamp", "mix", "step", "smoothstep", "length", "distance",
|
||||||
|
"dot", "cross", "normalize", "ftransform", "faceforward", "reflect", "matrixcompmult", "lessThan", "lessThanEqual",
|
||||||
|
"greaterThan", "greaterThanEqual", "equal", "notEqual", "any", "all", "not", "texture1D", "texture1DProj", "texture1DLod",
|
||||||
|
"texture1DProjLod", "texture", "texture2D", "texture2DProj", "texture2DLod", "texture2DProjLod", "texture3D",
|
||||||
|
"texture3DProj", "texture3DLod", "texture3DProjLod", "textureCube", "textureCubeLod", "shadow1D", "shadow1DProj",
|
||||||
|
"shadow1DLod", "shadow1DProjLod", "shadow2D", "shadow2DProj", "shadow2DLod", "shadow2DProjLod",
|
||||||
|
"dFdx", "dFdy", "fwidth", "noise1", "noise2", "noise3", "noise4", "refract", "exp", "log", "mainImage",
|
||||||
|
};
|
||||||
|
for (auto& k : identifiers)
|
||||||
|
{
|
||||||
|
TextEditor::Identifier id;
|
||||||
|
id.mDeclaration = "GLSL function";
|
||||||
|
lang.mIdentifiers.insert(std::make_pair(std::string(k), id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* const filter_keyword[] = {
|
||||||
|
"iResolution", "iTime", "iTimeDelta", "iFrame", "iChannelResolution", "iDate", "iMouse",
|
||||||
|
"iChannel0", "iChannel1", "iTransform", "FragColor", "vertexColor", "vertexUV"
|
||||||
|
};
|
||||||
|
for (auto& k : filter_keyword)
|
||||||
|
{
|
||||||
|
TextEditor::Identifier id;
|
||||||
|
id.mDeclaration = "Shader keyword";
|
||||||
|
lang.mPreprocIdentifiers.insert(std::make_pair(std::string(k), id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// init editor
|
||||||
|
_editor.SetLanguageDefinition(lang);
|
||||||
|
_editor.SetHandleKeyboardInputs(true);
|
||||||
|
_editor.SetShowWhitespaces(false);
|
||||||
|
_editor.SetText("");
|
||||||
|
_editor.SetReadOnly(true);
|
||||||
|
|
||||||
|
// status
|
||||||
|
status_ = "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderEditWindow::setVisible(bool on)
|
||||||
|
{
|
||||||
|
// restore workspace to show the window
|
||||||
|
if (WorkspaceWindow::clear_workspace_enabled) {
|
||||||
|
WorkspaceWindow::restoreWorkspace(on);
|
||||||
|
// do not change status if ask to hide (consider user asked to toggle because the window was not visible)
|
||||||
|
if (!on) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on && Settings::application.widget.shader_editor_view != Settings::application.current_view)
|
||||||
|
Settings::application.widget.shader_editor_view = -1;
|
||||||
|
|
||||||
|
Settings::application.widget.shader_editor = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderEditWindow::Visible() const
|
||||||
|
{
|
||||||
|
return ( Settings::application.widget.shader_editor
|
||||||
|
&& (Settings::application.widget.shader_editor_view < 0 || Settings::application.widget.shader_editor_view == Settings::application.current_view )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderEditWindow::BuildShader()
|
||||||
|
{
|
||||||
|
// if the UI has a current clone, and ref to code for current clone is valid
|
||||||
|
if (current_ != nullptr && filters_.find(current_) != filters_.end()) {
|
||||||
|
|
||||||
|
// set the code of the current filter
|
||||||
|
filters_[current_].setCode( { _editor.GetText(), "" } );
|
||||||
|
|
||||||
|
// filter changed, cannot be named as before
|
||||||
|
filters_[current_].setName("Custom");
|
||||||
|
|
||||||
|
// change the filter of the current image filter
|
||||||
|
// => this triggers compilation of shader
|
||||||
|
compilation_ = new std::promise<std::string>();
|
||||||
|
current_->setProgram( filters_[current_], compilation_ );
|
||||||
|
compilation_return_ = compilation_->get_future();
|
||||||
|
|
||||||
|
// inform status
|
||||||
|
status_ = "Building...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderEditWindow::Render()
|
||||||
|
{
|
||||||
|
ImGui::SetWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
|
if ( !ImGui::Begin(name_, &Settings::application.widget.shader_editor,
|
||||||
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse ))
|
||||||
|
{
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// menu (no title bar)
|
||||||
|
if (ImGui::BeginMenuBar())
|
||||||
|
{
|
||||||
|
// Close and widget menu
|
||||||
|
if (ImGuiToolkit::IconButton(4,16))
|
||||||
|
Settings::application.widget.shader_editor = false;
|
||||||
|
if (ImGui::BeginMenu(IMGUI_TITLE_SHADEREDITOR))
|
||||||
|
{
|
||||||
|
// reload code from GPU
|
||||||
|
if (ImGui::MenuItem( ICON_FA_SYNC " Reload", nullptr, nullptr, current_ != nullptr)) {
|
||||||
|
// force reload
|
||||||
|
if ( current_ != nullptr )
|
||||||
|
filters_.erase(current_);
|
||||||
|
current_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menu section for presets
|
||||||
|
if (ImGui::BeginMenu( ICON_FA_SCROLL " Example code", current_ != nullptr))
|
||||||
|
{
|
||||||
|
for (auto p = FilteringProgram::presets.begin(); p != FilteringProgram::presets.end(); ++p){
|
||||||
|
|
||||||
|
if (current_ != nullptr && ImGui::MenuItem( p->name().c_str() )) {
|
||||||
|
// change the filter of the current image filter
|
||||||
|
// => this triggers compilation of shader
|
||||||
|
compilation_ = new std::promise<std::string>();
|
||||||
|
current_->setProgram( *p, compilation_ );
|
||||||
|
compilation_return_ = compilation_->get_future();
|
||||||
|
// inform status
|
||||||
|
status_ = "Building...";
|
||||||
|
// force reload
|
||||||
|
if ( current_ != nullptr )
|
||||||
|
filters_.erase(current_);
|
||||||
|
current_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open browser to shadertoy website
|
||||||
|
if (ImGui::MenuItem( ICON_FA_EXTERNAL_LINK_ALT " Browse shadertoy.com"))
|
||||||
|
SystemToolkit::open("https://www.shadertoy.com/");
|
||||||
|
|
||||||
|
// Enable/Disable editor options
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::MenuItem( ICON_FA_UNDERLINE " Show Shader Inputs", nullptr, &show_shader_inputs_);
|
||||||
|
bool ws = _editor.IsShowingWhitespaces();
|
||||||
|
if (ImGui::MenuItem( ICON_FA_LONG_ARROW_ALT_RIGHT " Show whitespace", nullptr, &ws))
|
||||||
|
_editor.SetShowWhitespaces(ws);
|
||||||
|
|
||||||
|
// output manager menu
|
||||||
|
ImGui::Separator();
|
||||||
|
bool pinned = Settings::application.widget.shader_editor_view == Settings::application.current_view;
|
||||||
|
std::string menutext = std::string( ICON_FA_MAP_PIN " Stick to ") + Settings::application.views[Settings::application.current_view].name + " view";
|
||||||
|
if ( ImGui::MenuItem( menutext.c_str(), nullptr, &pinned) ){
|
||||||
|
if (pinned)
|
||||||
|
Settings::application.widget.shader_editor_view = Settings::application.current_view;
|
||||||
|
else
|
||||||
|
Settings::application.widget.shader_editor_view = -1;
|
||||||
|
}
|
||||||
|
if ( ImGui::MenuItem( MENU_CLOSE, SHORTCUT_SHADEREDITOR) )
|
||||||
|
Settings::application.widget.shader_editor = false;
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit menu
|
||||||
|
bool ro = _editor.IsReadOnly();
|
||||||
|
if (ImGui::BeginMenu( "Edit", current_ != nullptr ) ) {
|
||||||
|
|
||||||
|
if (ImGui::MenuItem( MENU_UNDO, SHORTCUT_UNDO, nullptr, !ro && _editor.CanUndo()))
|
||||||
|
_editor.Undo();
|
||||||
|
if (ImGui::MenuItem( MENU_REDO, CTRL_MOD "Y", nullptr, !ro && _editor.CanRedo()))
|
||||||
|
_editor.Redo();
|
||||||
|
if (ImGui::MenuItem( MENU_COPY, SHORTCUT_COPY, nullptr, _editor.HasSelection()))
|
||||||
|
_editor.Copy();
|
||||||
|
if (ImGui::MenuItem( MENU_CUT, SHORTCUT_CUT, nullptr, !ro && _editor.HasSelection()))
|
||||||
|
_editor.Cut();
|
||||||
|
if (ImGui::MenuItem( MENU_DELETE, SHORTCUT_DELETE, nullptr, !ro && _editor.HasSelection()))
|
||||||
|
_editor.Delete();
|
||||||
|
if (ImGui::MenuItem( MENU_PASTE, SHORTCUT_PASTE, nullptr, !ro && ImGui::GetClipboardText() != nullptr))
|
||||||
|
_editor.Paste();
|
||||||
|
if (ImGui::MenuItem( MENU_SELECTALL, SHORTCUT_SELECTALL, nullptr, _editor.GetText().size() > 1 ))
|
||||||
|
_editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(_editor.GetTotalLines(), 0));
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build action menu
|
||||||
|
if (ImGui::MenuItem( ICON_FA_HAMMER " Build", CTRL_MOD "B", nullptr, current_ != nullptr ))
|
||||||
|
BuildShader();
|
||||||
|
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// garbage collection
|
||||||
|
if ( Mixer::manager().session()->numSources() < 1 )
|
||||||
|
{
|
||||||
|
filters_.clear();
|
||||||
|
current_ = nullptr;
|
||||||
|
_editor.SetText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if compiling, cannot change source nor do anything else
|
||||||
|
static std::chrono::milliseconds timeout = std::chrono::milliseconds(4);
|
||||||
|
if (compilation_ != nullptr && compilation_return_.wait_for(timeout) == std::future_status::ready )
|
||||||
|
{
|
||||||
|
// get message returned from compilation
|
||||||
|
status_ = compilation_return_.get();
|
||||||
|
|
||||||
|
// end compilation promise
|
||||||
|
delete compilation_;
|
||||||
|
compilation_ = nullptr;
|
||||||
|
}
|
||||||
|
// not compiling
|
||||||
|
else {
|
||||||
|
|
||||||
|
ImageFilter *i = nullptr;
|
||||||
|
// get current clone source
|
||||||
|
Source *s = Mixer::manager().currentSource();
|
||||||
|
// if there is a current source
|
||||||
|
if (s != nullptr) {
|
||||||
|
CloneSource *c = dynamic_cast<CloneSource *>(s);
|
||||||
|
// if the current source is a clone
|
||||||
|
if ( c != nullptr ) {
|
||||||
|
FrameBufferFilter *f = c->filter();
|
||||||
|
// if the filter seems to be an Image Filter
|
||||||
|
if (f != nullptr && f->type() == FrameBufferFilter::FILTER_IMAGE ) {
|
||||||
|
i = dynamic_cast<ImageFilter *>(f);
|
||||||
|
// if we can access the code of the filter
|
||||||
|
if (i != nullptr) {
|
||||||
|
// if the current clone was not already registered
|
||||||
|
if ( filters_.find(i) == filters_.end() )
|
||||||
|
// remember code for this clone
|
||||||
|
filters_[i] = i->program();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filters_.erase(i);
|
||||||
|
i = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
status_ = "-";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
status_ = "-";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
status_ = "-";
|
||||||
|
|
||||||
|
// change editor text only if current changed
|
||||||
|
if ( current_ != i)
|
||||||
|
{
|
||||||
|
// get the editor text and remove trailing '\n'
|
||||||
|
std::string code = _editor.GetText();
|
||||||
|
code = code.substr(0, code.size() -1);
|
||||||
|
|
||||||
|
// remember this code as buffered for the filter of this source
|
||||||
|
filters_[current_].setCode( { code, "" } );
|
||||||
|
|
||||||
|
// if switch to another shader code
|
||||||
|
if ( i != nullptr ) {
|
||||||
|
// change editor
|
||||||
|
_editor.SetText( filters_[i].code().first );
|
||||||
|
_editor.SetReadOnly(false);
|
||||||
|
status_ = "Ready.";
|
||||||
|
}
|
||||||
|
// cancel edit clone
|
||||||
|
else {
|
||||||
|
// cancel editor
|
||||||
|
_editor.SetText("");
|
||||||
|
_editor.SetReadOnly(true);
|
||||||
|
status_ = "-";
|
||||||
|
}
|
||||||
|
// current changed
|
||||||
|
current_ = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the window content in mono font
|
||||||
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||||
|
|
||||||
|
// render status message
|
||||||
|
ImGui::Text("Status: %s", status_.c_str());
|
||||||
|
|
||||||
|
// render shader input
|
||||||
|
if (show_shader_inputs_) {
|
||||||
|
ImGuiTextBuffer info;
|
||||||
|
info.append(FilteringProgram::getFilterCodeInputs().c_str());
|
||||||
|
|
||||||
|
// Show info text bloc (multi line, dark background)
|
||||||
|
ImGuiToolkit::PushFont( ImGuiToolkit::FONT_MONO );
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6, 0.6, 0.6, 0.9f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
|
||||||
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
|
ImGui::InputTextMultiline("##Info", (char *)info.c_str(), info.size(), ImVec2(-1, 8*ImGui::GetTextLineHeightWithSpacing()), ImGuiInputTextFlags_ReadOnly);
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
// sliders iMouse
|
||||||
|
ImGui::SliderFloat4("##iMouse", glm::value_ptr( FilteringProgram::iMouse ), 0.0, 1.0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
// special case for 'CTRL + B' keyboard shortcut
|
||||||
|
// the TextEditor captures keyboard focus from the main imgui context
|
||||||
|
// so UserInterface::handleKeyboard cannot capture this event:
|
||||||
|
// reading key press before render bypasses this problem
|
||||||
|
const ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl && ImGui::IsKeyPressed(io.KeyMap[ImGuiKey_A]+1))
|
||||||
|
BuildShader();
|
||||||
|
|
||||||
|
// render main editor
|
||||||
|
_editor.Render("Shader Editor");
|
||||||
|
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
35
src/ShaderEditWindow.h
Normal file
35
src/ShaderEditWindow.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef SHADEREDITWINDOW_H
|
||||||
|
#define SHADEREDITWINDOW_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <future>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "ImageFilter.h"
|
||||||
|
#include "WorkspaceWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ShaderEditWindow : public WorkspaceWindow
|
||||||
|
{
|
||||||
|
ImageFilter *current_;
|
||||||
|
std::map<ImageFilter *, FilteringProgram> filters_;
|
||||||
|
std::promise<std::string> *compilation_;
|
||||||
|
std::future<std::string> compilation_return_;
|
||||||
|
|
||||||
|
bool show_shader_inputs_;
|
||||||
|
std::string status_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ShaderEditWindow();
|
||||||
|
|
||||||
|
void Render();
|
||||||
|
void setVisible(bool on);
|
||||||
|
|
||||||
|
void BuildShader();
|
||||||
|
|
||||||
|
// from WorkspaceWindow
|
||||||
|
bool Visible() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SHADEREDITWINDOW_H
|
||||||
1940
src/SourceControlWindow.cpp
Normal file
1940
src/SourceControlWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
81
src/SourceControlWindow.h
Normal file
81
src/SourceControlWindow.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#ifndef SOURCECONTROLWINDOW_H
|
||||||
|
#define SOURCECONTROLWINDOW_H
|
||||||
|
|
||||||
|
struct ImVec2;
|
||||||
|
|
||||||
|
#include "SourceList.h"
|
||||||
|
#include "InfoVisitor.h"
|
||||||
|
#include "DialogToolkit.h"
|
||||||
|
#include "Screenshot.h"
|
||||||
|
#include "WorkspaceWindow.h"
|
||||||
|
|
||||||
|
class SourceControlWindow : public WorkspaceWindow
|
||||||
|
{
|
||||||
|
float min_width_;
|
||||||
|
float h_space_;
|
||||||
|
float v_space_;
|
||||||
|
float scrollbar_;
|
||||||
|
float timeline_height_;
|
||||||
|
float mediaplayer_height_;
|
||||||
|
float buttons_width_;
|
||||||
|
float buttons_height_;
|
||||||
|
|
||||||
|
bool play_toggle_request_, replay_request_, capture_request_;
|
||||||
|
bool pending_;
|
||||||
|
std::string active_label_;
|
||||||
|
int active_selection_;
|
||||||
|
InfoVisitor info_;
|
||||||
|
SourceList selection_;
|
||||||
|
|
||||||
|
// re-usable ui parts
|
||||||
|
void DrawButtonBar(ImVec2 bottom, float width);
|
||||||
|
int SourceButton(Source *s, ImVec2 framesize);
|
||||||
|
|
||||||
|
// Render the sources dynamically selected
|
||||||
|
void RenderSelectedSources();
|
||||||
|
|
||||||
|
// Render a stored selection
|
||||||
|
bool selection_context_menu_;
|
||||||
|
MediaPlayer *selection_mediaplayer_;
|
||||||
|
double selection_target_slower_;
|
||||||
|
double selection_target_faster_;
|
||||||
|
void RenderSelectionContextMenu();
|
||||||
|
void RenderSelection(size_t i);
|
||||||
|
|
||||||
|
// Render a single source
|
||||||
|
void RenderSingleSource(Source *s);
|
||||||
|
|
||||||
|
// Render a single media player
|
||||||
|
MediaPlayer *mediaplayer_active_;
|
||||||
|
bool mediaplayer_edit_fading_;
|
||||||
|
bool mediaplayer_edit_pipeline_;
|
||||||
|
bool mediaplayer_mode_;
|
||||||
|
bool mediaplayer_slider_pressed_;
|
||||||
|
float mediaplayer_timeline_zoom_;
|
||||||
|
void RenderMediaPlayer(MediaSource *ms);
|
||||||
|
|
||||||
|
// magnifying glass
|
||||||
|
bool magnifying_glass;
|
||||||
|
|
||||||
|
// dialog to select frame capture location
|
||||||
|
DialogToolkit::OpenFolderDialog *captureFolderDialog;
|
||||||
|
Screenshot capture;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SourceControlWindow();
|
||||||
|
|
||||||
|
inline void Play() { play_toggle_request_ = true; }
|
||||||
|
inline void Replay() { replay_request_= true; }
|
||||||
|
inline void Capture(){ capture_request_= true; }
|
||||||
|
void resetActiveSelection();
|
||||||
|
|
||||||
|
void setVisible(bool on);
|
||||||
|
void Render();
|
||||||
|
|
||||||
|
// from WorkspaceWindow
|
||||||
|
void Update() override;
|
||||||
|
bool Visible() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SOURCECONTROLWINDOW_H
|
||||||
323
src/TimerMetronomeWindow.cpp
Normal file
323
src/TimerMetronomeWindow.cpp
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of vimix - video live mixer
|
||||||
|
*
|
||||||
|
* **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
// ImGui
|
||||||
|
#include "ImGuiToolkit.h"
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "GstToolkit.h"
|
||||||
|
#include "Metronome.h"
|
||||||
|
|
||||||
|
#include "TimerMetronomeWindow.h"
|
||||||
|
|
||||||
|
#define PLOT_CIRCLE_SEGMENTS 64
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TimerMetronomeWindow::TimerMetronomeWindow() : WorkspaceWindow("Timer")
|
||||||
|
{
|
||||||
|
// timer modes : 0 Metronome, 1 Stopwatch
|
||||||
|
timer_menu = { "Metronome", "Stopwatch" };
|
||||||
|
// clock times
|
||||||
|
start_time_ = gst_util_get_timestamp ();
|
||||||
|
start_time_hand_ = gst_util_get_timestamp ();
|
||||||
|
duration_hand_ = Settings::application.timer.stopwatch_duration * GST_SECOND;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TimerMetronomeWindow::setVisible(bool on)
|
||||||
|
{
|
||||||
|
// restore workspace to show the window
|
||||||
|
if (WorkspaceWindow::clear_workspace_enabled) {
|
||||||
|
WorkspaceWindow::restoreWorkspace(on);
|
||||||
|
// do not change status if ask to hide (consider user asked to toggle because the window was not visible)
|
||||||
|
if (!on) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::application.widget.timer_view > 0 && Settings::application.widget.timer_view != Settings::application.current_view){
|
||||||
|
Settings::application.widget.timer_view = -1;
|
||||||
|
on = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::application.widget.timer = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimerMetronomeWindow::Visible() const
|
||||||
|
{
|
||||||
|
return ( Settings::application.widget.timer
|
||||||
|
&& (Settings::application.widget.timer_view < 0 || Settings::application.widget.timer_view == Settings::application.current_view )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerMetronomeWindow::Render()
|
||||||
|
{
|
||||||
|
// constraint square resizing
|
||||||
|
static ImVec2 timer_window_size_min = ImVec2(11.f * ImGui::GetTextLineHeight(), 11.f * ImGui::GetTextLineHeight());
|
||||||
|
ImGui::SetNextWindowSizeConstraints(timer_window_size_min, timer_window_size_min * 1.5f, ImGuiToolkit::CustomConstraints::Square);
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(600, 20), ImGuiCond_FirstUseEver);
|
||||||
|
ImGui::SetNextWindowSize(timer_window_size_min, ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
|
if ( !ImGui::Begin(name_, &Settings::application.widget.timer,
|
||||||
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse ))
|
||||||
|
{
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// menu (no title bar)
|
||||||
|
if (ImGui::BeginMenuBar())
|
||||||
|
{
|
||||||
|
// Close and widget menu
|
||||||
|
if (ImGuiToolkit::IconButton(4,16))
|
||||||
|
Settings::application.widget.timer = false;
|
||||||
|
if (ImGui::BeginMenu(IMGUI_TITLE_TIMER))
|
||||||
|
{
|
||||||
|
// Enable/Disable Ableton Link
|
||||||
|
if ( ImGui::MenuItem( ICON_FA_USER_CLOCK " Ableton Link", nullptr, &Settings::application.timer.link_enabled) ) {
|
||||||
|
Metronome::manager().setEnabled(Settings::application.timer.link_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// output manager menu
|
||||||
|
ImGui::Separator();
|
||||||
|
bool pinned = Settings::application.widget.timer_view == Settings::application.current_view;
|
||||||
|
std::string menutext = std::string( ICON_FA_MAP_PIN " Stick to ") + Settings::application.views[Settings::application.current_view].name + " view";
|
||||||
|
if ( ImGui::MenuItem( menutext.c_str(), nullptr, &pinned) ){
|
||||||
|
if (pinned)
|
||||||
|
Settings::application.widget.timer_view = Settings::application.current_view;
|
||||||
|
else
|
||||||
|
Settings::application.widget.timer_view = -1;
|
||||||
|
}
|
||||||
|
if ( ImGui::MenuItem( MENU_CLOSE, SHORTCUT_TIMER) )
|
||||||
|
Settings::application.widget.timer = false;
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
// Selection of the timer mode
|
||||||
|
if (ImGui::BeginMenu( timer_menu[Settings::application.timer.mode].c_str() ))
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < timer_menu.size(); ++i) {
|
||||||
|
if (ImGui::MenuItem(timer_menu[i].c_str(), NULL, Settings::application.timer.mode==i))
|
||||||
|
Settings::application.timer.mode = i;
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// current windowdraw parameters
|
||||||
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
|
ImDrawList* draw_list = window->DrawList;
|
||||||
|
// positions and size of GUI elements
|
||||||
|
const float margin = window->MenuBarHeight();
|
||||||
|
const float h = 0.4f * ImGui::GetFrameHeight();
|
||||||
|
const ImVec2 circle_top_left = window->Pos + ImVec2(margin + h, margin + h);
|
||||||
|
const ImVec2 circle_top_right = window->Pos + ImVec2(window->Size.y - margin - h, margin + h);
|
||||||
|
const ImVec2 circle_botom_right = window->Pos + ImVec2(window->Size.y - margin - h, window->Size.x - margin - h);
|
||||||
|
const ImVec2 circle_center = window->Pos + (window->Size + ImVec2(margin, margin) )/ 2.f;
|
||||||
|
const float circle_radius = (window->Size.y - 2.f * margin) / 2.f;
|
||||||
|
|
||||||
|
// color palette
|
||||||
|
static ImU32 colorbg = ImGui::GetColorU32(ImGuiCol_FrameBgActive, 0.6f);
|
||||||
|
static ImU32 colorfg = ImGui::GetColorU32(ImGuiCol_FrameBg, 2.5f);
|
||||||
|
static ImU32 colorline = ImGui::GetColorU32(ImGuiCol_PlotHistogram);
|
||||||
|
|
||||||
|
//
|
||||||
|
// METRONOME
|
||||||
|
//
|
||||||
|
if (Settings::application.timer.mode < 1) {
|
||||||
|
|
||||||
|
// Metronome info
|
||||||
|
double t = Metronome::manager().tempo();
|
||||||
|
double p = Metronome::manager().phase();
|
||||||
|
double q = Metronome::manager().quantum();
|
||||||
|
uint np = (int) Metronome::manager().peers();
|
||||||
|
|
||||||
|
// draw background ring
|
||||||
|
draw_list->AddCircleFilled(circle_center, circle_radius, colorbg, PLOT_CIRCLE_SEGMENTS);
|
||||||
|
|
||||||
|
// draw quarter
|
||||||
|
static const float resolution = PLOT_CIRCLE_SEGMENTS / (2.f * M_PI);
|
||||||
|
static ImVec2 buffer[PLOT_CIRCLE_SEGMENTS];
|
||||||
|
float a0 = -M_PI_2 + (floor(p)/floor(q)) * (2.f * M_PI);
|
||||||
|
float a1 = a0 + (1.f / floor(q)) * (2.f * M_PI);
|
||||||
|
int n = ImMax(3, (int)((a1 - a0) * resolution));
|
||||||
|
double da = (a1 - a0) / (n - 1);
|
||||||
|
int index = 0;
|
||||||
|
buffer[index++] = circle_center;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
double a = a0 + i * da;
|
||||||
|
buffer[index++] = ImVec2(circle_center.x + circle_radius * cos(a), circle_center.y + circle_radius * sin(a));
|
||||||
|
}
|
||||||
|
draw_list->AddConvexPolyFilled(buffer, index, colorfg);
|
||||||
|
|
||||||
|
// draw clock hand
|
||||||
|
a0 = -M_PI_2 + (p/q) * (2.f * M_PI);
|
||||||
|
draw_list->AddLine(ImVec2(circle_center.x + margin * cos(a0), circle_center.y + margin * sin(a0)),
|
||||||
|
ImVec2(circle_center.x + circle_radius * cos(a0), circle_center.y + circle_radius * sin(a0)), colorline, 2.f);
|
||||||
|
|
||||||
|
// centered indicator 'x / N'
|
||||||
|
draw_list->AddCircleFilled(circle_center, margin, colorfg, PLOT_CIRCLE_SEGMENTS);
|
||||||
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||||
|
char text_buf[24];
|
||||||
|
sprintf(text_buf, "%d/%d", (int)(p)+1, (int)(q) );
|
||||||
|
ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL);
|
||||||
|
ImGui::SetCursorScreenPos(circle_center - label_size/2);
|
||||||
|
ImGui::Text("%s", text_buf);
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
// left slider : quantum
|
||||||
|
float float_value = ceil(Metronome::manager().quantum());
|
||||||
|
ImGui::SetCursorPos(ImVec2(0.5f * margin, 1.5f * margin));
|
||||||
|
if ( ImGui::VSliderFloat("##quantum", ImVec2(0.5f * margin, 2.f * circle_radius ), &float_value, 2, 200, "", 2.f) ){
|
||||||
|
Metronome::manager().setQuantum( ceil(float_value) );
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered() || ImGui::IsItemActive() ) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
guint64 time_phase = GST_SECOND * (60.0 * q / t) ;
|
||||||
|
ImGui::Text("%d beats per phase\n= %s at %d BPM", (int) ceil(float_value),
|
||||||
|
GstToolkit::time_to_string(time_phase, GstToolkit::TIME_STRING_READABLE).c_str(), (int) t);
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls NOT operational if peer connected
|
||||||
|
if (np >0 ) {
|
||||||
|
// Tempo
|
||||||
|
ImGui::SetCursorScreenPos(circle_top_right);
|
||||||
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, colorfg);
|
||||||
|
sprintf(text_buf, "%d", (int) ceil(t) );
|
||||||
|
ImGui::Text("%s", text_buf);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopFont();
|
||||||
|
if (ImGui::IsItemHovered()){
|
||||||
|
sprintf(text_buf, "%d BPM\n(set by peer)", (int) ceil(t));
|
||||||
|
ImGuiToolkit::ToolTip(text_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Controls operational only if no peer
|
||||||
|
else {
|
||||||
|
// Tempo
|
||||||
|
ImGui::SetCursorScreenPos(circle_top_right);
|
||||||
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
|
||||||
|
sprintf(text_buf, "%d", (int) ceil(t) );
|
||||||
|
ImGui::Text("%s", text_buf);
|
||||||
|
ImGui::PopFont();
|
||||||
|
if (ImGui::IsItemClicked())
|
||||||
|
ImGui::OpenPopup("bpm_popup");
|
||||||
|
else if (ImGui::IsItemHovered()){
|
||||||
|
sprintf(text_buf, "%d BPM\n(clic to edit)", (int) ceil(t));
|
||||||
|
ImGuiToolkit::ToolTip(text_buf);
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("bpm_popup", ImGuiWindowFlags_NoMove))
|
||||||
|
{
|
||||||
|
ImGui::SetNextItemWidth(80);
|
||||||
|
ImGui::InputText("BPM", text_buf, 8, ImGuiInputTextFlags_CharsDecimal);
|
||||||
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||||
|
int t = 0;
|
||||||
|
sscanf(text_buf, "%d", &t);
|
||||||
|
t = CLAMP(t, 20, 2000);
|
||||||
|
Metronome::manager().setTempo((double) t);
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
// restart icon
|
||||||
|
ImGui::SetCursorScreenPos(circle_top_left);
|
||||||
|
if (ImGuiToolkit::IconButton(9, 13)) {
|
||||||
|
Metronome::manager().restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network indicator, if link enabled
|
||||||
|
if (Settings::application.timer.link_enabled) {
|
||||||
|
ImGui::SetCursorScreenPos(circle_botom_right);
|
||||||
|
ImGuiToolkit::Icon(16, 5, np > 0);
|
||||||
|
if (ImGui::IsItemHovered()){
|
||||||
|
sprintf(text_buf, np < 1 ? "Ableton Link\nNo peer" : "Ableton Link\n%d peer%c", np, np < 2 ? ' ' : 's' );
|
||||||
|
ImGuiToolkit::ToolTip(text_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// STOPWATCH
|
||||||
|
//
|
||||||
|
else {
|
||||||
|
guint64 time_ = gst_util_get_timestamp ();
|
||||||
|
|
||||||
|
// draw ring
|
||||||
|
draw_list->AddCircle(circle_center, circle_radius, colorbg, PLOT_CIRCLE_SEGMENTS, 12 );
|
||||||
|
draw_list->AddCircleFilled(ImVec2(circle_center.x, circle_center.y - circle_radius), 7, colorfg, PLOT_CIRCLE_SEGMENTS);
|
||||||
|
// draw indicator time hand
|
||||||
|
double da = -M_PI_2 + ( (double) (time_-start_time_hand_) / (double) duration_hand_) * (2.f * M_PI);
|
||||||
|
draw_list->AddCircleFilled(ImVec2(circle_center.x + circle_radius * cos(da), circle_center.y + circle_radius * sin(da)), 7, colorline, PLOT_CIRCLE_SEGMENTS);
|
||||||
|
|
||||||
|
// left slider : countdown
|
||||||
|
float float_value = (float) Settings::application.timer.stopwatch_duration;
|
||||||
|
ImGui::SetCursorPos(ImVec2(0.5f * margin, 1.5f * margin));
|
||||||
|
if ( ImGui::VSliderFloat("##duration", ImVec2(0.5f * margin, 2.f * circle_radius ), &float_value, 1, 3600, "", 3.f) ){
|
||||||
|
Settings::application.timer.stopwatch_duration = (uint64_t) float_value;
|
||||||
|
duration_hand_ = Settings::application.timer.stopwatch_duration * GST_SECOND;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered() || ImGui::IsItemActive()) {
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::Text("%s\ncountdown", GstToolkit::time_to_string(duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() );
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
// main text: elapsed time
|
||||||
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
|
||||||
|
char text_buf[24];
|
||||||
|
sprintf(text_buf, "%s", GstToolkit::time_to_string(time_-start_time_, GstToolkit::TIME_STRING_FIXED).c_str() );
|
||||||
|
ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL);
|
||||||
|
ImGui::SetCursorScreenPos(circle_center - label_size/2);
|
||||||
|
ImGui::Text("%s", text_buf);
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
// small text: remaining time
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, colorfg);
|
||||||
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
|
||||||
|
|
||||||
|
guint64 lap = (time_-start_time_hand_)/duration_hand_;
|
||||||
|
sprintf(text_buf, "%ld turn%s", lap, lap > 1 ? "s" : " " );
|
||||||
|
label_size = ImGui::CalcTextSize(text_buf, NULL);
|
||||||
|
ImGui::SetCursorScreenPos(circle_center + ImVec2(0.f, circle_radius * -0.7f) - label_size/2);
|
||||||
|
ImGui::Text("%s", text_buf);
|
||||||
|
|
||||||
|
sprintf(text_buf, "%s", GstToolkit::time_to_string(duration_hand_-(time_-start_time_hand_)%duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() );
|
||||||
|
label_size = ImGui::CalcTextSize(text_buf, NULL);
|
||||||
|
ImGui::SetCursorScreenPos(circle_center - ImVec2(0.f, circle_radius * -0.7f) - label_size/2);
|
||||||
|
ImGui::Text("%s", text_buf);
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
// reset icon
|
||||||
|
ImGui::SetCursorScreenPos(circle_top_left);
|
||||||
|
if (ImGuiToolkit::IconButton(8, 13))
|
||||||
|
start_time_ = start_time_hand_ = time_; // reset timers
|
||||||
|
|
||||||
|
// TODO : pause ?
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
31
src/TimerMetronomeWindow.h
Normal file
31
src/TimerMetronomeWindow.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef TIMERMETRONOMEWINDOW_H
|
||||||
|
#define TIMERMETRONOMEWINDOW_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <gst/gstutils.h>
|
||||||
|
|
||||||
|
#include "WorkspaceWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TimerMetronomeWindow : public WorkspaceWindow
|
||||||
|
{
|
||||||
|
std::array< std::string, 2 > timer_menu;
|
||||||
|
// clock times
|
||||||
|
guint64 start_time_;
|
||||||
|
guint64 start_time_hand_;
|
||||||
|
guint64 duration_hand_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TimerMetronomeWindow();
|
||||||
|
|
||||||
|
void Render();
|
||||||
|
void setVisible(bool on);
|
||||||
|
|
||||||
|
// from WorkspaceWindow
|
||||||
|
bool Visible() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TIMERMETRONOMEWINDOW_H
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,136 +1,28 @@
|
|||||||
#ifndef __UI_MANAGER_H_
|
#ifndef __UI_MANAGER_H_
|
||||||
#define __UI_MANAGER_H_
|
#define __UI_MANAGER_H_
|
||||||
|
|
||||||
#include "Session.h"
|
|
||||||
#include <string>
|
|
||||||
#include <array>
|
|
||||||
#include <list>
|
|
||||||
#include <future>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#include <gst/gstutils.h>
|
|
||||||
|
|
||||||
#define NAV_COUNT 68
|
#define NAV_COUNT 68
|
||||||
#define NAV_MAX 64
|
#define NAV_MAX 64
|
||||||
#define NAV_NEW 65
|
#define NAV_NEW 65
|
||||||
#define NAV_MENU 66
|
#define NAV_MENU 66
|
||||||
#define NAV_TRANS 67
|
#define NAV_TRANS 67
|
||||||
|
|
||||||
#ifdef APPLE
|
#include <string>
|
||||||
#define CTRL_MOD "Cmd+"
|
|
||||||
#define ALT_MOD "Option+"
|
|
||||||
#else
|
|
||||||
#define CTRL_MOD "Ctrl+"
|
|
||||||
#define ALT_MOD "Alt+"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_PLAY_CIRCLE " Player"
|
#include "View.h"
|
||||||
#define IMGUI_TITLE_TIMER ICON_FA_CLOCK " Timer"
|
|
||||||
#define IMGUI_TITLE_INPUT_MAPPING ICON_FA_HAND_PAPER " Input Mapping"
|
|
||||||
#define IMGUI_TITLE_HELP ICON_FA_LIFE_RING " Help"
|
|
||||||
#define IMGUI_TITLE_TOOLBOX ICON_FA_HAMSA " Guru Toolbox"
|
|
||||||
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Shader Editor"
|
|
||||||
#define IMGUI_TITLE_PREVIEW ICON_FA_LAPTOP " Display"
|
|
||||||
|
|
||||||
#define MENU_NEW_FILE ICON_FA_FILE " New"
|
|
||||||
#define SHORTCUT_NEW_FILE CTRL_MOD "W"
|
|
||||||
#define MENU_OPEN_FILE ICON_FA_FILE_UPLOAD " Open"
|
|
||||||
#define SHORTCUT_OPEN_FILE CTRL_MOD "O"
|
|
||||||
#define MENU_REOPEN_FILE ICON_FA_FILE_UPLOAD " Re-open"
|
|
||||||
#define SHORTCUT_REOPEN_FILE CTRL_MOD "Shift+O"
|
|
||||||
#define MENU_SAVE_FILE ICON_FA_FILE_DOWNLOAD " Save"
|
|
||||||
#define SHORTCUT_SAVE_FILE CTRL_MOD "S"
|
|
||||||
#define MENU_SAVEAS_FILE ICON_FA_FILE_DOWNLOAD " Save as"
|
|
||||||
#define MENU_SAVE_ON_EXIT ICON_FA_LEVEL_DOWN_ALT " Save on exit"
|
|
||||||
#define MENU_OPEN_ON_START ICON_FA_LEVEL_UP_ALT " Restore on start"
|
|
||||||
#define SHORTCUT_SAVEAS_FILE CTRL_MOD "Shift+S"
|
|
||||||
#define MENU_QUIT ICON_FA_POWER_OFF " Quit"
|
|
||||||
#define SHORTCUT_QUIT CTRL_MOD "Q"
|
|
||||||
#define MENU_CUT ICON_FA_CUT " Cut"
|
|
||||||
#define SHORTCUT_CUT CTRL_MOD "X"
|
|
||||||
#define MENU_COPY ICON_FA_COPY " Copy"
|
|
||||||
#define SHORTCUT_COPY CTRL_MOD "C"
|
|
||||||
#define MENU_DELETE ICON_FA_ERASER " Delete"
|
|
||||||
#define SHORTCUT_DELETE "Del"
|
|
||||||
#define ACTION_DELETE ICON_FA_BACKSPACE " Delete"
|
|
||||||
#define MENU_PASTE ICON_FA_PASTE " Paste"
|
|
||||||
#define SHORTCUT_PASTE CTRL_MOD "V"
|
|
||||||
#define MENU_SELECTALL ICON_FA_TH_LIST " Select all"
|
|
||||||
#define SHORTCUT_SELECTALL CTRL_MOD "A"
|
|
||||||
#define MENU_UNDO ICON_FA_UNDO " Undo"
|
|
||||||
#define SHORTCUT_UNDO CTRL_MOD "Z"
|
|
||||||
#define MENU_REDO ICON_FA_REDO " Redo"
|
|
||||||
#define SHORTCUT_REDO CTRL_MOD "Shift+Z"
|
|
||||||
#define MENU_RECORD ICON_FA_CIRCLE " Record"
|
|
||||||
#define SHORTCUT_RECORD CTRL_MOD "R"
|
|
||||||
#define MENU_RECORDCONT ICON_FA_STOP_CIRCLE " Save & continue"
|
|
||||||
#define SHORTCUT_RECORDCONT CTRL_MOD "Alt+R"
|
|
||||||
#define MENU_CAPTUREFRAME ICON_FA_CAMERA_RETRO " Capture frame"
|
|
||||||
#define SHORTCUT_CAPTURE_DISPLAY "F11"
|
|
||||||
#define SHORTCUT_CAPTURE_PLAYER "F10"
|
|
||||||
#define MENU_CAPTUREGUI ICON_FA_CAMERA " Screenshot vimix"
|
|
||||||
#define SHORTCUT_CAPTURE_GUI "F9"
|
|
||||||
#define MENU_OUTPUTDISABLE ICON_FA_EYE_SLASH " Disable"
|
|
||||||
#define SHORTCUT_OUTPUTDISABLE "F12"
|
|
||||||
#define MENU_LARGEPREVIEW ICON_FA_EXPAND_ARROWS_ALT " Large preview"
|
|
||||||
#define SHORTCUT_LARGEPREVIEW "F6"
|
|
||||||
#define MENU_CLOSE ICON_FA_TIMES " Close"
|
|
||||||
#define DIALOG_FAILED_SOURCE ICON_FA_EXCLAMATION_TRIANGLE " Source failure"
|
|
||||||
|
|
||||||
#define MENU_NOTE ICON_FA_STICKY_NOTE " Add sticky note"
|
|
||||||
#define MENU_METRICS ICON_FA_TACHOMETER_ALT " Metrics"
|
|
||||||
#define MENU_SOURCE_TOOL ICON_FA_WRENCH " Source toolbar"
|
|
||||||
#define MENU_HELP ICON_FA_LIFE_RING " Help"
|
|
||||||
#define SHORTCUT_HELP CTRL_MOD "H"
|
|
||||||
#define MENU_LOGS ICON_FA_LIST_UL " Logs"
|
|
||||||
#define SHORTCUT_LOGS CTRL_MOD "L"
|
|
||||||
#define TOOLTIP_PLAYER "Player "
|
|
||||||
#define SHORTCUT_PLAYER CTRL_MOD "P"
|
|
||||||
#define TOOLTIP_OUTPUT "Display "
|
|
||||||
#define SHORTCUT_OUTPUT CTRL_MOD "D"
|
|
||||||
#define TOOLTIP_TIMER "Timer "
|
|
||||||
#define SHORTCUT_TIMER CTRL_MOD "T"
|
|
||||||
#define TOOLTIP_INPUTS "Inputs mapping "
|
|
||||||
#define SHORTCUT_INPUTS CTRL_MOD "I"
|
|
||||||
#define TOOLTIP_SHADEREDITOR "Shader Editor "
|
|
||||||
#define SHORTCUT_SHADEREDITOR CTRL_MOD "E"
|
|
||||||
#define TOOLTIP_FULLSCREEN "Fullscreen "
|
|
||||||
#define SHORTCUT_FULLSCREEN CTRL_MOD "F"
|
|
||||||
#define TOOLTIP_MAIN "Main menu "
|
|
||||||
#define SHORTCUT_MAIN "HOME"
|
|
||||||
#define TOOLTIP_NEW_SOURCE "New source "
|
|
||||||
#define SHORTCUT_NEW_SOURCE "INS"
|
|
||||||
#define TOOLTIP_HIDE "Hide windows "
|
|
||||||
#define TOOLTIP_SHOW "Show windows "
|
|
||||||
#define SHORTCUT_HIDE "ESC"
|
|
||||||
#define TOOLTIP_PANEL_VISIBLE "Panel always visible "
|
|
||||||
#define TOOLTIP_PANEL_AUTO "Panel auto hide "
|
|
||||||
#define SHORTCUT_PANEL_MODE "HOME"
|
|
||||||
|
|
||||||
#define LABEL_AUTO_MEDIA_PLAYER ICON_FA_USER_CIRCLE " User selection"
|
|
||||||
#define LABEL_STORE_SELECTION " Create batch"
|
|
||||||
#define LABEL_EDIT_FADING ICON_FA_RANDOM " Fade in & out"
|
|
||||||
#define LABEL_VIDEO_SEQUENCE " Encode an image sequence"
|
|
||||||
|
|
||||||
#include "SourceList.h"
|
|
||||||
#include "InfoVisitor.h"
|
|
||||||
#include "DialogToolkit.h"
|
#include "DialogToolkit.h"
|
||||||
#include "SessionParser.h"
|
#include "SourceControlWindow.h"
|
||||||
#include "ImageFilter.h"
|
#include "OutputPreviewWindow.h"
|
||||||
#include "Screenshot.h"
|
#include "TimerMetronomeWindow.h"
|
||||||
|
#include "InputMappingWindow.h"
|
||||||
|
#include "ShaderEditWindow.h"
|
||||||
|
|
||||||
|
|
||||||
struct ImVec2;
|
struct ImVec2;
|
||||||
class MediaPlayer;
|
void DrawInspector(uint texture, ImVec2 texturesize, ImVec2 cropsize, ImVec2 origin);
|
||||||
class FrameBufferImage;
|
|
||||||
class FrameGrabber;
|
|
||||||
class VideoRecorder;
|
|
||||||
class VideoBroadcast;
|
|
||||||
class ShmdataBroadcast;
|
|
||||||
class Loopback;
|
|
||||||
|
|
||||||
class SourcePreview {
|
|
||||||
|
|
||||||
|
class SourcePreview
|
||||||
|
{
|
||||||
Source *source_;
|
Source *source_;
|
||||||
std::string label_;
|
std::string label_;
|
||||||
bool reset_;
|
bool reset_;
|
||||||
@@ -146,20 +38,6 @@ public:
|
|||||||
inline bool filled() const { return source_ != nullptr; }
|
inline bool filled() const { return source_ != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Thumbnail
|
|
||||||
{
|
|
||||||
float aspect_ratio_;
|
|
||||||
uint texture_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Thumbnail();
|
|
||||||
~Thumbnail();
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
void fill (const FrameBufferImage *image);
|
|
||||||
bool filled();
|
|
||||||
void Render(float width);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Navigator
|
class Navigator
|
||||||
{
|
{
|
||||||
@@ -246,217 +124,11 @@ public:
|
|||||||
void Render();
|
void Render();
|
||||||
};
|
};
|
||||||
|
|
||||||
class HelperToolbox
|
|
||||||
{
|
|
||||||
SessionParser parser_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
HelperToolbox();
|
|
||||||
|
|
||||||
void Render();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class WorkspaceWindow
|
|
||||||
{
|
|
||||||
static std::list<WorkspaceWindow *> windows_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
WorkspaceWindow(const char* name);
|
|
||||||
|
|
||||||
// global access to Workspace control
|
|
||||||
static bool clear() { return clear_workspace_enabled; }
|
|
||||||
static void toggleClearRestoreWorkspace();
|
|
||||||
static void clearWorkspace();
|
|
||||||
static void restoreWorkspace(bool instantaneous = false);
|
|
||||||
static void notifyWorkspaceSizeChanged(int prev_width, int prev_height, int curr_width, int curr_height);
|
|
||||||
|
|
||||||
// for inherited classes
|
|
||||||
virtual void Update();
|
|
||||||
virtual bool Visible() const { return true; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
static bool clear_workspace_enabled;
|
|
||||||
|
|
||||||
const char *name_;
|
|
||||||
struct ImGuiProperties *impl_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SourceController : public WorkspaceWindow
|
|
||||||
{
|
|
||||||
float min_width_;
|
|
||||||
float h_space_;
|
|
||||||
float v_space_;
|
|
||||||
float scrollbar_;
|
|
||||||
float timeline_height_;
|
|
||||||
float mediaplayer_height_;
|
|
||||||
float buttons_width_;
|
|
||||||
float buttons_height_;
|
|
||||||
|
|
||||||
bool play_toggle_request_, replay_request_, capture_request_;
|
|
||||||
bool pending_;
|
|
||||||
std::string active_label_;
|
|
||||||
int active_selection_;
|
|
||||||
InfoVisitor info_;
|
|
||||||
SourceList selection_;
|
|
||||||
|
|
||||||
// re-usable ui parts
|
|
||||||
void DrawButtonBar(ImVec2 bottom, float width);
|
|
||||||
int SourceButton(Source *s, ImVec2 framesize);
|
|
||||||
|
|
||||||
// Render the sources dynamically selected
|
|
||||||
void RenderSelectedSources();
|
|
||||||
|
|
||||||
// Render a stored selection
|
|
||||||
bool selection_context_menu_;
|
|
||||||
MediaPlayer *selection_mediaplayer_;
|
|
||||||
double selection_target_slower_;
|
|
||||||
double selection_target_faster_;
|
|
||||||
void RenderSelectionContextMenu();
|
|
||||||
void RenderSelection(size_t i);
|
|
||||||
|
|
||||||
// Render a single source
|
|
||||||
void RenderSingleSource(Source *s);
|
|
||||||
|
|
||||||
// Render a single media player
|
|
||||||
MediaPlayer *mediaplayer_active_;
|
|
||||||
bool mediaplayer_edit_fading_;
|
|
||||||
bool mediaplayer_edit_pipeline_;
|
|
||||||
bool mediaplayer_mode_;
|
|
||||||
bool mediaplayer_slider_pressed_;
|
|
||||||
float mediaplayer_timeline_zoom_;
|
|
||||||
void RenderMediaPlayer(MediaSource *ms);
|
|
||||||
|
|
||||||
// magnifying glass
|
|
||||||
bool magnifying_glass;
|
|
||||||
|
|
||||||
// dialog to select frame capture location
|
|
||||||
DialogToolkit::OpenFolderDialog *captureFolderDialog;
|
|
||||||
Screenshot capture;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SourceController();
|
|
||||||
|
|
||||||
inline void Play() { play_toggle_request_ = true; }
|
|
||||||
inline void Replay() { replay_request_= true; }
|
|
||||||
inline void Capture(){ capture_request_= true; }
|
|
||||||
void resetActiveSelection();
|
|
||||||
|
|
||||||
void setVisible(bool on);
|
|
||||||
void Render();
|
|
||||||
|
|
||||||
// from WorkspaceWindow
|
|
||||||
void Update() override;
|
|
||||||
bool Visible() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OutputPreview : public WorkspaceWindow
|
|
||||||
{
|
|
||||||
// frame grabbers
|
|
||||||
VideoRecorder *video_recorder_;
|
|
||||||
VideoBroadcast *video_broadcaster_;
|
|
||||||
ShmdataBroadcast *shm_broadcaster_;
|
|
||||||
Loopback *loopback_broadcaster_;
|
|
||||||
|
|
||||||
// delayed trigger for recording
|
|
||||||
std::vector< std::future<VideoRecorder *> > _video_recorders;
|
|
||||||
|
|
||||||
// dialog to select record location
|
|
||||||
DialogToolkit::OpenFolderDialog *recordFolderDialog;
|
|
||||||
|
|
||||||
// magnifying glass
|
|
||||||
bool magnifying_glass;
|
|
||||||
|
|
||||||
public:
|
|
||||||
OutputPreview();
|
|
||||||
|
|
||||||
void ToggleRecord(bool save_and_continue = false);
|
|
||||||
inline bool isRecording() const { return video_recorder_ != nullptr; }
|
|
||||||
|
|
||||||
void ToggleVideoBroadcast();
|
|
||||||
inline bool videoBroadcastEnabled() const { return video_broadcaster_ != nullptr; }
|
|
||||||
|
|
||||||
void ToggleSharedMemory();
|
|
||||||
inline bool sharedMemoryEnabled() const { return shm_broadcaster_ != nullptr; }
|
|
||||||
|
|
||||||
bool ToggleLoopbackCamera();
|
|
||||||
inline bool loopbackCameraEnabled() const { return loopback_broadcaster_!= nullptr; }
|
|
||||||
|
|
||||||
void Render();
|
|
||||||
void setVisible(bool on);
|
|
||||||
|
|
||||||
// from WorkspaceWindow
|
|
||||||
void Update() override;
|
|
||||||
bool Visible() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TimerMetronome : public WorkspaceWindow
|
|
||||||
{
|
|
||||||
std::array< std::string, 2 > timer_menu;
|
|
||||||
// clock times
|
|
||||||
guint64 start_time_;
|
|
||||||
guint64 start_time_hand_;
|
|
||||||
guint64 duration_hand_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
TimerMetronome();
|
|
||||||
|
|
||||||
void Render();
|
|
||||||
void setVisible(bool on);
|
|
||||||
|
|
||||||
// from WorkspaceWindow
|
|
||||||
bool Visible() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class InputMappingInterface : public WorkspaceWindow
|
|
||||||
{
|
|
||||||
std::array< std::string, 4 > input_mode;
|
|
||||||
std::array< uint, 4 > current_input_for_mode;
|
|
||||||
uint current_input_;
|
|
||||||
|
|
||||||
Target ComboSelectTarget(const Target ¤t);
|
|
||||||
uint ComboSelectCallback(uint current, bool imageprocessing);
|
|
||||||
void SliderParametersCallback(SourceCallback *callback, const Target &target);
|
|
||||||
|
|
||||||
public:
|
|
||||||
InputMappingInterface();
|
|
||||||
|
|
||||||
void Render();
|
|
||||||
void setVisible(bool on);
|
|
||||||
|
|
||||||
// from WorkspaceWindow
|
|
||||||
bool Visible() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShaderEditor : public WorkspaceWindow
|
|
||||||
{
|
|
||||||
ImageFilter *current_;
|
|
||||||
std::map<ImageFilter *, FilteringProgram> filters_;
|
|
||||||
std::promise<std::string> *compilation_;
|
|
||||||
std::future<std::string> compilation_return_;
|
|
||||||
|
|
||||||
bool show_shader_inputs_;
|
|
||||||
std::string status_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ShaderEditor();
|
|
||||||
|
|
||||||
void Render();
|
|
||||||
void setVisible(bool on);
|
|
||||||
|
|
||||||
void BuildShader();
|
|
||||||
|
|
||||||
// from WorkspaceWindow
|
|
||||||
bool Visible() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class UserInterface
|
class UserInterface
|
||||||
{
|
{
|
||||||
friend class ImGuiVisitor;
|
friend class ImGuiVisitor;
|
||||||
friend class Navigator;
|
friend class Navigator;
|
||||||
friend class OutputPreview;
|
friend class OutputPreviewWindow;
|
||||||
|
|
||||||
// Private Constructor
|
// Private Constructor
|
||||||
UserInterface();
|
UserInterface();
|
||||||
@@ -525,12 +197,11 @@ protected:
|
|||||||
// objects and windows
|
// objects and windows
|
||||||
Navigator navigator;
|
Navigator navigator;
|
||||||
ToolBox toolbox;
|
ToolBox toolbox;
|
||||||
SourceController sourcecontrol;
|
SourceControlWindow sourcecontrol;
|
||||||
OutputPreview outputcontrol;
|
OutputPreviewWindow outputcontrol;
|
||||||
TimerMetronome timercontrol;
|
TimerMetronomeWindow timercontrol;
|
||||||
InputMappingInterface inputscontrol;
|
InputMappingWindow inputscontrol;
|
||||||
ShaderEditor shadercontrol;
|
ShaderEditWindow shadercontrol;
|
||||||
HelperToolbox helpwindow;
|
|
||||||
|
|
||||||
void showMenuFile();
|
void showMenuFile();
|
||||||
void showMenuEdit();
|
void showMenuEdit();
|
||||||
@@ -544,6 +215,7 @@ protected:
|
|||||||
void RenderOutputView();
|
void RenderOutputView();
|
||||||
void RenderAbout(bool* p_open);
|
void RenderAbout(bool* p_open);
|
||||||
void RenderNotes();
|
void RenderNotes();
|
||||||
|
void RenderHelp();
|
||||||
|
|
||||||
void setView(View::Mode mode);
|
void setView(View::Mode mode);
|
||||||
void handleKeyboard();
|
void handleKeyboard();
|
||||||
|
|||||||
228
src/WorkspaceWindow.cpp
Normal file
228
src/WorkspaceWindow.cpp
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of vimix - video live mixer
|
||||||
|
*
|
||||||
|
* **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "WorkspaceWindow.h"
|
||||||
|
|
||||||
|
bool WorkspaceWindow::clear_workspace_enabled = false;
|
||||||
|
std::list<WorkspaceWindow *> WorkspaceWindow::windows_;
|
||||||
|
|
||||||
|
struct ImGuiProperties
|
||||||
|
{
|
||||||
|
ImGuiWindow *ptr;
|
||||||
|
ImVec2 user_pos;
|
||||||
|
ImVec2 outside_pos;
|
||||||
|
float progress; // [0 1]
|
||||||
|
float target; // 1 go to outside, 0 go to user pos
|
||||||
|
bool animation; // need animation
|
||||||
|
bool resizing_workspace;
|
||||||
|
ImVec2 resized_pos;
|
||||||
|
|
||||||
|
ImGuiProperties ()
|
||||||
|
{
|
||||||
|
ptr = nullptr;
|
||||||
|
progress = 0.f;
|
||||||
|
target = 0.f;
|
||||||
|
animation = false;
|
||||||
|
resizing_workspace = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WorkspaceWindow::WorkspaceWindow(const char* name): name_(name), impl_(nullptr)
|
||||||
|
{
|
||||||
|
WorkspaceWindow::windows_.push_back(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceWindow::toggleClearRestoreWorkspace()
|
||||||
|
{
|
||||||
|
// stop animations that are ongoing
|
||||||
|
for(auto it = windows_.cbegin(); it != windows_.cend(); ++it) {
|
||||||
|
if ( (*it)->impl_ && (*it)->impl_->animation )
|
||||||
|
(*it)->impl_->animation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle
|
||||||
|
if (clear_workspace_enabled)
|
||||||
|
restoreWorkspace();
|
||||||
|
else
|
||||||
|
clearWorkspace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceWindow::restoreWorkspace(bool instantaneous)
|
||||||
|
{
|
||||||
|
if (clear_workspace_enabled) {
|
||||||
|
const ImVec2 display_size = ImGui::GetIO().DisplaySize;
|
||||||
|
for(auto it = windows_.cbegin(); it != windows_.cend(); ++it) {
|
||||||
|
ImGuiProperties *impl = (*it)->impl_;
|
||||||
|
if (impl && impl->ptr)
|
||||||
|
{
|
||||||
|
float margin = (impl->ptr->MenuBarHeight() + impl->ptr->TitleBarHeight()) * 3.f;
|
||||||
|
impl->user_pos.x = CLAMP(impl->user_pos.x, -impl->ptr->SizeFull.x +margin, display_size.x -margin);
|
||||||
|
impl->user_pos.y = CLAMP(impl->user_pos.y, -impl->ptr->SizeFull.y +margin, display_size.y -margin);
|
||||||
|
|
||||||
|
if (instantaneous) {
|
||||||
|
impl->animation = false;
|
||||||
|
ImGui::SetWindowPos(impl->ptr, impl->user_pos);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// remember outside position before move
|
||||||
|
impl->outside_pos = impl->ptr->Pos;
|
||||||
|
|
||||||
|
// initialize animation
|
||||||
|
impl->progress = 1.f;
|
||||||
|
impl->target = 0.f;
|
||||||
|
impl->animation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear_workspace_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceWindow::clearWorkspace()
|
||||||
|
{
|
||||||
|
if (!clear_workspace_enabled) {
|
||||||
|
const ImVec2 display_size = ImGui::GetIO().DisplaySize;
|
||||||
|
for(auto it = windows_.cbegin(); it != windows_.cend(); ++it) {
|
||||||
|
ImGuiProperties *impl = (*it)->impl_;
|
||||||
|
if (impl && impl->ptr)
|
||||||
|
{
|
||||||
|
// remember user position before move
|
||||||
|
impl->user_pos = impl->ptr->Pos;
|
||||||
|
|
||||||
|
// init before decision
|
||||||
|
impl->outside_pos = impl->ptr->Pos;
|
||||||
|
|
||||||
|
// distance to right side, top & bottom
|
||||||
|
float right = display_size.x - (impl->ptr->Pos.x + impl->ptr->SizeFull.x * 0.7);
|
||||||
|
float top = impl->ptr->Pos.y;
|
||||||
|
float bottom = display_size.y - (impl->ptr->Pos.y + impl->ptr->SizeFull.y);
|
||||||
|
|
||||||
|
// move to closest border, with a margin to keep visible
|
||||||
|
float margin = (impl->ptr->MenuBarHeight() + impl->ptr->TitleBarHeight()) * 1.5f;
|
||||||
|
if (top < bottom && top < right)
|
||||||
|
impl->outside_pos.y = margin - impl->ptr->SizeFull.y;
|
||||||
|
else if (right < top && right < bottom)
|
||||||
|
impl->outside_pos.x = display_size.x - margin;
|
||||||
|
else
|
||||||
|
impl->outside_pos.y = display_size.y - margin;
|
||||||
|
|
||||||
|
// initialize animation
|
||||||
|
impl->progress = 0.f;
|
||||||
|
impl->target = 1.f;
|
||||||
|
impl->animation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear_workspace_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceWindow::notifyWorkspaceSizeChanged(int prev_width, int prev_height, int curr_width, int curr_height)
|
||||||
|
{
|
||||||
|
// restore windows pos before rescale
|
||||||
|
restoreWorkspace(true);
|
||||||
|
|
||||||
|
for(auto it = windows_.cbegin(); it != windows_.cend(); ++it) {
|
||||||
|
ImGuiProperties *impl = (*it)->impl_;
|
||||||
|
if ( impl && impl->ptr)
|
||||||
|
{
|
||||||
|
ImVec2 distance_to_corner = ImVec2(prev_width, prev_height) - impl->ptr->Pos - impl->ptr->SizeFull;
|
||||||
|
|
||||||
|
impl->resized_pos = impl->ptr->Pos;
|
||||||
|
|
||||||
|
if ( ABS(distance_to_corner.x) < 100.f ) {
|
||||||
|
impl->resized_pos.x += curr_width - prev_width;
|
||||||
|
impl->resizing_workspace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ABS(distance_to_corner.y) < 100.f ) {
|
||||||
|
impl->resized_pos.y += curr_height -prev_height;
|
||||||
|
impl->resizing_workspace = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceWindow::Update()
|
||||||
|
{
|
||||||
|
// get ImGui pointer to window (available only after first run)
|
||||||
|
if (!impl_) {
|
||||||
|
ImGuiWindow *w = ImGui::FindWindowByName(name_);
|
||||||
|
if (w != NULL) {
|
||||||
|
impl_ = new ImGuiProperties;
|
||||||
|
impl_->ptr = w;
|
||||||
|
impl_->user_pos = w->Pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( Visible() ) {
|
||||||
|
// perform animation for clear/restore workspace
|
||||||
|
if (impl_->animation) {
|
||||||
|
// increment animation progress by small steps
|
||||||
|
impl_->progress += SIGN(impl_->target -impl_->progress) * 0.1f;
|
||||||
|
// finished animation : apply full target position
|
||||||
|
if (ABS_DIFF(impl_->target, impl_->progress) < 0.05f) {
|
||||||
|
impl_->animation = false;
|
||||||
|
ImVec2 pos = impl_->user_pos * (1.f -impl_->target) + impl_->outside_pos * impl_->target;
|
||||||
|
ImGui::SetWindowPos(impl_->ptr, pos);
|
||||||
|
}
|
||||||
|
// move window by interpolation between user position and outside target position
|
||||||
|
else {
|
||||||
|
ImVec2 pos = impl_->user_pos * (1.f -impl_->progress) + impl_->outside_pos * impl_->progress;
|
||||||
|
ImGui::SetWindowPos(impl_->ptr, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Restore if clic on overlay
|
||||||
|
if (clear_workspace_enabled)
|
||||||
|
{
|
||||||
|
// draw another window on top of the WorkspaceWindow, at exact same position and size
|
||||||
|
ImGuiWindow* window = impl_->ptr;
|
||||||
|
ImGui::SetNextWindowPos(window->Pos, ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowSize(window->Size, ImGuiCond_Always);
|
||||||
|
char nameoverlay[64];
|
||||||
|
sprintf(nameoverlay, "%sOverlay", name_);
|
||||||
|
if (ImGui::Begin(nameoverlay, NULL, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings ))
|
||||||
|
{
|
||||||
|
// exit workspace clear mode if user clics on the window
|
||||||
|
ImGui::InvisibleButton("##dummy", window->Size);
|
||||||
|
if (ImGui::IsItemClicked())
|
||||||
|
WorkspaceWindow::restoreWorkspace();
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// move windows because workspace was resized
|
||||||
|
if (impl_->resizing_workspace) {
|
||||||
|
// how far from the target ?
|
||||||
|
ImVec2 delta = impl_->resized_pos - impl_->ptr->Pos;
|
||||||
|
// got close enough to stop workspace resize
|
||||||
|
if (ABS(delta.x) < 2.f && ABS(delta.y) < 2.f)
|
||||||
|
impl_->resizing_workspace = false;
|
||||||
|
// dichotomic reach of target position
|
||||||
|
ImVec2 pos = impl_->ptr->Pos + delta * 0.5f;
|
||||||
|
ImGui::SetWindowPos(impl_->ptr, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/WorkspaceWindow.h
Normal file
32
src/WorkspaceWindow.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef WORKSPACEWINDOW_H
|
||||||
|
#define WORKSPACEWINDOW_H
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
class WorkspaceWindow
|
||||||
|
{
|
||||||
|
static std::list<WorkspaceWindow *> windows_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WorkspaceWindow(const char* name);
|
||||||
|
|
||||||
|
// global access to Workspace control
|
||||||
|
static bool clear() { return clear_workspace_enabled; }
|
||||||
|
static void toggleClearRestoreWorkspace();
|
||||||
|
static void clearWorkspace();
|
||||||
|
static void restoreWorkspace(bool instantaneous = false);
|
||||||
|
static void notifyWorkspaceSizeChanged(int prev_width, int prev_height, int curr_width, int curr_height);
|
||||||
|
|
||||||
|
// for inherited classes
|
||||||
|
virtual void Update();
|
||||||
|
virtual bool Visible() const { return true; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
static bool clear_workspace_enabled;
|
||||||
|
|
||||||
|
const char *name_;
|
||||||
|
struct ImGuiProperties *impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WORKSPACEWINDOW_H
|
||||||
@@ -122,4 +122,100 @@
|
|||||||
#define OSC_PORT_SEND_DEFAULT 7001
|
#define OSC_PORT_SEND_DEFAULT 7001
|
||||||
#define OSC_CONFIG_FILE "osc.xml"
|
#define OSC_CONFIG_FILE "osc.xml"
|
||||||
|
|
||||||
|
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_PLAY_CIRCLE " Player"
|
||||||
|
#define IMGUI_TITLE_TIMER ICON_FA_CLOCK " Timer"
|
||||||
|
#define IMGUI_TITLE_INPUT_MAPPING ICON_FA_HAND_PAPER " Input Mapping"
|
||||||
|
#define IMGUI_TITLE_HELP ICON_FA_LIFE_RING " Help"
|
||||||
|
#define IMGUI_TITLE_TOOLBOX ICON_FA_HAMSA " Guru Toolbox"
|
||||||
|
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Shader Editor"
|
||||||
|
#define IMGUI_TITLE_PREVIEW ICON_FA_LAPTOP " Display"
|
||||||
|
|
||||||
|
#ifdef APPLE
|
||||||
|
#define CTRL_MOD "Cmd+"
|
||||||
|
#define ALT_MOD "Option+"
|
||||||
|
#else
|
||||||
|
#define CTRL_MOD "Ctrl+"
|
||||||
|
#define ALT_MOD "Alt+"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MENU_NEW_FILE ICON_FA_FILE " New"
|
||||||
|
#define SHORTCUT_NEW_FILE CTRL_MOD "W"
|
||||||
|
#define MENU_OPEN_FILE ICON_FA_FILE_UPLOAD " Open"
|
||||||
|
#define SHORTCUT_OPEN_FILE CTRL_MOD "O"
|
||||||
|
#define MENU_REOPEN_FILE ICON_FA_FILE_UPLOAD " Re-open"
|
||||||
|
#define SHORTCUT_REOPEN_FILE CTRL_MOD "Shift+O"
|
||||||
|
#define MENU_SAVE_FILE ICON_FA_FILE_DOWNLOAD " Save"
|
||||||
|
#define SHORTCUT_SAVE_FILE CTRL_MOD "S"
|
||||||
|
#define MENU_SAVEAS_FILE ICON_FA_FILE_DOWNLOAD " Save as"
|
||||||
|
#define MENU_SAVE_ON_EXIT ICON_FA_LEVEL_DOWN_ALT " Save on exit"
|
||||||
|
#define MENU_OPEN_ON_START ICON_FA_LEVEL_UP_ALT " Restore on start"
|
||||||
|
#define SHORTCUT_SAVEAS_FILE CTRL_MOD "Shift+S"
|
||||||
|
#define MENU_QUIT ICON_FA_POWER_OFF " Quit"
|
||||||
|
#define SHORTCUT_QUIT CTRL_MOD "Q"
|
||||||
|
#define MENU_CUT ICON_FA_CUT " Cut"
|
||||||
|
#define SHORTCUT_CUT CTRL_MOD "X"
|
||||||
|
#define MENU_COPY ICON_FA_COPY " Copy"
|
||||||
|
#define SHORTCUT_COPY CTRL_MOD "C"
|
||||||
|
#define MENU_DELETE ICON_FA_ERASER " Delete"
|
||||||
|
#define SHORTCUT_DELETE "Del"
|
||||||
|
#define ACTION_DELETE ICON_FA_BACKSPACE " Delete"
|
||||||
|
#define MENU_PASTE ICON_FA_PASTE " Paste"
|
||||||
|
#define SHORTCUT_PASTE CTRL_MOD "V"
|
||||||
|
#define MENU_SELECTALL ICON_FA_TH_LIST " Select all"
|
||||||
|
#define SHORTCUT_SELECTALL CTRL_MOD "A"
|
||||||
|
#define MENU_UNDO ICON_FA_UNDO " Undo"
|
||||||
|
#define SHORTCUT_UNDO CTRL_MOD "Z"
|
||||||
|
#define MENU_REDO ICON_FA_REDO " Redo"
|
||||||
|
#define SHORTCUT_REDO CTRL_MOD "Shift+Z"
|
||||||
|
#define MENU_RECORD ICON_FA_CIRCLE " Record"
|
||||||
|
#define SHORTCUT_RECORD CTRL_MOD "R"
|
||||||
|
#define MENU_RECORDCONT ICON_FA_STOP_CIRCLE " Save & continue"
|
||||||
|
#define SHORTCUT_RECORDCONT CTRL_MOD "Alt+R"
|
||||||
|
#define MENU_CAPTUREFRAME ICON_FA_CAMERA_RETRO " Capture frame"
|
||||||
|
#define SHORTCUT_CAPTURE_DISPLAY "F11"
|
||||||
|
#define SHORTCUT_CAPTURE_PLAYER "F10"
|
||||||
|
#define MENU_CAPTUREGUI ICON_FA_CAMERA " Screenshot vimix"
|
||||||
|
#define SHORTCUT_CAPTURE_GUI "F9"
|
||||||
|
#define MENU_OUTPUTDISABLE ICON_FA_EYE_SLASH " Disable"
|
||||||
|
#define SHORTCUT_OUTPUTDISABLE "F12"
|
||||||
|
#define MENU_LARGEPREVIEW ICON_FA_EXPAND_ARROWS_ALT " Large preview"
|
||||||
|
#define SHORTCUT_LARGEPREVIEW "F6"
|
||||||
|
#define MENU_CLOSE ICON_FA_TIMES " Close"
|
||||||
|
#define DIALOG_FAILED_SOURCE ICON_FA_EXCLAMATION_TRIANGLE " Source failure"
|
||||||
|
|
||||||
|
#define MENU_NOTE ICON_FA_STICKY_NOTE " Add sticky note"
|
||||||
|
#define MENU_METRICS ICON_FA_TACHOMETER_ALT " Metrics"
|
||||||
|
#define MENU_SOURCE_TOOL ICON_FA_WRENCH " Source toolbar"
|
||||||
|
#define MENU_HELP ICON_FA_LIFE_RING " Help"
|
||||||
|
#define SHORTCUT_HELP CTRL_MOD "H"
|
||||||
|
#define MENU_LOGS ICON_FA_LIST_UL " Logs"
|
||||||
|
#define SHORTCUT_LOGS CTRL_MOD "L"
|
||||||
|
#define TOOLTIP_PLAYER "Player "
|
||||||
|
#define SHORTCUT_PLAYER CTRL_MOD "P"
|
||||||
|
#define TOOLTIP_OUTPUT "Display "
|
||||||
|
#define SHORTCUT_OUTPUT CTRL_MOD "D"
|
||||||
|
#define TOOLTIP_TIMER "Timer "
|
||||||
|
#define SHORTCUT_TIMER CTRL_MOD "T"
|
||||||
|
#define TOOLTIP_INPUTS "Inputs mapping "
|
||||||
|
#define SHORTCUT_INPUTS CTRL_MOD "I"
|
||||||
|
#define TOOLTIP_SHADEREDITOR "Shader Editor "
|
||||||
|
#define SHORTCUT_SHADEREDITOR CTRL_MOD "E"
|
||||||
|
#define TOOLTIP_FULLSCREEN "Fullscreen "
|
||||||
|
#define SHORTCUT_FULLSCREEN CTRL_MOD "F"
|
||||||
|
#define TOOLTIP_MAIN "Main menu "
|
||||||
|
#define SHORTCUT_MAIN "HOME"
|
||||||
|
#define TOOLTIP_NEW_SOURCE "New source "
|
||||||
|
#define SHORTCUT_NEW_SOURCE "INS"
|
||||||
|
#define TOOLTIP_HIDE "Hide windows "
|
||||||
|
#define TOOLTIP_SHOW "Show windows "
|
||||||
|
#define SHORTCUT_HIDE "ESC"
|
||||||
|
#define TOOLTIP_PANEL_VISIBLE "Panel always visible "
|
||||||
|
#define TOOLTIP_PANEL_AUTO "Panel auto hide "
|
||||||
|
#define SHORTCUT_PANEL_MODE "HOME"
|
||||||
|
|
||||||
|
#define LABEL_AUTO_MEDIA_PLAYER ICON_FA_USER_CIRCLE " User selection"
|
||||||
|
#define LABEL_STORE_SELECTION " Create batch"
|
||||||
|
#define LABEL_EDIT_FADING ICON_FA_RANDOM " Fade in & out"
|
||||||
|
#define LABEL_VIDEO_SEQUENCE " Encode an image sequence"
|
||||||
|
|
||||||
#endif // VMIX_DEFINES_H
|
#endif // VMIX_DEFINES_H
|
||||||
|
|||||||
Reference in New Issue
Block a user