Files
vimix/src/UserInterfaceManager.cpp
Bruno Herbelin 2f8411a658 Improved vimix first launch (or after upgrade)
Changed Mixer Load behavior at init, detect change of version and do not load settings if different, show About Vimix after change of version, fixed initial position of windows at first run.
2024-01-28 12:26:05 +01:00

6339 lines
275 KiB
C++

/*
* 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/>.
**/
#define PLOT_ARRAY_SIZE 180
#define WINDOW_TOOLBOX_ALPHA 0.35f
#define WINDOW_TOOLBOX_DIST_TO_BORDER 10.f
#include <iostream>
#include <sstream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <regex>
// ImGui
#include "imgui.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
// Desktop OpenGL function loader
#include <glad/glad.h>
// Include glfw3.h after our OpenGL definitions
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
// generic image loader
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#include "defines.h"
#include "Settings.h"
#include "Log.h"
#include "SystemToolkit.h"
#include "DialogToolkit.h"
#include "BaseToolkit.h"
#include "NetworkToolkit.h"
#include "GlmToolkit.h"
#include "GstToolkit.h"
#include "ImGuiToolkit.h"
#include "ImGuiVisitor.h"
#include "ControlManager.h"
#include "ActionManager.h"
#include "Resource.h"
#include "Connection.h"
#include "SessionCreator.h"
#include "Mixer.h"
#include "Recorder.h"
#include "SourceCallback.h"
#include "MediaSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "ScreenCaptureSource.h"
#include "MultiFileSource.h"
#include "ShmdataBroadcast.h"
#include "VideoBroadcast.h"
#include "MultiFileRecorder.h"
#include "MousePointer.h"
#include "Playlist.h"
#include "Audio.h"
#include "UserInterfaceManager.h"
// utility functions
void ShowAboutGStreamer(bool* p_open);
void ShowAboutOpengl(bool* p_open);
void ShowSandbox(bool* p_open);
void SetMouseCursor(ImVec2 mousepos, View::Cursor c = View::Cursor());
std::string readable_date_time_string(std::string date){
if (date.length()<12)
return "";
std::string s = date.substr(6, 2) + "/" + date.substr(4, 2) + "/" + date.substr(0, 4);
s += " @ " + date.substr(8, 2) + ":" + date.substr(10, 2);
return s;
}
class Thumbnail
{
float aspect_ratio_;
uint texture_;
public:
Thumbnail();
~Thumbnail();
void reset();
void fill (const FrameBufferImage *image);
bool filled();
void Render(float width);
};
UserInterface::UserInterface()
{
start_time = gst_util_get_timestamp ();
ctrl_modifier_active = false;
alt_modifier_active = false;
shift_modifier_active = false;
show_vimix_about = false;
show_imgui_about = false;
show_gst_about = false;
show_opengl_about = false;
show_view_navigator = 0;
target_view_navigator = 1;
screenshot_step = 0;
pending_save_on_exit = false;
show_preview = UserInterface::PREVIEW_NONE;
sessionopendialog = nullptr;
sessionimportdialog = nullptr;
sessionsavedialog = nullptr;
}
bool UserInterface::Init()
{
if (Rendering::manager().mainWindow().window()== nullptr)
return false;
pending_save_on_exit = false;
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = Settings::application.scale;
// Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(Rendering::manager().mainWindow().window(), true);
ImGui_ImplOpenGL3_Init(VIMIX_GLSL_VERSION);
// hack to change keys according to keyboard layout
io.KeyMap[ImGuiKey_A] = Control::layoutKey(GLFW_KEY_A);
io.KeyMap[ImGuiKey_C] = Control::layoutKey(GLFW_KEY_C);
io.KeyMap[ImGuiKey_V] = Control::layoutKey(GLFW_KEY_V);
io.KeyMap[ImGuiKey_X] = Control::layoutKey(GLFW_KEY_X);
io.KeyMap[ImGuiKey_Y] = Control::layoutKey(GLFW_KEY_Y);
io.KeyMap[ImGuiKey_Z] = Control::layoutKey(GLFW_KEY_Z);
// Setup Dear ImGui style
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.accent_color));
// Estalish the base size from the resolution of the monitor
float base_font_size = float(Rendering::manager().mainWindow().pixelsforRealHeight(4.f)) ;
// at least 8 pixels font size
base_font_size = MAX(base_font_size, 8.f);
// Load Fonts (using resource manager, NB: a temporary copy of the raw data is necessary)
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_DEFAULT, "Roboto-Regular", int(base_font_size) );
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_BOLD, "Roboto-Bold", int(base_font_size) + 1 );
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_ITALIC, "Roboto-Italic", int(base_font_size) + 1 );
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_MONO, "Hack-Regular", int(base_font_size) - 2);
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_LARGE, "Hack-Regular", MIN(int(base_font_size * 1.5f), 50) );
// info
// Log::Info("Monitor (%.1f,%.1f)", Rendering::manager().monitorWidth(), Rendering::manager().monitorHeight());
Log::Info("Font size %d", int(base_font_size) );
// Style
ImGuiStyle& style = ImGui::GetStyle();
style.WindowPadding.x = base_font_size / 2.5f;
style.WindowPadding.y = style.WindowPadding.x / 2.f;
style.FramePadding.x = base_font_size / 2.5f;
style.FramePadding.y = style.FramePadding.x / 2.f;
style.IndentSpacing = base_font_size;
style.ItemSpacing.x = base_font_size / 2.f;
style.ItemSpacing.y = style.ItemSpacing.x / 3.f;
style.ItemInnerSpacing.x = base_font_size / 2.5f;
style.ItemInnerSpacing.y = style.ItemInnerSpacing.x / 2.f;
style.WindowRounding = base_font_size / 2.5f;
style.ChildRounding = style.WindowRounding / 2.f;
style.FrameRounding = style.WindowRounding / 2.f;
style.PopupRounding = style.WindowRounding / 2.f;
style.GrabRounding = style.FrameRounding / 2.f;
style.GrabMinSize = base_font_size / 1.5f;
style.Alpha = 0.92f;
// prevent bug with imgui clipboard (null at start)
ImGui::SetClipboardText("");
// setup settings filename
std::string inifile = SystemToolkit::full_filename(SystemToolkit::settings_path(), "imgui.ini");
std::snprintf(inifilepath, 2048, "%s", inifile.c_str() );
io.IniFilename = inifilepath;
// load favorites
favorites.load( SystemToolkit::full_filename(SystemToolkit::settings_path(), "favorites.lix") );
playlists_path = SystemToolkit::full_filename(SystemToolkit::settings_path(), "playlists");
if ( !SystemToolkit::file_exists(playlists_path)) {
if ( !SystemToolkit::create_directory(playlists_path) )
playlists_path = SystemToolkit::home_path();
}
// init dialogs
sessionopendialog = new DialogToolkit::OpenFileDialog("Open Session",
VIMIX_FILE_TYPE, VIMIX_FILE_PATTERN);
sessionsavedialog = new DialogToolkit::SaveFileDialog("Save Session",
VIMIX_FILE_TYPE, VIMIX_FILE_PATTERN);
sessionimportdialog = new DialogToolkit::OpenFileDialog("Import Sources",
VIMIX_FILE_TYPE, VIMIX_FILE_PATTERN);
// init tooltips
ImGuiToolkit::setToolTipsEnabled(Settings::application.show_tooptips);
// show about dialog on first run
show_vimix_about = (Settings::application.total_runtime < 1);
return true;
}
uint64_t UserInterface::Runtime() const
{
return gst_util_get_timestamp () - start_time;
}
void UserInterface::setView(View::Mode mode)
{
Mixer::manager().setView(mode);
navigator.discardPannel();
}
void UserInterface::handleKeyboard()
{
static bool esc_repeat_ = false;
const ImGuiIO& io = ImGui::GetIO();
alt_modifier_active = io.KeyAlt;
shift_modifier_active = io.KeyShift;
ctrl_modifier_active = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
keyboard_available = !io.WantCaptureKeyboard;
// do not capture keys if keyboard is used (e.g. for entering text field)
if (io.WantCaptureKeyboard || io.WantTextInput)
return;
// Application "CTRL +"" Shortcuts
if ( ctrl_modifier_active ) {
if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_Q), false )) {
// try quit
if ( TryClose() )
Rendering::manager().close();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_O), false )) {
// SHIFT + CTRL + O : reopen current session
if (shift_modifier_active && !Mixer::manager().session()->filename().empty())
Mixer::manager().load( Mixer::manager().session()->filename() );
// CTRL + O : Open session
else
selectOpenFilename();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_S), false )) {
// SHIFT + CTRL + S : save as
if (shift_modifier_active)
selectSaveFilename();
// CTRL + S : save (save as if necessary)
else
saveOrSaveAs();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_W), false )) {
// New Session
Mixer::manager().close();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_B), false )) {
// restart media player
sourcecontrol.Replay();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_L), false )) {
// Logs
Settings::application.widget.logs = !Settings::application.widget.logs;
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_T), false )) {
// Timers
timercontrol.setVisible(!Settings::application.widget.timer);
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_G), false )) {
// Developer toolbox
Settings::application.widget.toolbox = !Settings::application.widget.toolbox;
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_H), false )) {
// Helper
Settings::application.widget.help = true;
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_E), false )) {
// Shader Editor
shadercontrol.setVisible(!Settings::application.widget.shader_editor);
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_D), false )) {
// Display output
outputcontrol.setVisible(!Settings::application.widget.preview);
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_P), false )) {
// Media player
sourcecontrol.setVisible(!Settings::application.widget.media_player);
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_A), false )) {
if (shift_modifier_active)
{
// clear selection
Mixer::manager().unsetCurrentSource();
Mixer::selection().clear();
}
else
// select all
Mixer::manager().view()->selectAll();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_R), false )) {
// toggle recording stop / start (or save and continue if + SHIFT modifier)
outputcontrol.ToggleRecord(shift_modifier_active);
}
else if (ImGui::IsKeyPressed( GLFW_KEY_SPACE, false )) {
// toggle pause recorder
outputcontrol.ToggleRecordPause();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_Z), false )) {
if (shift_modifier_active)
Action::manager().redo();
else
Action::manager().undo();
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_C), false )) {
std::string clipboard = Mixer::selection().clipboard();
if (!clipboard.empty())
ImGui::SetClipboardText(clipboard.c_str());
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_X), false )) {
std::string clipboard = Mixer::selection().clipboard();
if (!clipboard.empty()) {
ImGui::SetClipboardText(clipboard.c_str());
Mixer::manager().deleteSelection();
}
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_V), false )) {
auto clipboard = ImGui::GetClipboardText();
if (clipboard != nullptr && strlen(clipboard) > 0)
Mixer::manager().paste(clipboard);
}
else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_I), false )) {
Settings::application.widget.inputs = !Settings::application.widget.inputs;
}
else if (ImGui::IsKeyPressed( GLFW_KEY_0 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 0 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_1 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 1 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_2 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 2 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_3 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 3 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_4 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 4 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_5 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 5 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_6 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 6 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_7 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 7 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_8 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 8 ) );
else if (ImGui::IsKeyPressed( GLFW_KEY_9 ))
Mixer::selection().toggle( Mixer::manager().sourceAtIndex( 9 ) );
}
// No CTRL modifier
else {
// Application F-Keys
if (ImGui::IsKeyPressed( GLFW_KEY_F1, false ))
setView(View::MIXING);
else if (ImGui::IsKeyPressed( GLFW_KEY_F2, false ))
setView(View::GEOMETRY);
else if (ImGui::IsKeyPressed( GLFW_KEY_F3, false ))
setView(View::LAYER);
else if (ImGui::IsKeyPressed( GLFW_KEY_F4, false ))
setView(View::TEXTURE);
else if (ImGui::IsKeyPressed( GLFW_KEY_F5, false ))
setView(View::DISPLAYS);
else if (ImGui::IsKeyPressed( GLFW_KEY_F6, false ))
show_preview = PREVIEW_OUTPUT;
else if (ImGui::IsKeyPressed( GLFW_KEY_F7, false ))
show_preview = PREVIEW_SOURCE;
else if (ImGui::IsKeyPressed( GLFW_KEY_F9, false ))
StartScreenshot();
else if (ImGui::IsKeyPressed( GLFW_KEY_F10, false ))
sourcecontrol.Capture();
else if (ImGui::IsKeyPressed( GLFW_KEY_F11, false ))
FrameGrabbing::manager().add(new PNGRecorder(SystemToolkit::base_filename( Mixer::manager().session()->filename())));
else if (ImGui::IsKeyPressed( GLFW_KEY_F12, false )) {
Settings::application.render.disabled = !Settings::application.render.disabled;
}
// button home to toggle panel
else if (ImGui::IsKeyPressed( GLFW_KEY_HOME, false ))
navigator.togglePannelAutoHide();
// button home to toggle menu
else if (ImGui::IsKeyPressed( GLFW_KEY_INSERT, false ))
navigator.togglePannelNew();
// button esc : react to press and to release
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE, false )) {
// hide pannel
navigator.discardPannel();
// toggle clear workspace
WorkspaceWindow::toggleClearRestoreWorkspace();
// ESC key is not yet maintained pressed
esc_repeat_ = false;
}
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE, true )) {
// ESC key is maintained pressed
esc_repeat_ = true;
}
else if ( esc_repeat_ && WorkspaceWindow::clear() && ImGui::IsKeyReleased( GLFW_KEY_ESCAPE )) {
// restore cleared workspace when releasing ESC after maintain
WorkspaceWindow::restoreWorkspace();
esc_repeat_ = false;
}
// Space bar
else if (ImGui::IsKeyPressed( GLFW_KEY_SPACE, false ))
// Space bar to toggle play / pause
sourcecontrol.Play();
// Backspace to delete source
else if (ImGui::IsKeyPressed( GLFW_KEY_BACKSPACE ) || ImGui::IsKeyPressed( GLFW_KEY_DELETE ))
Mixer::manager().deleteSelection();
else if (ImGui::IsKeyPressed( GLFW_KEY_0 ))
setSourceInPanel( 0 );
else if (ImGui::IsKeyPressed( GLFW_KEY_1 ))
setSourceInPanel( 1 );
else if (ImGui::IsKeyPressed( GLFW_KEY_2 ))
setSourceInPanel( 2 );
else if (ImGui::IsKeyPressed( GLFW_KEY_3 ))
setSourceInPanel( 3 );
else if (ImGui::IsKeyPressed( GLFW_KEY_4 ))
setSourceInPanel( 4 );
else if (ImGui::IsKeyPressed( GLFW_KEY_5 ))
setSourceInPanel( 5 );
else if (ImGui::IsKeyPressed( GLFW_KEY_6 ))
setSourceInPanel( 6 );
else if (ImGui::IsKeyPressed( GLFW_KEY_7 ))
setSourceInPanel( 7 );
else if (ImGui::IsKeyPressed( GLFW_KEY_8 ))
setSourceInPanel( 8 );
else if (ImGui::IsKeyPressed( GLFW_KEY_9 ))
setSourceInPanel( 9 );
// button tab to select next
else if ( !alt_modifier_active && ImGui::IsKeyPressed( GLFW_KEY_TAB )) {
// cancel selection
if (Mixer::selection().size() > 1)
Mixer::selection().clear();
if (shift_modifier_active)
Mixer::manager().setCurrentPrevious();
else
Mixer::manager().setCurrentNext();
if (navigator.pannelVisible())
navigator.showPannelSource( Mixer::manager().indexCurrentSource() );
}
// arrow keys to act on current view
else if ( ImGui::IsKeyDown( GLFW_KEY_LEFT ) ||
ImGui::IsKeyDown( GLFW_KEY_RIGHT ) ||
ImGui::IsKeyDown( GLFW_KEY_UP ) ||
ImGui::IsKeyDown( GLFW_KEY_DOWN ) ){
glm::vec2 delta(0.f, 0.f);
delta.x += (int) ImGui::IsKeyDown( GLFW_KEY_RIGHT ) - (int) ImGui::IsKeyDown( GLFW_KEY_LEFT );
delta.y += (int) ImGui::IsKeyDown( GLFW_KEY_DOWN ) - (int) ImGui::IsKeyDown( GLFW_KEY_UP );
Mixer::manager().view()->arrow( delta );
}
else if ( ImGui::IsKeyReleased( GLFW_KEY_LEFT ) ||
ImGui::IsKeyReleased( GLFW_KEY_RIGHT ) ||
ImGui::IsKeyReleased( GLFW_KEY_UP ) ||
ImGui::IsKeyReleased( GLFW_KEY_DOWN ) ){
Mixer::manager().view()->terminate(true);
MousePointer::manager().active()->terminate();
}
}
// special case: CTRL + TAB is ALT + TAB in OSX
if (io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl) {
if (ImGui::IsKeyPressed( GLFW_KEY_TAB, false ))
show_view_navigator += shift_modifier_active ? 5 : 1;
}
else if (show_view_navigator > 0) {
show_view_navigator = 0;
Mixer::manager().setView((View::Mode) target_view_navigator);
}
}
void UserInterface::handleMouse()
{
ImGuiIO& io = ImGui::GetIO();
// get mouse coordinates and prevent invalid values
static glm::vec2 _prev_mousepos = glm::vec2(0.f);
glm::vec2 mousepos = _prev_mousepos;
if (io.MousePos.x > -1 && io.MousePos.y > -1) {
mousepos = glm::vec2 (io.MousePos.x * io.DisplayFramebufferScale.x, io.MousePos.y * io.DisplayFramebufferScale.y);
mousepos = glm::clamp(mousepos, glm::vec2(0.f), glm::vec2(io.DisplaySize.x * io.DisplayFramebufferScale.x, io.DisplaySize.y * io.DisplayFramebufferScale.y));
_prev_mousepos = mousepos;
}
static glm::vec2 mouseclic[2];
mouseclic[ImGuiMouseButton_Left] = glm::vec2(io.MouseClickedPos[ImGuiMouseButton_Left].x * io.DisplayFramebufferScale.y, io.MouseClickedPos[ImGuiMouseButton_Left].y* io.DisplayFramebufferScale.x);
mouseclic[ImGuiMouseButton_Right] = glm::vec2(io.MouseClickedPos[ImGuiMouseButton_Right].x * io.DisplayFramebufferScale.y, io.MouseClickedPos[ImGuiMouseButton_Right].y* io.DisplayFramebufferScale.x);
static bool mousedown = false;
static View *view_drag = nullptr;
static std::pair<Node *, glm::vec2> picked = {nullptr, glm::vec2(0.f)};
// allow toggle ALT to enable / disable mouse pointer
static bool _was_alt = false;
if (_was_alt != alt_modifier_active) {
// remember to toggle
_was_alt = alt_modifier_active;
// simulate mouse release (mouse down will be re-activated)
mousedown = false;
// cancel active mouse pointer
MousePointer::manager().active()->terminate();
MousePointer::manager().setActiveMode( Pointer::POINTER_DEFAULT );
}
// steal focus on right button clic
if (!io.WantCaptureMouse)
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) /*|| ImGui::IsMouseClicked(ImGuiMouseButton_Middle)*/)
ImGui::FocusWindow(NULL);
//
// Mouse over
//
{
View::Cursor c = Mixer::manager().view()->over(mousepos);
if (c.type > 0)
SetMouseCursor(io.MousePos, c);
}
// if not on any window
if ( !ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !ImGui::IsWindowFocused(ImGuiHoveredFlags_AnyWindow) )
{
//
// RIGHT Mouse button
//
if ( ImGui::IsMouseDragging(ImGuiMouseButton_Right, 10.0f) )
{
// right mouse drag => drag current view
View::Cursor c = Mixer::manager().view()->drag( mouseclic[ImGuiMouseButton_Right], mousepos);
SetMouseCursor(io.MousePos, c);
}
else if ( ImGui::IsMouseDown(ImGuiMouseButton_Right))
{
Mixer::manager().unsetCurrentSource();
navigator.discardPannel();
// glm::vec3 point = Rendering::manager().unProject(mousepos, Mixer::manager().currentView()->scene.root()->transform_ );
}
if ( ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right) )
{
Mixer::manager().view()->recenter();
}
//
// LEFT Mouse button
//
if ( ImGui::IsMouseDown(ImGuiMouseButton_Left) ) {
if ( !mousedown )
{
mousedown = true;
// initiate Mouse pointer from position at mouse down event
if (alt_modifier_active || Settings::application.mouse_pointer_lock) {
MousePointer::manager().setActiveMode( (Pointer::Mode) Settings::application.mouse_pointer );
MousePointer::manager().active()->setStrength( Settings::application.mouse_pointer_strength[Settings::application.mouse_pointer] );
}
else
MousePointer::manager().setActiveMode( Pointer::POINTER_DEFAULT );
// ask the view what was picked
picked = Mixer::manager().view()->pick(mousepos);
bool clear_selection = false;
// if nothing picked,
if ( picked.first == nullptr ) {
clear_selection = true;
}
// something was picked
else {
// initiate the pointer effect
MousePointer::manager().active()->initiate(mousepos);
// get if a source was picked
Source *s = Mixer::manager().findSource(picked.first);
if (s != nullptr)
{
// CTRL + clic = multiple selection
if (ctrl_modifier_active) {
if ( !Mixer::selection().contains(s) )
Mixer::selection().add( s );
else {
Mixer::selection().remove( s );
if ( Mixer::selection().size() > 1 )
s = Mixer::selection().front();
else {
s = nullptr;
}
}
}
// making the picked source the current one
if (s)
Mixer::manager().setCurrentSource( s );
else
Mixer::manager().unsetCurrentSource();
if (navigator.pannelVisible())
navigator.showPannelSource( Mixer::manager().indexCurrentSource() );
// indicate to view that an action can be initiated (e.g. grab)
Mixer::manager().view()->initiate();
}
// no source is selected
else {
// unset current
Mixer::manager().unsetCurrentSource();
navigator.discardPannel();
}
}
if (clear_selection) {
// unset current
Mixer::manager().unsetCurrentSource();
navigator.discardPannel();
// clear selection
Mixer::selection().clear();
}
}
}
if ( ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) )
{
// if double clic event was not used in view
if ( !Mixer::manager().view()->doubleclic(mousepos) ) {
int i = Mixer::manager().indexCurrentSource();
// if no current source
if (i<0){
// hide left pannel & toggle clear workspace
navigator.discardPannel();
WorkspaceWindow::toggleClearRestoreWorkspace();
}
else
// display current source in left panel /or/ hide left panel if no current source
navigator.showPannelSource( Mixer::manager().indexCurrentSource() );
}
}
// if ( mousedown && glm::distance(mouseclic[ImGuiMouseButton_Left], mousepos) > 3.f )
if ( ImGui::IsMouseDragging(ImGuiMouseButton_Left, 5.0f) )
{
if(view_drag == nullptr) {
view_drag = Mixer::manager().view();
// indicate to view that an action can be initiated (e.g. grab)
Mixer::manager().view()->initiate();
}
// only operate if the view didn't change
if (view_drag == Mixer::manager().view()) {
if ( picked.first != nullptr ) {
// Apply Mouse pointer filter
// + scrollwheel changes strength
if ( io.MouseWheel != 0) {
MousePointer::manager().active()->incrementStrength(0.1 * io.MouseWheel);
Settings::application.mouse_pointer_strength[Settings::application.mouse_pointer] = MousePointer::manager().active()->strength();
}
MousePointer::manager().active()->update(mousepos, 1.f / ( MAX(io.Framerate, 1.f) ));
// action on current source
View::Cursor c;
Source *current = Mixer::manager().currentSource();
if (current)
{
// grab current sources
c = Mixer::manager().view()->grab(current, mouseclic[ImGuiMouseButton_Left],
MousePointer::manager().active()->target(), picked);
}
// action on other (non-source) elements in the view
else
{
// grab picked object
c = Mixer::manager().view()->grab(nullptr, mouseclic[ImGuiMouseButton_Left],
MousePointer::manager().active()->target(), picked);
}
// Set cursor appearance
SetMouseCursor(io.MousePos, c);
// Draw Mouse pointer effect
MousePointer::manager().active()->draw();
}
// Selection area
else {
// highlight-colored selection rectangle
ImVec4 color = ImGuiToolkit::HighlightColor();
ImGui::GetBackgroundDrawList()->AddRect(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, ImGui::GetColorU32(color));
color.w = 0.12; // transparent
ImGui::GetBackgroundDrawList()->AddRectFilled(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, ImGui::GetColorU32(color));
// Bounding box multiple sources selection
Mixer::manager().view()->select(mouseclic[ImGuiMouseButton_Left], mousepos);
}
}
}
//
// Mouse wheel over background without source action
//
else if ( !mousedown && io.MouseWheel != 0) {
// scroll => zoom current view
Mixer::manager().view()->zoom( io.MouseWheel );
}
}
else {
// cancel all operations on view when interacting on GUI
if (mousedown || view_drag)
Mixer::manager().view()->terminate();
view_drag = nullptr;
mousedown = false;
}
if ( ImGui::IsMouseReleased(ImGuiMouseButton_Left) || ImGui::IsMouseReleased(ImGuiMouseButton_Right) )
{
// special case of one single source in area selection : make current after release
if (view_drag && picked.first == nullptr && Mixer::selection().size() == 1) {
Mixer::manager().setCurrentSource( Mixer::selection().front() );
navigator.discardPannel();
}
view_drag = nullptr;
mousedown = false;
picked = { nullptr, glm::vec2(0.f) };
Mixer::manager().view()->terminate();
MousePointer::manager().active()->terminate();
SetMouseCursor(io.MousePos);
}
}
bool UserInterface::saveOrSaveAs(bool force_versioning)
{
bool finished = false;
if (Mixer::manager().session()->filename().empty())
selectSaveFilename();
else {
Mixer::manager().save(force_versioning || Settings::application.save_version_snapshot);
finished = true;
}
return finished;
}
bool UserInterface::TryClose()
{
// cannot close if a file dialog is pending
if (DialogToolkit::FileDialog::busy() || DialogToolkit::ColorPickerDialog::busy())
return false;
// always stop all recordings
FrameGrabbing::manager().stopAll();
// force close if trying to close again although it is already pending for save
if (pending_save_on_exit)
return true;
// check if there is something to save
pending_save_on_exit = false;
if (!Mixer::manager().session()->empty())
{
// determine if a pending save of session is required
if (Mixer::manager().session()->filename().empty())
// need to wait for user to give a filename
pending_save_on_exit = true;
// save on exit
else if (Settings::application.recentSessions.save_on_exit)
// ok to save the session
Mixer::manager().save(false);
}
// say we can close if no pending save of session is needed
return !pending_save_on_exit;
}
void UserInterface::selectSaveFilename()
{
if (sessionsavedialog) {
if (!Mixer::manager().session()->filename().empty())
sessionsavedialog->setFolder( Mixer::manager().session()->filename() );
sessionsavedialog->open();
}
navigator.discardPannel();
}
void UserInterface::selectOpenFilename()
{
// launch file dialog to select a session filename to open
if (sessionopendialog)
sessionopendialog->open();
navigator.discardPannel();
}
void UserInterface::NewFrame()
{
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// deal with keyboard and mouse events
handleMouse();
handleScreenshot();
// handle FileDialogs
if (sessionopendialog && sessionopendialog->closed() && !sessionopendialog->path().empty())
Mixer::manager().open(sessionopendialog->path());
if (sessionimportdialog && sessionimportdialog->closed() && !sessionimportdialog->path().empty())
Mixer::manager().import(sessionimportdialog->path());
if (sessionsavedialog && sessionsavedialog->closed() && !sessionsavedialog->path().empty())
Mixer::manager().saveas(sessionsavedialog->path(), Settings::application.save_version_snapshot);
// overlay to ensure file dialog is modal
if (DialogToolkit::FileDialog::busy()){
if (!ImGui::IsPopupOpen("Busy"))
ImGui::OpenPopup("Busy");
if (ImGui::BeginPopupModal("Busy", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Close file dialog box to resume.");
ImGui::EndPopup();
}
}
// overlay to ensure file color dialog is closed after use
if (DialogToolkit::ColorPickerDialog::busy()){
if (!ImGui::IsPopupOpen("##Color"))
ImGui::OpenPopup("##Color");
if (ImGui::BeginPopup("##Color")) {
ImGui::Text("Validate color dialog to return to vimix.");
ImGui::EndPopup();
}
}
// popup to inform to save before close
if (pending_save_on_exit) {
if (!ImGui::IsPopupOpen(MENU_SAVE_ON_EXIT))
ImGui::OpenPopup(MENU_SAVE_ON_EXIT);
if (ImGui::BeginPopupModal(MENU_SAVE_ON_EXIT, NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Spacing();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC);
ImGui::Text(" Looks like you started some work ");
ImGui::Text(" but didn't save the session. ");
ImGui::PopFont();
ImGui::Spacing();
if (ImGui::Button(ICON_FA_TIMES " Cancel", ImVec2(ImGui::GetWindowContentRegionWidth(), 0))) {
pending_save_on_exit = false;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button(MENU_SAVEAS_FILE, ImVec2(ImGui::GetWindowContentRegionWidth(), 0))) {
pending_save_on_exit = false;
saveOrSaveAs();
ImGui::CloseCurrentPopup();
}
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_Tab));
if (ImGui::Button(MENU_QUIT, ImVec2(ImGui::GetWindowContentRegionWidth(), 0))
|| ImGui::IsKeyPressed(GLFW_KEY_ENTER) || ImGui::IsKeyPressed(GLFW_KEY_KP_ENTER) ) {
Rendering::manager().close();
ImGui::CloseCurrentPopup();
}
ImGui::PopStyleColor(1);
ImGui::Spacing();
ImGui::EndPopup();
}
}
}
void UserInterface::Render()
{
// navigator bar first
navigator.Render();
// update windows before render
outputcontrol.Update();
sourcecontrol.Update();
timercontrol.Update();
inputscontrol.Update();
shadercontrol.Update();
// warnings and notifications
Log::Render(&Settings::application.widget.logs);
if ( WorkspaceWindow::clear())
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.4);
// Output controller
if (outputcontrol.Visible())
outputcontrol.Render();
// Source controller
if (sourcecontrol.Visible())
sourcecontrol.Render();
// Timer controller
if (timercontrol.Visible())
timercontrol.Render();
// Keyboards controller
if (inputscontrol.Visible())
inputscontrol.Render();
// Shader controller
if (shadercontrol.Visible())
shadercontrol.Render();
// stats in the corner
if (Settings::application.widget.stats)
RenderMetrics(&Settings::application.widget.stats,
&Settings::application.widget.stats_corner,
&Settings::application.widget.stats_mode);
// source editor
if (Settings::application.widget.source_toolbar)
RenderSourceToolbar(&Settings::application.widget.source_toolbar,
&Settings::application.widget.source_toolbar_border,
&Settings::application.widget.source_toolbar_mode);
if ( WorkspaceWindow::clear())
ImGui::PopStyleVar();
// All other windows are simply not rendered if workspace is clear
else {
// windows
if (Settings::application.widget.logs)
Log::ShowLogWindow(&Settings::application.widget.logs);
if (Settings::application.widget.help)
RenderHelp();
if (Settings::application.widget.toolbox)
toolbox.Render();
// About
if (show_vimix_about)
RenderAbout(&show_vimix_about);
if (show_imgui_about)
ImGui::ShowAboutWindow(&show_imgui_about);
if (show_gst_about)
ShowAboutGStreamer(&show_gst_about);
if (show_opengl_about)
ShowAboutOpengl(&show_opengl_about);
}
// Notes
RenderNotes();
// Navigator
if (show_view_navigator > 0)
target_view_navigator = RenderViewNavigator( &show_view_navigator );
//
RenderPreview();
// handle keyboard input after all IMGUI widgets have potentially captured keyboard
handleKeyboard();
// all IMGUI Rendering
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void UserInterface::Terminate()
{
// save favorites
favorites.save();
// restore windows position for saving
WorkspaceWindow::restoreWorkspace(true);
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
}
void UserInterface::showMenuEdit()
{
bool has_selection = !Mixer::selection().empty();
const char *clipboard = ImGui::GetClipboardText();
bool has_clipboard = (clipboard != nullptr && strlen(clipboard) > 0 && SessionLoader::isClipboard(clipboard));
// UNDO
if ( ImGui::MenuItem( MENU_UNDO, SHORTCUT_UNDO, false, Action::manager().current() > 1) )
Action::manager().undo();
if ( ImGui::MenuItem( MENU_REDO, SHORTCUT_REDO, false, Action::manager().current() < Action::manager().max()) )
Action::manager().redo();
// EDIT
ImGui::Separator();
if (ImGui::MenuItem( MENU_CUT, SHORTCUT_CUT, false, has_selection)) {
std::string copied_text = Mixer::selection().clipboard();
if (!copied_text.empty()) {
ImGui::SetClipboardText(copied_text.c_str());
Mixer::manager().deleteSelection();
}
navigator.discardPannel();
}
if (ImGui::MenuItem( MENU_COPY, SHORTCUT_COPY, false, has_selection)) {
std::string copied_text = Mixer::selection().clipboard();
if (!copied_text.empty())
ImGui::SetClipboardText(copied_text.c_str());
navigator.discardPannel();
}
if (ImGui::MenuItem( MENU_PASTE, SHORTCUT_PASTE, false, has_clipboard)) {
if (clipboard)
Mixer::manager().paste(clipboard);
navigator.discardPannel();
}
if (ImGui::MenuItem( MENU_SELECTALL, SHORTCUT_SELECTALL, false, Mixer::manager().numSource() > 0)) {
Mixer::manager().view()->selectAll();
navigator.discardPannel();
}
// GROUP
ImGui::Separator();
if (ImGuiToolkit::MenuItemIcon(11, 2, " Bundle all active sources", NULL, false, Mixer::manager().numSource() > 0)) {
// create a new group session with only active sources
Mixer::manager().groupAll( true );
// switch pannel to show first source (created)
navigator.showPannelSource(0);
}
if (ImGuiToolkit::MenuItemIcon(7, 2, " Expand all bundles", NULL, false, Mixer::manager().numSource() > 0)) {
// create a new group session with all sources
Mixer::manager().ungroupAll();
}
}
void UserInterface::showMenuWindows()
{
if ( ImGui::MenuItem( MENU_OUTPUT, SHORTCUT_OUTPUT, &Settings::application.widget.preview) )
UserInterface::manager().outputcontrol.setVisible(Settings::application.widget.preview);
if ( ImGui::MenuItem( MENU_PLAYER, SHORTCUT_PLAYER, &Settings::application.widget.media_player) )
UserInterface::manager().sourcecontrol.setVisible(Settings::application.widget.media_player);
if ( ImGui::MenuItem( MENU_TIMER, SHORTCUT_TIMER, &Settings::application.widget.timer) )
UserInterface::manager().timercontrol.setVisible(Settings::application.widget.timer);
if ( ImGui::MenuItem( MENU_INPUTS, SHORTCUT_INPUTS, &Settings::application.widget.inputs) )
UserInterface::manager().inputscontrol.setVisible(Settings::application.widget.inputs);
// Show Help
ImGui::MenuItem( MENU_HELP, SHORTCUT_HELP, &Settings::application.widget.help );
// Show Logs
ImGui::MenuItem( MENU_LOGS, SHORTCUT_LOGS, &Settings::application.widget.logs );
ImGui::Separator();
// Enable / disable source toolbar
ImGui::MenuItem( MENU_SOURCE_TOOL, NULL, &Settings::application.widget.source_toolbar );
// Enable / disable metrics toolbar
ImGui::MenuItem( MENU_METRICS, NULL, &Settings::application.widget.stats );
}
void UserInterface::showMenuFile()
{
// NEW
if (ImGui::MenuItem( MENU_NEW_FILE, SHORTCUT_NEW_FILE)) {
Mixer::manager().close();
navigator.discardPannel();
}
ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x * 0.54f);
ImGui::Combo("Ratio", &Settings::application.render.ratio, RenderView::ratio_preset_name, IM_ARRAYSIZE(RenderView::ratio_preset_name) );
if (Settings::application.render.ratio < RenderView::AspectRatio_Custom) {
// Presets height
ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x * 0.54f);
ImGui::Combo("Height", &Settings::application.render.res, RenderView::height_preset_name, IM_ARRAYSIZE(RenderView::height_preset_name) );
}
else {
// Custom width and height
ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x * 0.54f);
ImGui::InputInt("Width", &Settings::application.render.custom_width, 100, 500);
ImGui::SetNextItemWidth( ImGui::GetContentRegionAvail().x * 0.54f);
ImGui::InputInt("Height", &Settings::application.render.custom_height, 100, 500);
}
// FILE OPEN AND SAVE
ImGui::Separator();
const std::string currentfilename = Mixer::manager().session()->filename();
const bool currentfileopen = !currentfilename.empty();
ImGui::MenuItem( MENU_OPEN_ON_START, nullptr, &Settings::application.recentSessions.load_at_start);
if (ImGui::MenuItem( MENU_OPEN_FILE, SHORTCUT_OPEN_FILE))
selectOpenFilename();
if (ImGui::MenuItem( MENU_REOPEN_FILE, SHORTCUT_REOPEN_FILE, false, currentfileopen))
Mixer::manager().load( currentfilename );
if (sessionimportdialog && ImGui::MenuItem( ICON_FA_FILE_EXPORT " Import" )) {
// launch file dialog to open a session file
sessionimportdialog->open();
// close pannel to select file
navigator.discardPannel();
}
if (ImGui::MenuItem( MENU_SAVE_FILE, SHORTCUT_SAVE_FILE, false, currentfileopen)) {
if (saveOrSaveAs())
navigator.discardPannel();
}
if (ImGui::MenuItem( MENU_SAVEAS_FILE, SHORTCUT_SAVEAS_FILE))
selectSaveFilename();
ImGui::MenuItem( MENU_SAVE_ON_EXIT, nullptr, &Settings::application.recentSessions.save_on_exit);
// HELP AND QUIT
ImGui::Separator();
if (ImGui::MenuItem( MENU_QUIT, SHORTCUT_QUIT) && TryClose())
Rendering::manager().close();
}
void UserInterface::StartScreenshot()
{
screenshot_step = 1;
}
void UserInterface::handleScreenshot()
{
// taking screenshot is in 3 steps
// 1) wait 1 frame that the menu / action showing button to take screenshot disapears
// 2) wait 1 frame that rendering manager takes the actual screenshot
// 3) if rendering manager current screenshot is ok, save it
if (screenshot_step > 0) {
switch(screenshot_step) {
case 1:
screenshot_step = 2;
break;
case 2:
Rendering::manager().requestScreenshot();
screenshot_step = 3;
break;
case 3:
{
if ( Rendering::manager().currentScreenshot()->isFull() ){
std::string filename = SystemToolkit::full_filename( SystemToolkit::home_path(), SystemToolkit::date_time_string() + "_vmixcapture.png" );
Rendering::manager().currentScreenshot()->save( filename );
Log::Notify("Screenshot saved %s", filename.c_str() );
}
screenshot_step = 4;
}
break;
default:
screenshot_step = 0;
break;
}
}
}
int UserInterface::RenderViewNavigator(int *shift)
{
// calculate potential target view index :
// - shift increment : minus 1 to not react to first trigger
// - current_view : indices are >= 1 (Mixing) and < 7 (INVALID)
// - Modulo 6 to allow multiple repetition of shift increment
// - skipping TRANSITION view 5 that is called only during transition
int target_index = ( (Settings::application.current_view -1) + (*shift -1) )%6 + 1;
// skip TRANSITION view
if (target_index == View::TRANSITION)
++target_index;
// prepare rendering of centered, fixed-size, semi-transparent window;
const ImGuiIO& io = ImGui::GetIO();
ImVec2 window_pos = ImVec2(io.DisplaySize.x / 2.f, io.DisplaySize.y / 2.f);
ImVec2 window_pos_pivot = ImVec2(0.5f, 0.5f);
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
ImGui::SetNextWindowSize(ImVec2(5.f * 120.f, 120.f + 2.f * ImGui::GetTextLineHeight()), ImGuiCond_Always);
ImGui::SetNextWindowBgAlpha(0.85f);
// show window
if (ImGui::Begin("Views", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
// prepare rendering of the array of selectable icons
bool selected_view[View::INVALID] = { };
selected_view[ target_index ] = true;
ImVec2 iconsize(120.f, 120.f);
// draw icons centered horizontally and vertically
ImVec2 alignment = ImVec2(0.4f, 0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);
// draw in 5 columns
ImGui::Columns(5, NULL, false);
// 4 selectable large icons
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
if (ImGui::Selectable( ICON_FA_BULLSEYE, &selected_view[1], 0, iconsize))
{
setView(View::MIXING);
*shift = 0;
}
ImGui::NextColumn();
if (ImGui::Selectable( ICON_FA_OBJECT_UNGROUP , &selected_view[2], 0, iconsize))
{
setView(View::GEOMETRY);
*shift = 0;
}
ImGui::NextColumn();
if (ImGuiToolkit::SelectableIcon(ICON_WORKSPACE, "", selected_view[3], iconsize))
{
setView(View::LAYER);
*shift = 0;
}
ImGui::NextColumn();
if (ImGui::Selectable( ICON_FA_CHESS_BOARD, &selected_view[4], 0, iconsize))
{
setView(View::TEXTURE);
*shift = 0;
}
// skip TRANSITION view
ImGui::NextColumn();
if (ImGuiToolkit::SelectableIcon(10, 7, "", selected_view[6], iconsize))
{
setView(View::DISPLAYS);
*shift = 0;
}
ImGui::PopFont();
// 5 subtitles (text centered in column)
for (int v = View::MIXING; v < View::INVALID; ++v) {
// skip TRANSITION view
if (v == View::TRANSITION)
continue;
ImGui::NextColumn();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (ImGui::GetColumnWidth() - ImGui::CalcTextSize(Settings::application.views[v].name.c_str()).x) * 0.5f - ImGui::GetStyle().ItemSpacing.x);
ImGuiToolkit::PushFont(Settings::application.current_view == v ? ImGuiToolkit::FONT_BOLD : ImGuiToolkit::FONT_DEFAULT);
ImGui::Text("%s", Settings::application.views[v].name.c_str());
ImGui::PopFont();
}
ImGui::Columns(1);
ImGui::PopStyleVar();
ImGui::End();
}
return target_index;
}
void UserInterface::setSourceInPanel(int index)
{
Mixer::manager().setCurrentIndex(index);
if (navigator.pannelVisible())
navigator.showPannelSource( Mixer::manager().indexCurrentSource() );
}
void UserInterface::setSourceInPanel(Source *s)
{
if (s) {
Mixer::manager().setCurrentSource( s );
if (navigator.pannelVisible())
navigator.showPannelSource( Mixer::manager().indexCurrentSource() );
}
}
Source *UserInterface::sourceInPanel()
{
Source *ret = nullptr;
int idxpanel = navigator.selectedPannelSource();
if (idxpanel > -1 && idxpanel < NAV_MAX) {
ret = Mixer::manager().sourceAtIndex(idxpanel);
}
return ret;
}
void UserInterface::showSourceEditor(Source *s)
{
Mixer::manager().unsetCurrentSource();
Mixer::selection().clear();
if (s) {
Mixer::manager().setCurrentSource( s );
if (!s->failed()) {
sourcecontrol.setVisible(true);
sourcecontrol.resetActiveSelection();
}
else
setSourceInPanel(s);
}
}
void UserInterface::RenderPreview()
{
static bool _inspector = false;
static bool _sustain = false;
static FrameBuffer *_framebuffer = nullptr;
if (show_preview != PREVIEW_NONE && !ImGui::IsPopupOpen("##RENDERPREVIEW")) {
// select which framebuffer to display depending on input
if (show_preview == PREVIEW_OUTPUT)
_framebuffer = Mixer::manager().session()->frame();
else if (show_preview == PREVIEW_SOURCE) {
_framebuffer = sourcecontrol.renderedFramebuffer();
if (_framebuffer == nullptr && Mixer::manager().currentSource() != nullptr) {
_framebuffer = Mixer::manager().currentSource()->frame();
}
}
// if a famebuffer is valid, open preview
if (_framebuffer != nullptr) {
ImGui::OpenPopup("##RENDERPREVIEW");
_inspector = false;
_sustain = false;
} else
show_preview = PREVIEW_NONE;
}
if (ImGui::BeginPopupModal("##RENDERPREVIEW", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoNav)) {
// making sure the pointer is still valid
if (_framebuffer != nullptr)
{
ImGuiIO& io = ImGui::GetIO();
float ar = _framebuffer->aspectRatio();
// image takes the available window area
ImVec2 imagesize = io.DisplaySize;
// image height respects original aspect ratio but is at most the available window height
imagesize.y = MIN( imagesize.x / ar, imagesize.y) * 0.95f;
// image respects original aspect ratio
imagesize.x = imagesize.y * ar;
// 100% opacity for the image (ensures true colors)
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.f);
ImGui::Image((void*)(intptr_t)_framebuffer->texture(), imagesize);
ImGui::PopStyleVar();
// closing icon in top left corner
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetCursorScreenPos(draw_pos + ImVec2(IMGUI_SAME_LINE, IMGUI_SAME_LINE));
if (ImGuiToolkit::IconButton(ICON_FA_TIMES, "Close preview"))
show_preview = PREVIEW_NONE;
ImGui::PopFont();
// handle mouse clic and hovering on image
const ImRect bb(draw_pos, draw_pos + imagesize);
const ImGuiID id = ImGui::GetCurrentWindow()->GetID("##preview-texture");
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb,
id,
&hovered,
&held,
ImGuiButtonFlags_PressedOnClick);
// toggle inspector on mouse clic
if (pressed)
_inspector = !_inspector;
// draw inspector (magnifying glass) on mouse hovering
if (hovered & _inspector)
DrawInspector(_framebuffer->texture(), imagesize, imagesize, draw_pos);
// close view on mouse clic outside
// and ignore show_preview on single clic
if (!hovered
&& !_sustain
&& !ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)
&& ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
show_preview = PREVIEW_NONE;
}
}
// local keyboard handler (because focus is captured by modal dialog)
if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE, false ) ||
(show_preview == PREVIEW_OUTPUT && ImGui::IsKeyPressed( GLFW_KEY_F6, false )) ||
(show_preview == PREVIEW_SOURCE && ImGui::IsKeyPressed( GLFW_KEY_F7, false )) )
show_preview = PREVIEW_NONE;
else if ((show_preview == PREVIEW_OUTPUT && ImGui::IsKeyPressed( GLFW_KEY_F6, true )) ||
(show_preview == PREVIEW_SOURCE && ImGui::IsKeyPressed( GLFW_KEY_F7, true )) )
_sustain = true;
else if ((show_preview == PREVIEW_OUTPUT && _sustain && ImGui::IsKeyReleased( GLFW_KEY_F6 )) ||
(show_preview == PREVIEW_SOURCE && _sustain && ImGui::IsKeyReleased( GLFW_KEY_F7 )) )
show_preview = PREVIEW_NONE;
if ( !alt_modifier_active && ImGui::IsKeyPressed( GLFW_KEY_TAB )) {
if (shift_modifier_active)
Mixer::manager().setCurrentPrevious();
else
Mixer::manager().setCurrentNext();
if (navigator.pannelVisible())
navigator.showPannelSource( Mixer::manager().indexCurrentSource() );
// re-open after change source
ImGui::CloseCurrentPopup();
}
// close
if (show_preview == PREVIEW_NONE) {
_framebuffer = nullptr;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
enum MetricsFlags_
{
Metrics_none = 0,
Metrics_framerate = 1,
Metrics_ram = 2,
Metrics_gpu = 4,
Metrics_session = 8,
Metrics_runtime = 16,
Metrics_lifetime = 32
};
void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode)
{
if (!p_open || !p_corner || !p_mode)
return;
if (*p_mode == Metrics_none)
*p_mode = Metrics_framerate;
ImGuiIO& io = ImGui::GetIO();
if (*p_corner != -1) {
ImVec2 window_pos = ImVec2((*p_corner & 1) ? io.DisplaySize.x - WINDOW_TOOLBOX_DIST_TO_BORDER : WINDOW_TOOLBOX_DIST_TO_BORDER,
(*p_corner & 2) ? io.DisplaySize.y - WINDOW_TOOLBOX_DIST_TO_BORDER : WINDOW_TOOLBOX_DIST_TO_BORDER);
ImVec2 window_pos_pivot = ImVec2((*p_corner & 1) ? 1.0f : 0.0f, (*p_corner & 2) ? 1.0f : 0.0f);
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
}
ImGui::SetNextWindowBgAlpha(WINDOW_TOOLBOX_ALPHA); // Transparent background
if (!ImGui::Begin("Metrics", NULL, (*p_corner != -1 ? ImGuiWindowFlags_NoMove : 0) |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
ImGui::End();
return;
}
// title
ImGui::Text( MENU_METRICS );
ImGui::SameLine(0, 2.2f * ImGui::GetTextLineHeightWithSpacing());
if (ImGuiToolkit::IconButton(5,8))
ImGui::OpenPopup("metrics_menu");
// read Memory info every 1/2 second
static long ram = 0;
static glm::ivec2 gpu(INT_MAX, INT_MAX);
{
static GTimer *timer = g_timer_new ();
double elapsed = g_timer_elapsed (timer, NULL);
if ( elapsed > 0.5 ){
ram = SystemToolkit::memory_usage();
gpu = Rendering::manager().getGPUMemoryInformation();
g_timer_start(timer);
}
}
static char dummy_str[256];
uint64_t time = Runtime();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.f, 2.5f));
const float _width = 4.f * ImGui::GetTextLineHeightWithSpacing();
if (*p_mode & Metrics_framerate) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
snprintf(dummy_str, 256, "%.1f", io.Framerate);
ImGui::SetNextItemWidth(_width);
ImGui::InputText("##dummy", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopFont();
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("FPS");
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Frames per second");
}
if (*p_mode & Metrics_ram) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
snprintf(dummy_str, 256, "%s", BaseToolkit::byte_to_string( ram ).c_str());
ImGui::SetNextItemWidth(_width);
ImGui::InputText("##dummy2", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopFont();
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("RAM");
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Amount of physical memory\nused by vimix");
}
// GPU RAM if available
if (gpu.x < INT_MAX && gpu.x > 0 && *p_mode & Metrics_gpu) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
// got free and max GPU RAM (nvidia)
if (gpu.y < INT_MAX && gpu.y > 0)
snprintf(dummy_str, 256, "%s", BaseToolkit::byte_to_string( long(gpu.y-gpu.x) * 1024 ).c_str());
// got used GPU RAM (ati)
else
snprintf(dummy_str, 256, "%s", BaseToolkit::byte_to_string( long(gpu.x) * 1024 ).c_str());
ImGui::SetNextItemWidth(_width);
ImGui::InputText("##dummy3", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopFont();
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("GPU");
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Total memory used in GPU");
}
if (*p_mode & Metrics_session) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
snprintf(dummy_str, 256, "%s", GstToolkit::time_to_string(Mixer::manager().session()->runtime(), GstToolkit::TIME_STRING_READABLE).c_str());
ImGui::SetNextItemWidth(_width);
ImGui::InputText("##dummy", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopFont();
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Session");
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Runtime since session load");
}
if (*p_mode & Metrics_runtime) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
snprintf(dummy_str, 256, "%s", GstToolkit::time_to_string(time, GstToolkit::TIME_STRING_READABLE).c_str());
ImGui::SetNextItemWidth(_width);
ImGui::InputText("##dummy2", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopFont();
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Runtime");
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Runtime since vimix started");
}
if (*p_mode & Metrics_lifetime) {
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
time += Settings::application.total_runtime;
snprintf(dummy_str, 256, "%s", GstToolkit::time_to_string(time, GstToolkit::TIME_STRING_READABLE).c_str());
ImGui::SetNextItemWidth(_width);
ImGui::InputText("##dummy3", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopFont();
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Lifetime");
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Accumulated runtime of vimix\nsince its installation");
}
ImGui::PopStyleVar();
if (ImGui::BeginPopup("metrics_menu"))
{
if (ImGui::MenuItem( "Framerate", NULL, *p_mode & Metrics_framerate))
*p_mode ^= Metrics_framerate;
if (ImGui::MenuItem( "RAM", NULL, *p_mode & Metrics_ram))
*p_mode ^= Metrics_ram;
// GPU RAM if available
if (gpu.x < INT_MAX && gpu.x > 0)
if (ImGui::MenuItem( "GPU", NULL, *p_mode & Metrics_gpu))
*p_mode ^= Metrics_gpu;
if (ImGui::MenuItem( "Session time", NULL, *p_mode & Metrics_session))
*p_mode ^= Metrics_session;
if (ImGui::MenuItem( "Runtime", NULL, *p_mode & Metrics_runtime))
*p_mode ^= Metrics_runtime;
if (ImGui::MenuItem( "Lifetime", NULL, *p_mode & Metrics_lifetime))
*p_mode ^= Metrics_lifetime;
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_ANGLE_UP " Top right", NULL, *p_corner == 1))
*p_corner = 1;
if (ImGui::MenuItem( ICON_FA_ANGLE_DOWN " Bottom right", NULL, *p_corner == 3))
*p_corner = 3;
if (ImGui::MenuItem( ICON_FA_ARROWS_ALT " Free position", NULL, *p_corner == -1))
*p_corner = -1;
if (p_open && ImGui::MenuItem( ICON_FA_TIMES " Close"))
*p_open = false;
ImGui::EndPopup();
}
ImGui::End();
}
enum SourceToolbarFlags_
{
SourceToolbar_none = 0,
SourceToolbar_linkar = 1,
SourceToolbar_autohide = 2
};
void UserInterface::RenderSourceToolbar(bool *p_open, int* p_border, int *p_mode) {
if (!p_open || !p_border || !p_mode || !Mixer::manager().session()->ready())
return;
Source *s = Mixer::manager().currentSource();
if (s || !(*p_mode & SourceToolbar_autohide) ) {
ImGuiIO& io = ImGui::GetIO();
std::ostringstream info;
const glm::vec3 out = Mixer::manager().session()->frame()->resolution();
const char *tooltip_lock[2] = {"Width & height not linked", "Width & height linked"};
//
// horizontal layout for top and bottom placements
//
if (*p_border > 0) {
ImVec2 window_pos = ImVec2((*p_border & 1) ? io.DisplaySize.x * 0.5 : WINDOW_TOOLBOX_DIST_TO_BORDER,
(*p_border & 2) ? io.DisplaySize.y - WINDOW_TOOLBOX_DIST_TO_BORDER : WINDOW_TOOLBOX_DIST_TO_BORDER);
ImVec2 window_pos_pivot = ImVec2((*p_border & 1) ? 0.5f : 0.0f, (*p_border & 2) ? 1.0f : 0.0f);
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
ImGui::SetNextWindowBgAlpha(WINDOW_TOOLBOX_ALPHA); // Transparent background
if (!ImGui::Begin("SourceToolbarfixed", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav))
{
ImGui::End();
return;
}
const float sliderwidth = 3.f * ImGui::GetTextLineHeightWithSpacing();
if (s) {
// get info on source
Group *n = s->group(View::GEOMETRY);
info << s->name() << ": ";
//
// ALPHA
//
float v = s->alpha() * 100.f;
if (ImGuiToolkit::TextButton( ICON_FA_BULLSEYE , "Alpha")) {
s->call(new SetAlpha(1.f), true);
info << "Alpha " << std::fixed << std::setprecision(3) << 0.f;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(sliderwidth);
if ( ImGui::DragFloat("##Alpha", &v, 0.1f, 0.f, 100.f, "%.1f%%") )
s->call(new SetAlpha(v*0.01f), true);
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f ){
v = CLAMP(v + 0.1f * io.MouseWheel, 0.f, 100.f);
s->call(new SetAlpha(v*0.01f), true);
info << "Alpha " << std::fixed << std::setprecision(3) << v*0.01f;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ) {
info << "Alpha " << std::fixed << std::setprecision(3) << v*0.01f;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("|");
//
// POSITION COORDINATES
//
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton( ICON_FA_SIGN, "Position")) {
n->translation_.x = 0.f;
n->translation_.y = 0.f;
s->touch();
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
// Position X
v = n->translation_.x * (0.5f * out.y);
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth( sliderwidth);
if ( ImGui::DragFloat("##PosX", &v, 1.0f, -MAX_SCALE * (0.5f * out.y), MAX_SCALE * (0.5f * out.y), "%.0fpx") ) {
n->translation_.x = v / (0.5f * out.y);
s->touch();
}
if ( ImGui::IsItemHovered() && io.MouseWheel != 0.f ){
v += io.MouseWheel;
n->translation_.x = v / (0.5f * out.y);
s->touch();
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
// Position Y
v = n->translation_.y * (0.5f * out.y);
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth( sliderwidth);
if ( ImGui::DragFloat("##PosY", &v, 1.0f, -MAX_SCALE * (0.5f * out.y), MAX_SCALE * (0.5f * out.y), "%.0fpx") ) {
n->translation_.y = v / (0.5f * out.y);
s->touch();
}
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f ){
v += io.MouseWheel;
n->translation_.y = v / (0.5f * out.y);
s->touch();
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("|");
//
// SCALE
//
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton( ICON_FA_RULER_COMBINED, "Size")) {
n->scale_.x = 1.f;
n->scale_.y = 1.f;
s->touch();
info << "Scale " << std::setprecision(3) << n->scale_.x << ", " << n->scale_.y;
Action::manager().store(info.str());
}
float ar_scale = n->scale_.x / n->scale_.y;
// SCALE X
v = n->scale_.x * ( out.y * s->frame()->aspectRatio());
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth( sliderwidth );
if ( ImGui::DragFloat("##ScaleX", &v, 1.f, -MAX_SCALE * out.x, MAX_SCALE * out.x, "%.0fpx") ) {
if (v > 10.f) {
n->scale_.x = v / ( out.y * s->frame()->aspectRatio());
if (*p_mode & SourceToolbar_linkar)
n->scale_.y = n->scale_.x / ar_scale;
s->touch();
}
}
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f && v > 10.f){
v += io.MouseWheel;
n->scale_.x = v / ( out.y * s->frame()->aspectRatio());
if (*p_mode & SourceToolbar_linkar)
n->scale_.y = n->scale_.x / ar_scale;
s->touch();
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
// SCALE LOCK ASPECT RATIO
ImGui::SameLine(0, 0);
bool lock = *p_mode & SourceToolbar_linkar;
if (ImGuiToolkit::IconToggle(5,1,6,1, &lock, tooltip_lock ))
*p_mode ^= SourceToolbar_linkar; // *p_mode |= lock ? SourceToolbar_linkar : !SourceToolbar_linkar;
ImGui::SameLine(0, 0);
// SCALE Y
v = n->scale_.y * out.y;
ImGui::SetNextItemWidth( sliderwidth );
if ( ImGui::DragFloat("##ScaleY", &v, 1.f, -MAX_SCALE * out.y, MAX_SCALE * out.y, "%.0fpx") ) {
if (v > 10.f) {
n->scale_.y = v / out.y;
if (*p_mode & SourceToolbar_linkar)
n->scale_.x = n->scale_.y * ar_scale;
s->touch();
}
}
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f && v > 10.f){
v += io.MouseWheel;
n->scale_.y = v / out.y;
if (*p_mode & SourceToolbar_linkar)
n->scale_.x = n->scale_.y * ar_scale;
s->touch();
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("|");
//
// ROTATION ANGLE
//
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::IconButton( 18, 9, "Angle")) {
n->rotation_.z = 0.f;
s->touch();
info << "Angle " << std::setprecision(3) << n->rotation_.z * 180.f / M_PI;
Action::manager().store(info.str());
}
float v_deg = n->rotation_.z * 360.0f / (2.f*M_PI);
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(sliderwidth);
if ( ImGui::DragFloat("##Angle", &v_deg, 0.02f, -180.f, 180.f, "%.2f" UNICODE_DEGREE) ) {
n->rotation_.z = v_deg * (2.f*M_PI) / 360.0f;
s->touch();
}
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f){
v_deg = CLAMP(v_deg + 0.01f * io.MouseWheel, -180.f, 180.f);
n->rotation_.z = v_deg * (2.f*M_PI) / 360.0f;
s->touch();
info << "Angle " << std::setprecision(3) << n->rotation_.z * 180.f / M_PI;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ) {
info << "Angle " << std::setprecision(3) << n->rotation_.z * 180.f / M_PI;
Action::manager().store(info.str());
}
ImGui::SameLine(0, 2 * IMGUI_SAME_LINE);
}
// NO SOURCE and not auto hide
else {
ImGui::AlignTextToFramePadding();
ImGui::Text( MENU_SOURCE_TOOL );
ImGui::SameLine(0, sliderwidth);
ImGui::TextDisabled("No active source");
ImGui::SameLine(0, sliderwidth);
}
if (ImGuiToolkit::IconButton(5,8))
ImGui::OpenPopup("sourcetool_menu");
}
//
// compact layout for free placement
//
else {
ImGui::SetNextWindowPos(ImVec2(690,20), ImGuiCond_FirstUseEver); // initial pos
ImGui::SetNextWindowBgAlpha(WINDOW_TOOLBOX_ALPHA); // Transparent background
if (!ImGui::Begin("SourceToolbar", NULL, ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav))
{
ImGui::End();
return;
}
// Title window
ImGui::Text( MENU_SOURCE_TOOL );
ImGui::SameLine(0, 2.f * ImGui::GetTextLineHeightWithSpacing());
if (ImGuiToolkit::IconButton(5,8))
ImGui::OpenPopup("sourcetool_menu");
// WITH SOURCE
if (s) {
// get info on source
Group *n = s->group(View::GEOMETRY);
info << s->name() << ": ";
const float sliderwidth = 6.4f * ImGui::GetTextLineHeightWithSpacing();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.f, 2.f));
//
// ALPHA
//
float v = s->alpha() * 100.f;
ImGui::SetNextItemWidth(sliderwidth);
if ( ImGui::DragFloat("##Alpha", &v, 0.1f, 0.f, 100.f, "%.1f%%") )
s->call(new SetAlpha(v*0.01f), true);
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f ){
v = CLAMP(v + 0.1f * io.MouseWheel, 0.f, 100.f);
s->call(new SetAlpha(v*0.01f), true);
info << "Alpha " << std::fixed << std::setprecision(3) << v*0.01f;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ) {
info << "Alpha " << std::fixed << std::setprecision(3) << v*0.01f;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Alpha")) {
s->call(new SetAlpha(1.f), true);
info << "Alpha " << std::fixed << std::setprecision(3) << 0.f;
Action::manager().store(info.str());
}
//
// POSITION COORDINATES
//
// Position X
v = n->translation_.x * (0.5f * out.y);
ImGui::SetNextItemWidth( 3.08f * ImGui::GetTextLineHeightWithSpacing() );
if ( ImGui::DragFloat("##PosX", &v, 1.0f, -MAX_SCALE * (0.5f * out.y), MAX_SCALE * (0.5f * out.y), "%.0fpx") ) {
n->translation_.x = v / (0.5f * out.y);
s->touch();
}
if ( ImGui::IsItemHovered() && io.MouseWheel != 0.f ){
v += io.MouseWheel;
n->translation_.x = v / (0.5f * out.y);
s->touch();
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
// Position Y
v = n->translation_.y * (0.5f * out.y);
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth( 3.08f * ImGui::GetTextLineHeightWithSpacing() );
if ( ImGui::DragFloat("##PosY", &v, 1.0f, -MAX_SCALE * (0.5f * out.y), MAX_SCALE * (0.5f * out.y), "%.0fpx") ) {
n->translation_.y = v / (0.5f * out.y);
s->touch();
}
if ( ImGui::IsItemHovered() && io.MouseWheel != 0.f ){
v += io.MouseWheel;
n->translation_.y = v / (0.5f * out.y);
s->touch();
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Pos")) {
n->translation_.x = 0.f;
n->translation_.y = 0.f;
s->touch();
info << "Position " << std::setprecision(3) << n->translation_.x << ", " << n->translation_.y;
Action::manager().store(info.str());
}
//
// SCALE
//
float ar_scale = n->scale_.x / n->scale_.y;
// SCALE X
v = n->scale_.x * ( out.y * s->frame()->aspectRatio());
ImGui::SetNextItemWidth( 2.7f * ImGui::GetTextLineHeightWithSpacing() );
if ( ImGui::DragFloat("##ScaleX", &v, 1.f, -MAX_SCALE * out.x, MAX_SCALE * out.x, "%.0f") ) {
if (v > 10.f) {
n->scale_.x = v / ( out.y * s->frame()->aspectRatio());
if (*p_mode & SourceToolbar_linkar)
n->scale_.y = n->scale_.x / ar_scale;
s->touch();
}
}
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f && v > 10.f){
v += io.MouseWheel;
n->scale_.x = v / ( out.y * s->frame()->aspectRatio());
if (*p_mode & SourceToolbar_linkar)
n->scale_.y = n->scale_.x / ar_scale;
s->touch();
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
// SCALE LOCK ASPECT RATIO
ImGui::SameLine(0, 0);
bool lock = *p_mode & SourceToolbar_linkar;
if (ImGuiToolkit::IconToggle(5,1,6,1, &lock, tooltip_lock ))
*p_mode ^= SourceToolbar_linkar;
ImGui::SameLine(0, 0);
// SCALE Y
v = n->scale_.y * out.y ;
ImGui::SetNextItemWidth( 2.7f * ImGui::GetTextLineHeightWithSpacing() );
if ( ImGui::DragFloat("##ScaleY", &v, 1.f, -MAX_SCALE * out.y, MAX_SCALE * out.y, "%.0f") ) {
if (v > 10.f) {
n->scale_.y = v / out.y;
if (*p_mode & SourceToolbar_linkar)
n->scale_.x = n->scale_.y * ar_scale;
s->touch();
}
}
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f && v > 10.f){
v += io.MouseWheel;
n->scale_.y = v / out.y;
if (*p_mode & SourceToolbar_linkar)
n->scale_.x = n->scale_.y * ar_scale;
s->touch();
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ){
info << "Scale " << std::setprecision(3) << n->scale_.x << " x " << n->scale_.y;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Size")) {
n->scale_.x = 1.f;
n->scale_.y = 1.f;
s->touch();
info << "Scale " << std::setprecision(3) << n->scale_.x << ", " << n->scale_.y;
Action::manager().store(info.str());
}
//
// ROTATION ANGLE
//
float v_deg = n->rotation_.z * 360.0f / (2.f*M_PI);
ImGui::SetNextItemWidth(sliderwidth);
if ( ImGui::DragFloat("##Angle", &v_deg, 0.02f, -180.f, 180.f, "%.2f" UNICODE_DEGREE) ) {
n->rotation_.z = v_deg * (2.f*M_PI) / 360.0f;
s->touch();
}
if (ImGui::IsItemHovered() && io.MouseWheel != 0.f){
v_deg = CLAMP(v_deg + 0.01f * io.MouseWheel, -180.f, 180.f);
n->rotation_.z = v_deg * (2.f*M_PI) / 360.0f;
s->touch();
info << "Angle " << std::setprecision(3) << n->rotation_.z * 180.f / M_PI;
Action::manager().store(info.str());
}
if ( ImGui::IsItemDeactivatedAfterEdit() ) {
info << "Angle " << std::setprecision(3) << n->rotation_.z * 180.f / M_PI;
Action::manager().store(info.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Angle")) {
n->rotation_.z = 0.f;
s->touch();
info << "Angle " << std::setprecision(3) << n->rotation_.z * 180.f / M_PI;
Action::manager().store(info.str());
}
ImGui::PopStyleVar();
}
// NO SOURCE and not auto hide
else {
ImGui::TextDisabled(" ");
ImGui::TextDisabled("No active source");
ImGui::TextDisabled(" ");
}
}
if (ImGui::BeginPopup("sourcetool_menu"))
{
if (ImGui::MenuItem( "Auto hide", NULL, *p_mode & SourceToolbar_autohide))
*p_mode ^= SourceToolbar_autohide;
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_ANGLE_UP " Top", NULL, *p_border == 1))
*p_border = 1;
if (ImGui::MenuItem( ICON_FA_ANGLE_DOWN " Bottom", NULL, *p_border == 3))
*p_border = 3;
if (ImGui::MenuItem( ICON_FA_ARROWS_ALT " Free position", NULL, *p_border == -1))
*p_border = -1;
if (p_open && ImGui::MenuItem( ICON_FA_TIMES " Close"))
*p_open = false;
ImGui::EndPopup();
}
ImGui::Spacing();
ImGui::End();
}
}
void UserInterface::RenderAbout(bool* p_open)
{
ImGui::SetNextWindowPos(ImVec2(600, 40), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("About " APP_TITLE, p_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::End();
return;
}
ImVec2 top = ImGui::GetCursorScreenPos();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
#ifdef VIMIX_VERSION_MAJOR
ImGui::Text("%s %d.%d.%d", APP_NAME, VIMIX_VERSION_MAJOR, VIMIX_VERSION_MINOR, VIMIX_VERSION_PATCH);
#else
ImGui::Text("%s", APP_NAME);
#endif
ImGui::PopFont();
#ifdef VIMIX_GIT
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC);
ImGui::Text(VIMIX_GIT);
ImGui::PopFont();
#endif
static unsigned int img_crow = 0;
if (img_crow == 0)
img_crow = Resource::getTextureImage("images/vimix_crow_white.png");
ImGui::SetCursorScreenPos(top);
ImGui::Image((void*)(intptr_t)img_crow, ImVec2(512, 340));
ImGui::Text("vimix performs graphical mixing and blending of\nseveral movie clips and computer generated graphics,\nwith image processing effects in real-time.");
ImGui::Text("\nvimix is licensed under GNU GPL version 3 or later.\n" UNICODE_COPYRIGHT " 2019-2023 Bruno Herbelin.");
ImGui::Spacing();
ImGuiToolkit::ButtonOpenUrl("Visit vimix website", "https://brunoherbelin.github.io/vimix/", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
ImGui::Text("Learn more about the libraries behind vimix:");
ImGui::Spacing();
if ( ImGui::Button("About GStreamer (available plugins)", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
show_gst_about = true;
if ( ImGui::Button("About OpenGL (runtime extensions)", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
show_opengl_about = true;
if ( ImGui::Button("About Dear ImGui (build information)", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
show_imgui_about = true;
ImGui::Columns(3, "abouts");
ImGui::Separator();
ImGuiToolkit::ButtonOpenUrl("Glad", "https://glad.dav1d.de", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("GLFW", "http://www.glfw.org", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("glm", "https://glm.g-truc.net", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("OSCPack", "https://github.com/RossBencina/oscpack", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("TinyXML2", "https://github.com/leethomason/tinyxml2.git", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("STB", "https://github.com/nothings/stb", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::Columns(1);
ImGui::End();
}
void UserInterface::showPannel(int id)
{
if (id == NAV_MENU)
navigator.togglePannelMenu();
else if (id == NAV_NEW)
navigator.togglePannelNew();
else
navigator.showPannelSource(id);
}
void UserInterface::RenderNotes()
{
Session *se = Mixer::manager().session();
if (se!=nullptr && se->beginNotes() != se->endNotes()) {
ImVec4 color = ImGui::GetStyle().Colors[ImGuiCol_ResizeGripHovered];
color.w = 0.35f;
ImGui::PushStyleColor(ImGuiCol_WindowBg, color);
ImGui::PushStyleColor(ImGuiCol_TitleBg, color);
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, color);
ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, color);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());
for (auto note = se->beginNotes(); note != se->endNotes(); ) {
// detect close clic
bool close = false;
if ( (*note).stick < 1 || (*note).stick == Settings::application.current_view)
{
// window
ImGui::SetNextWindowSizeConstraints(ImVec2(150, 150), ImVec2(500, 500));
ImGui::SetNextWindowPos(ImVec2( (*note).pos.x, (*note).pos.y ), ImGuiCond_Once);
ImGui::SetNextWindowSize(ImVec2((*note).size.x, (*note).size.y), ImGuiCond_Once);
ImGui::SetNextWindowBgAlpha(color.w); // Transparent background
// draw
if (ImGui::Begin((*note).label.c_str(), NULL, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoSavedSettings))
{
ImVec2 size = ImGui::GetContentRegionAvail();
ImVec2 pos = ImGui::GetCursorPos();
// close & delete
close = ImGuiToolkit::IconButton(4,16,"Delete");
if (ImGui::IsWindowFocused()) {
// font size
pos.x = size.x - 2.f * ImGui::GetTextLineHeightWithSpacing();
ImGui::SetCursorPos( pos );
if (ImGuiToolkit::IconButton(1, 13) )
(*note).large = !(*note).large ;
// stick to views icon
pos.x = size.x - ImGui::GetTextLineHeightWithSpacing() + 8.f;
ImGui::SetCursorPos( pos );
bool s = (*note).stick > 0;
if (ImGuiToolkit::IconToggle(5, 2, 4, 2, &s) )
(*note).stick = s ? Settings::application.current_view : 0;
}
// Text area
size.y -= ImGui::GetTextLineHeightWithSpacing() + 2.f;
ImGuiToolkit::PushFont( (*note).large ? ImGuiToolkit::FONT_LARGE : ImGuiToolkit::FONT_MONO );
ImGuiToolkit::InputTextMultiline("##notes", &(*note).text, size);
ImGui::PopFont();
// TODO smart detect when window moves
ImVec2 p = ImGui::GetWindowPos();
(*note).pos = glm::vec2( p.x, p.y);
p = ImGui::GetWindowSize();
(*note).size = glm::vec2( p.x, p.y);
ImGui::End();
}
}
// loop
if (close)
note = se->deleteNote(note);
else
++note;
}
ImGui::PopStyleColor(5);
}
}
///
/// TOOLBOX
///
ToolBox::ToolBox()
{
show_demo_window = false;
show_icons_window = false;
show_sandbox = false;
}
void ToolBox::Render()
{
static bool record_ = false;
static std::ofstream csv_file_;
// first run
ImGui::SetNextWindowPos(ImVec2(40, 40), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(350, 300), ImVec2(FLT_MAX, FLT_MAX));
if ( !ImGui::Begin(IMGUI_TITLE_TOOLBOX, &Settings::application.widget.toolbox, ImGuiWindowFlags_MenuBar) )
{
ImGui::End();
return;
}
// Menu Bar
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Render"))
{
if ( ImGui::MenuItem( MENU_CAPTUREGUI, SHORTCUT_CAPTURE_GUI) )
UserInterface::manager().StartScreenshot();
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Gui"))
{
ImGui::MenuItem("Sandbox", nullptr, &show_sandbox);
ImGui::MenuItem("Icons", nullptr, &show_icons_window);
ImGui::MenuItem("Demo ImGui", nullptr, &show_demo_window);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Stats"))
{
if (ImGui::MenuItem("Record", nullptr, &record_) )
{
if ( record_ )
csv_file_.open( SystemToolkit::home_path() + std::to_string(BaseToolkit::uniqueId()) + ".csv", std::ofstream::out | std::ofstream::app);
else
csv_file_.close();
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
//
// display histogram of update time and plot framerate
//
// keep array of 180 values, i.e. approx 3 seconds of recording
static float recorded_values[3][PLOT_ARRAY_SIZE] = {{}};
static float recorded_sum[3] = { 0.f, 0.f, 0.f };
static float recorded_bounds[3][2] = { {40.f, 65.f}, {1.f, 50.f}, {0.f, 50.f} };
static float refresh_rate = -1.f;
static int values_index = 0;
float megabyte = static_cast<float>( static_cast<double>(FrameBuffer::memory_usage()) / 1000000.0 );
// init
if (refresh_rate < 0.f) {
const GLFWvidmode* mode = glfwGetVideoMode(Rendering::manager().mainWindow().monitor());
refresh_rate = float(mode->refreshRate);
if (Settings::application.render.vsync > 0)
refresh_rate /= Settings::application.render.vsync;
else
refresh_rate = 0.f;
recorded_bounds[0][0] = refresh_rate - 15.f; // min fps
recorded_bounds[0][1] = refresh_rate + 10.f; // max
for(int i = 0; i<PLOT_ARRAY_SIZE; ++i) {
recorded_values[0][i] = refresh_rate;
recorded_sum[0] += recorded_values[0][i];
recorded_values[1][i] = 16.f;
recorded_sum[1] += recorded_values[1][i];
recorded_values[2][i] = megabyte;
recorded_sum[2] += recorded_values[2][i];
}
}
// compute average step 1: remove previous value from the sum
recorded_sum[0] -= recorded_values[0][values_index];
recorded_sum[1] -= recorded_values[1][values_index];
recorded_sum[2] -= recorded_values[2][values_index];
// store values
recorded_values[0][values_index] = MINI(ImGui::GetIO().Framerate, 1000.f);
recorded_values[1][values_index] = MINI(Mixer::manager().dt(), 100.f);
recorded_values[2][values_index] = megabyte;
// compute average step 2: add current value to the sum
recorded_sum[0] += recorded_values[0][values_index];
recorded_sum[1] += recorded_values[1][values_index];
recorded_sum[2] += recorded_values[2][values_index];
// move inside array
values_index = (values_index+1) % PLOT_ARRAY_SIZE;
// non-vsync fixed FPS : have to calculate plot dimensions based on past values
if (refresh_rate < 1.f) {
recorded_bounds[0][0] = recorded_sum[0] / float(PLOT_ARRAY_SIZE) - 15.f;
recorded_bounds[0][1] = recorded_sum[0] / float(PLOT_ARRAY_SIZE) + 10.f;
}
recorded_bounds[2][0] = recorded_sum[2] / float(PLOT_ARRAY_SIZE) - 400.f;
recorded_bounds[2][1] = recorded_sum[2] / float(PLOT_ARRAY_SIZE) + 300.f;
// plot values, with title overlay to display the average
ImVec2 plot_size = ImGui::GetContentRegionAvail();
plot_size.y *= 0.32;
char overlay[128];
snprintf(overlay, 128, "Rendering %.1f FPS", recorded_sum[0] / float(PLOT_ARRAY_SIZE));
ImGui::PlotLines("LinesRender", recorded_values[0], PLOT_ARRAY_SIZE, values_index, overlay, recorded_bounds[0][0], recorded_bounds[0][1], plot_size);
snprintf(overlay, 128, "Update time %.1f ms (%.1f FPS)", recorded_sum[1] / float(PLOT_ARRAY_SIZE), (float(PLOT_ARRAY_SIZE) * 1000.f) / recorded_sum[1]);
ImGui::PlotHistogram("LinesMixer", recorded_values[1], PLOT_ARRAY_SIZE, values_index, overlay, recorded_bounds[1][0], recorded_bounds[1][1], plot_size);
snprintf(overlay, 128, "Framebuffers %.1f MB", recorded_values[2][(values_index+PLOT_ARRAY_SIZE-1) % PLOT_ARRAY_SIZE] );
ImGui::PlotLines("LinesMemo", recorded_values[2], PLOT_ARRAY_SIZE, values_index, overlay, recorded_bounds[2][0], recorded_bounds[2][1], plot_size);
ImGui::End();
// save to file
if ( record_ && csv_file_.is_open()) {
csv_file_ << megabyte << ", " << ImGui::GetIO().Framerate << std::endl;
}
// About and other utility windows
if (show_icons_window)
ImGuiToolkit::ShowIconsWindow(&show_icons_window);
if (show_sandbox)
ShowSandbox(&show_sandbox);
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
}
void UserInterface::RenderHelp()
{
// first run
ImGui::SetNextWindowPos(ImVec2(520, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(460, 800), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(350, 300), ImVec2(FLT_MAX, FLT_MAX));
if ( !ImGui::Begin(IMGUI_TITLE_HELP, &Settings::application.widget.help, 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.help = false;
if (ImGui::BeginMenu(IMGUI_TITLE_HELP))
{
// Enable/Disable Ableton Link
if ( ImGui::MenuItem( ICON_FA_BOOK_OPEN " Online wiki") ) {
SystemToolkit::open("https://github.com/brunoherbelin/vimix/wiki");
}
// Enable/Disable tooltips
if ( ImGui::MenuItem( ICON_FA_QUESTION_CIRCLE " Show tooltips", nullptr, &Settings::application.show_tooptips) ) {
ImGuiToolkit::setToolTipsEnabled( Settings::application.show_tooptips );
}
// output manager menu
ImGui::Separator();
if ( ImGui::MenuItem( MENU_CLOSE, SHORTCUT_HELP) )
Settings::application.widget.help = false;
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
const float width_window = ImGui::GetWindowSize().x - ImGui::GetFontSize();
const float width_column0 = ImGui::GetFontSize() * 6;
if (ImGui::CollapsingHeader("Documentation", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Columns(2, "doccolumn", false); // 4-ways, with border
ImGui::SetColumnWidth(0, width_column0);
ImGui::Text("General"); ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("User manual", "https://github.com/brunoherbelin/vimix/wiki/User-manual", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGui::Text("Filters"); ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("Filters and ShaderToy reference", "https://github.com/brunoherbelin/vimix/wiki/Filters-and-ShaderToy", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGui::Text("OSC"); ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("Open Sound Control API", "https://github.com/brunoherbelin/vimix/wiki/Open-Sound-Control-API", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGui::Text("SRT"); ImGui::NextColumn();
ImGuiToolkit::ButtonOpenUrl("Secure Reliable Transport Broadcast", "https://github.com/brunoherbelin/vimix/wiki/SRT-stream-I-O", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::Columns(1);
}
if (ImGui::CollapsingHeader("Views"))
{
ImGui::Columns(2, "viewscolumn", false); // 4-ways, with border
ImGui::SetColumnWidth(0, width_column0);
ImGui::PushTextWrapPos(width_window );
ImGui::Text(ICON_FA_MOUSE_POINTER " Snap cursor"); ImGui::NextColumn();
ImGui::Text ("Snapping mouse cursors modify the mouse effective position to enhace the movement: e.g. snap to grid, move on a line, or trigger on metronome. "
"They are activated with the [" ALT_MOD "] key" );
ImGui::NextColumn();
ImGui::Text(ICON_FA_BULLSEYE " Mixing"); ImGui::NextColumn();
ImGui::Text ("Adjust opacity of sources, visible in the center and transparent on the side. Sources are de-activated outside of darker circle.");
ImGui::NextColumn();
ImGui::Text(ICON_FA_OBJECT_UNGROUP " Geometry"); ImGui::NextColumn();
ImGui::Text ("Move, scale, rotate or crop sources to place them in the output frame.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_WORKSPACE); ImGui::SameLine(0, IMGUI_SAME_LINE); ImGui::Text("Layers"); ImGui::NextColumn();
ImGui::Text ("Organize the rendering order of sources in depth, from background to foreground.");
ImGui::NextColumn();
ImGui::Text(ICON_FA_CHESS_BOARD " Texturing"); ImGui::NextColumn();
ImGui::Text ("Apply masks or freely paint the texture on the source surface. Repeat or crop the graphics.");
ImGui::NextColumn();
ImGui::Text(ICON_FA_TV " Displays"); ImGui::NextColumn();
ImGui::Text ("Manage and place output windows in computer's displays (e.g. fullscreen mode, white balance adjustment).");
ImGui::NextColumn();
ImGui::Columns(1);
ImGui::PopTextWrapPos();
}
if (ImGui::CollapsingHeader("Windows"))
{
ImGui::Columns(2, "windowcolumn", false); // 4-ways, with border
ImGui::SetColumnWidth(0, width_column0);
ImGui::PushTextWrapPos(width_window );
ImGui::Text(IMGUI_TITLE_PREVIEW); ImGui::NextColumn();
ImGui::Text ("Preview the output displayed in the rendering window(s). Control video recording and streaming.");
ImGui::NextColumn();
ImGui::Text(IMGUI_TITLE_MEDIAPLAYER); ImGui::NextColumn();
ImGui::Text ("Play, pause, rewind videos or dynamic sources. Control play duration, speed and synchronize multiple videos.");
ImGui::NextColumn();
ImGui::Text(IMGUI_TITLE_TIMER); ImGui::NextColumn();
ImGui::Text ("Keep track of time with a stopwatch or a metronome (Ableton Link).");
ImGui::NextColumn();
ImGui::Text(ICON_FA_HAND_PAPER " Inputs"); ImGui::NextColumn();
ImGui::Text ("Define how user inputs (e.g. keyboard, joystick) are mapped to custom actions in the session.");
ImGui::NextColumn();
ImGui::Text(IMGUI_TITLE_LOGS); ImGui::NextColumn();
ImGui::Text ("History of program logs, with information on success and failure of commands.");
ImGui::NextColumn();
ImGui::Text(IMGUI_TITLE_HELP); ImGui::NextColumn();
ImGui::Text ("Link to online documentation and list of concepts (this window).");
ImGui::NextColumn();
ImGui::Text(ICON_FA_WRENCH " Source"); ImGui::NextColumn();
ImGui::Text ("Toolbar to show and edit alpha and geometry of the current source.");
ImGui::NextColumn();
ImGui::Text(ICON_FA_TACHOMETER_ALT " Metrics"); ImGui::NextColumn();
ImGui::Text ("Monitoring of metrics on the system (e.g. FPS, RAM) and runtime (e.g. session duration).");
ImGui::Columns(1);
ImGui::PopTextWrapPos();
}
if (ImGui::CollapsingHeader("Sources"))
{
ImGui::Columns(2, "sourcecolumn", false); // 4-ways, with border
ImGui::SetColumnWidth(0, width_column0);
ImGui::PushTextWrapPos(width_window );
ImGuiToolkit::Icon(ICON_SOURCE_VIDEO); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Video"); ImGui::NextColumn();
ImGui::Text ("Video file (*.mpg, *mov, *.avi, etc.). Decoding can be optimized with hardware acceleration.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_IMAGE); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Image"); ImGui::NextColumn();
ImGui::Text ("Image file (*.jpg, *.png, etc.) or vector graphics (*.svg).");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_SESSION); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Session"); ImGui::NextColumn();
ImGui::Text ("Render a session (*.mix) as a source. Recursion is limited.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_SEQUENCE); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Sequence"); ImGui::NextColumn();
ImGui::Text ("Set of images numbered sequentially (*.jpg, *.png, etc.).");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_RENDER); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Loopback"); ImGui::NextColumn();
ImGui::Text ("Loopback the rendering output as a source, with or without recursion.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_DEVICE_SCREEN); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Screen"); ImGui::NextColumn();
ImGui::Text ("Screen capture of the entire screen or a selected window.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_DEVICE); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Device"); ImGui::NextColumn();
ImGui::Text ("Connected webcam or frame grabber. Highest resolution and framerate automatically selected.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_NETWORK); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Shared"); ImGui::NextColumn();
ImGui::Text ("Connected stream from another vimix in the local network (peer-to-peer).");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_SRT); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("SRT"); ImGui::NextColumn();
ImGui::Text ("Connected Secure Reliable Transport (SRT) stream emitted on the network (e.g. broadcasted by vimix).");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_PATTERN); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Pattern"); ImGui::NextColumn();
ImGui::Text ("Algorithmically generated source; colors, grids, test patterns, timers...");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_GSTREAMER); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("GStreamer"); ImGui::NextColumn();
ImGui::Text ("Custom gstreamer pipeline, as described in command line for gst-launch-1.0 (without the target sink).");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_CLONE); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Clone"); ImGui::NextColumn();
ImGui::Text ("Clones the frames of a source into another one and applies a GPU filter.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_SOURCE_GROUP); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Bundle"); ImGui::NextColumn();
ImGui::Text ("Bundles together several sources and renders them as an internal session.");
ImGui::Columns(1);
ImGui::PopTextWrapPos();
}
if (ImGui::CollapsingHeader("Filters"))
{
ImGui::Text("Select 'Clone & Filter' on a source to access filters;");
ImGui::Columns(2, "filterscolumn", false); // 4-ways, with border
ImGui::SetColumnWidth(0, width_column0);
ImGui::PushTextWrapPos(width_window );
ImGuiToolkit::Icon(ICON_FILTER_DELAY); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Delay"); ImGui::NextColumn();
ImGui::Text("Postpones the display of the input source by a given delay (between 0.0 and 2.0 seconds).");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_FILTER_RESAMPLE); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Resample"); ImGui::NextColumn();
ImGui::Text ("Displays the input source with a different resolution. Downsampling is producing a smaller resolution (half or quarter). Upsampling is producing a higher resolution (double). GPU filtering is applied to improve scaling quality.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_FILTER_BLUR); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Blur"); ImGui::NextColumn();
ImGui::Text ("Applies a real-time GPU bluring filter. Radius of the filter (when available) is a fraction of the image height. ");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_FILTER_SHARPEN); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Sharpen"); ImGui::NextColumn();
ImGui::Text ("Applies a real-time GPU sharpening filter.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_FILTER_SMOOTH); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Smooth"); ImGui::NextColumn();
ImGui::Text ("Applies a real-time GPU smoothing filters to reduce noise. Inverse filters to add noise or grain are also available.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_FILTER_EDGE); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Edge"); ImGui::NextColumn();
ImGui::Text ("Applies a real-time GPU filter to outline edges.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_FILTER_ALPHA); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Alpha"); ImGui::NextColumn();
ImGui::Text ("Applies a real-time GPU chroma-key (green screen) or luma-key (black screen). Inverse filter fills transparent alpha with an opaque color.");
ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_FILTER_IMAGE); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Custom"); ImGui::NextColumn();
ImGui::Text ("Applies a real-time GPU fragment shader defined by custom code in OpenGL Shading Language (GLSL). ");
ImGuiToolkit::ButtonOpenUrl("About GLSL", "https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGuiToolkit::ButtonOpenUrl("Browse shadertoy.com", "https://www.shadertoy.com", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::Columns(1);
ImGui::PopTextWrapPos();
}
if (ImGui::CollapsingHeader("Input Mapping"))
{
ImGui::Columns(2, "inputcolumn", false); // 4-ways, with border
ImGui::SetColumnWidth(0, width_column0);
ImGui::PushTextWrapPos(width_window );
ImGui::Text(ICON_FA_KEYBOARD " Keyboard"); ImGui::NextColumn();
ImGui::Text ("React to key press on standard keyboard, covering 25 keys from [A] to [Y], without modifier.");
ImGui::NextColumn();
ImGui::Text(ICON_FA_CALCULATOR " Numpad"); ImGui::NextColumn();
ImGui::Text ("React to key press on numerical keypad, covering 15 keys from [0] to [9] and including [ . ], [ + ], [ - ], [ * ], [ / ], without modifier.");
ImGui::NextColumn();
ImGui::Text(ICON_FA_TABLET_ALT " TouchOSC"); ImGui::NextColumn();
ImGui::Text ("React to OSC events sent in a local betwork by TouchOSC.");
ImGuiToolkit::ButtonOpenUrl("Install TouchOSC", "https://github.com/brunoherbelin/vimix/wiki/TouchOSC-companion", ImVec2(ImGui::GetContentRegionAvail().x, 0));
ImGui::NextColumn();
ImGui::Text(ICON_FA_GAMEPAD " Gamepad"); ImGui::NextColumn();
ImGui::Text ("React to button press and axis movement on a gamepad or a joystick. Only the first plugged device is considered.");
ImGui::Columns(1);
ImGui::PopTextWrapPos();
}
if (ImGui::CollapsingHeader("Keyboard shortcuts"))
{
ImGui::Columns(2, "keyscolumns", false); // 4-ways, with border
ImGui::SetColumnWidth(0, width_column0);
ImGui::Text("HOME"); ImGui::NextColumn();
ImGui::Text(ICON_FA_BARS " Toggle left panel"); ImGui::NextColumn();
ImGui::Text("INS"); ImGui::NextColumn();
ImGui::Text(ICON_FA_PLUS " New source"); ImGui::NextColumn();
ImGui::Text("DEL"); ImGui::NextColumn();
ImGui::Text(ICON_FA_BACKSPACE " Delete source"); ImGui::NextColumn();
ImGui::Text("TAB"); ImGui::NextColumn();
ImGui::Text(ICON_FA_EXCHANGE_ALT " Switch Current source"); ImGui::NextColumn();
ImGui::Text("[ 0 ][ i ]..[ 9 ]"); ImGui::NextColumn();
ImGui::Text(ICON_FA_HASHTAG " Switch to source at index i"); ImGui::NextColumn();
ImGui::Text(ALT_MOD); ImGui::NextColumn();
ImGui::Text(ICON_FA_MOUSE_POINTER " Activate Snap mouse cursor"); ImGui::NextColumn();
ImGui::Text("F1"); ImGui::NextColumn();
ImGui::Text(ICON_FA_BULLSEYE " Mixing view"); ImGui::NextColumn();
ImGui::Text("F2"); ImGui::NextColumn();
ImGui::Text(ICON_FA_OBJECT_UNGROUP " Geometry view"); ImGui::NextColumn();
ImGui::Text("F3"); ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_WORKSPACE); ImGui::SameLine(0, IMGUI_SAME_LINE); ImGui::Text("Layers view"); ImGui::NextColumn();
ImGui::Text("F4"); ImGui::NextColumn();
ImGui::Text(ICON_FA_CHESS_BOARD " Texturing view"); ImGui::NextColumn();
ImGui::Text("F5"); ImGui::NextColumn();
ImGui::Text(ICON_FA_TV " Displays view"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_PREVIEW_OUT); ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_PREVIEW); ImGui::SameLine(0, IMGUI_SAME_LINE); ImGui::Text("Preview Output"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_PREVIEW_SRC); ImGui::NextColumn();
ImGuiToolkit::Icon(ICON_PREVIEW); ImGui::SameLine(0, IMGUI_SAME_LINE); ImGui::Text("Preview Source"); ImGui::NextColumn();
ImGui::NextColumn();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC); ImGui::Text("Press & hold for momentary on/off"); ImGui::PopFont();
ImGui::NextColumn();
ImGui::Text(CTRL_MOD "TAB"); ImGui::NextColumn();
ImGui::Text("Switch view"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_FULLSCREEN); ImGui::NextColumn();
ImGui::Text(ICON_FA_EXPAND_ALT " " TOOLTIP_FULLSCREEN " window"); ImGui::NextColumn();
ImGui::Separator();
ImGui::Text(SHORTCUT_OUTPUT); ImGui::NextColumn();
ImGui::Text(ICON_FA_DESKTOP " " TOOLTIP_OUTPUT "window"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_PLAYER); ImGui::NextColumn();
ImGui::Text(ICON_FA_PLAY_CIRCLE " " TOOLTIP_PLAYER "window" ); ImGui::NextColumn();
ImGui::Text(SHORTCUT_TIMER); ImGui::NextColumn();
ImGui::Text(ICON_FA_CLOCK " " TOOLTIP_TIMER "window"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_INPUTS); ImGui::NextColumn();
ImGui::Text(ICON_FA_HAND_PAPER " " TOOLTIP_INPUTS "window"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_SHADEREDITOR); ImGui::NextColumn();
ImGui::Text(ICON_FA_CODE " " TOOLTIP_SHADEREDITOR "window"); ImGui::NextColumn();
ImGui::Text("ESC"); ImGui::NextColumn();
ImGui::Text(" Hide | Show all windows"); ImGui::NextColumn();
ImGui::NextColumn();
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC); ImGui::Text("Press & hold for momentary on/off"); ImGui::PopFont();
ImGui::NextColumn();
ImGui::Separator();
ImGui::Text(SHORTCUT_NEW_FILE); ImGui::NextColumn();
ImGui::Text(MENU_NEW_FILE " session"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_OPEN_FILE); ImGui::NextColumn();
ImGui::Text(MENU_OPEN_FILE " session"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_REOPEN_FILE); ImGui::NextColumn();
ImGui::Text(MENU_REOPEN_FILE " session"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_SAVE_FILE); ImGui::NextColumn();
ImGui::Text(MENU_SAVE_FILE " session"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_SAVEAS_FILE); ImGui::NextColumn();
ImGui::Text(MENU_SAVEAS_FILE " session"); ImGui::NextColumn();
ImGui::Separator();
ImGui::Text(SHORTCUT_UNDO); ImGui::NextColumn();
ImGui::Text(MENU_UNDO); ImGui::NextColumn();
ImGui::Text(SHORTCUT_REDO); ImGui::NextColumn();
ImGui::Text(MENU_REDO); ImGui::NextColumn();
ImGui::Text(SHORTCUT_CUT); ImGui::NextColumn();
ImGui::Text(MENU_CUT " source"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_COPY); ImGui::NextColumn();
ImGui::Text(MENU_COPY " source"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_PASTE); ImGui::NextColumn();
ImGui::Text(MENU_PASTE); ImGui::NextColumn();
ImGui::Text(SHORTCUT_SELECTALL); ImGui::NextColumn();
ImGui::Text(MENU_SELECTALL " sources"); ImGui::NextColumn();
ImGui::Separator();
ImGui::Text(SHORTCUT_CAPTURE_DISPLAY); ImGui::NextColumn();
ImGui::Text(MENU_CAPTUREFRAME " display"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_OUTPUTDISABLE); ImGui::NextColumn();
ImGui::Text(MENU_OUTPUTDISABLE " display output"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_RECORD); ImGui::NextColumn();
ImGui::Text(MENU_RECORD " Output"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_RECORDCONT); ImGui::NextColumn();
ImGui::Text(MENU_RECORDCONT " recording"); ImGui::NextColumn();
ImGui::Separator();
ImGui::Text(SHORTCUT_CAPTURE_PLAYER); ImGui::NextColumn();
ImGui::Text(MENU_CAPTUREFRAME " Player"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_PLAY_PAUSE); ImGui::NextColumn();
ImGui::Text(MENU_PLAY_PAUSE " selected videos"); ImGui::NextColumn();
ImGui::Text(SHORTCUT_PLAY_BEGIN); ImGui::NextColumn();
ImGui::Text(MENU_PLAY_BEGIN " selected videos"); ImGui::NextColumn();
ImGui::Text(ICON_FA_ARROW_DOWN " " ICON_FA_ARROW_UP " " ICON_FA_ARROW_DOWN " " ICON_FA_ARROW_RIGHT ); ImGui::NextColumn();
ImGui::Text("Move the selection in the canvas"); ImGui::NextColumn();
ImGui::Separator();
ImGui::Text(SHORTCUT_CAPTURE_GUI); ImGui::NextColumn();
ImGui::Text(MENU_CAPTUREGUI); ImGui::NextColumn();
ImGui::Text(SHORTCUT_LOGS); ImGui::NextColumn();
ImGui::Text(IMGUI_TITLE_LOGS); ImGui::NextColumn();
ImGui::Text(SHORTCUT_HELP); ImGui::NextColumn();
ImGui::Text(IMGUI_TITLE_HELP ); ImGui::NextColumn();
ImGui::Text(SHORTCUT_QUIT); ImGui::NextColumn();
ImGui::Text(MENU_QUIT); ImGui::NextColumn();
ImGui::Columns(1);
}
ImGui::End();
}
///
/// NAVIGATOR
///
///
std::vector< std::pair<int, int> > Navigator::icons_ordering_files = { {2,12}, {3,12}, {4,12}, {5,12} };
std::vector< std::string > Navigator::tooltips_ordering_files = { "Alphabetical", "Invert alphabetical", "Older files first", "Recent files first" };
Navigator::Navigator()
{
// default geometry
width_ = 100.f;
pannel_width_ = 5.f * width_;
height_ = 100.f;
padding_width_ = 100.f;
// clean start
pannel_main_mode_ = Settings::application.pannel_main_mode;
pannel_visible_ = false;
pannel_alpha_ = 0.85f;
view_pannel_visible = false;
clearButtonSelection();
// restore media mode as saved
if (Settings::application.recentImportFolders.path.empty() ||
Settings::application.recentImportFolders.path.compare(IMGUI_LABEL_RECENT_FILES) == 0)
setNewMedia(MEDIA_RECENT);
else if (Settings::application.recentImportFolders.path.compare(IMGUI_LABEL_RECENT_RECORDS) == 0)
setNewMedia(MEDIA_RECORDING);
else
setNewMedia(MEDIA_FOLDER, Settings::application.recentImportFolders.path);
source_to_replace = nullptr;
}
void Navigator::applyButtonSelection(int index)
{
// ensure only one button is active at a time
bool status = selected_button[index];
clearButtonSelection();
selected_button[index] = status;
selected_index = index;
// set visible if button is active
pannel_visible_ = status;
pannel_main_mode_ = Settings::application.pannel_main_mode;
}
void Navigator::clearNewPannel()
{
new_source_preview_.setSource();
pattern_type = -1;
generated_type = -1;
custom_connected = false;
custom_screencapture = false;
sourceSequenceFiles.clear();
sourceMediaFileCurrent.clear();
new_media_mode_changed = true;
}
void Navigator::clearButtonSelection()
{
// clear all buttons
for(int i=0; i<NAV_COUNT; ++i)
selected_button[i] = false;
// clear new source pannel
clearNewPannel();
source_to_replace = nullptr;
selected_index = -1;
}
void Navigator::showPannelSource(int index)
{
selected_index = index;
// invalid index given
if ( index < 0 )
discardPannel();
else {
selected_button[index] = true;
applyButtonSelection(index);
}
}
int Navigator::selectedPannelSource()
{
return selected_index;
}
void Navigator::showConfig()
{
selected_button[NAV_MENU] = true;
applyButtonSelection(NAV_MENU);
pannel_main_mode_ = 2;
}
void Navigator::togglePannelMenu()
{
selected_button[NAV_MENU] = !selected_button[NAV_MENU];
applyButtonSelection(NAV_MENU);
if (Settings::application.pannel_always_visible)
showPannelSource(NAV_MENU);
}
void Navigator::togglePannelNew()
{
selected_button[NAV_NEW] = !selected_button[NAV_NEW];
applyButtonSelection(NAV_NEW);
new_media_mode_changed = true;
if (Settings::application.pannel_always_visible)
showPannelSource( Mixer::manager().numSource() );
}
void Navigator::togglePannelAutoHide()
{
// toggle variable
Settings::application.pannel_always_visible = !Settings::application.pannel_always_visible;
// initiate change
if (Settings::application.pannel_always_visible) {
int current = Mixer::manager().indexCurrentSource();
if ( current < 0 ) {
if (!selected_button[NAV_MENU] && !selected_button[NAV_TRANS] && !selected_button[NAV_NEW] )
showPannelSource(NAV_MENU);
}
else
showPannelSource( current );
}
else {
pannel_visible_ = true;
discardPannel();
}
}
bool Navigator::pannelVisible()
{
return pannel_visible_ || Settings::application.pannel_always_visible;
}
void Navigator::discardPannel()
{
// in the 'always visible mode',
// discard the panel means cancel current action
if ( Settings::application.pannel_always_visible ) {
// if panel is the 'Insert' new source
if ( selected_button[NAV_NEW] ) {
// cancel the current source creation
clearNewPannel();
}
// if panel is the 'Transition' session
else if ( selected_button[NAV_TRANS] ) {
// allows to hide pannel
clearButtonSelection();
}
// if panel shows a source (i.e. not NEW, TRANS nor MENU selected)
else if ( !selected_button[NAV_MENU] )
{
// revert to menu panel
togglePannelMenu();
/// ALTERNATIVE : stay on source panel
// // get index of current source
// int idx = Mixer::manager().indexCurrentSource();
// if (idx < 0) {
// // no current source, try to get source previously in panel
// Source *cs = Mixer::manager().sourceAtIndex( selected_index );
// if ( cs )
// idx = selected_index;
// else {
// // really no source is current, try to get one from user selection
// cs = Mixer::selection().front();
// if ( cs )
// idx = Mixer::manager().session()->index( Mixer::manager().session()->find(cs) );
// }
// }
// // if current source or a selected source, show it's pannel
// if (idx >= 0)
// showPannelSource( idx );
}
}
// in the general mode,
// discard means hide pannel
else if ( pannel_visible_)
clearButtonSelection();
pannel_visible_ = false;
view_pannel_visible = false;
pannel_main_mode_ = Settings::application.pannel_main_mode;
}
void Navigator::Render()
{
std::tuple<std::string, std::string, Source *> tooltip = {"", "", nullptr};
static uint _timeout_tooltip = 0;
const ImGuiStyle& style = ImGui::GetStyle();
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(COLOR_NAVIGATOR, 1.f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(COLOR_NAVIGATOR, 1.f));
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.50f, 0.50f));
// calculate size of items based on text size and display dimensions
width_ = 2.f * ImGui::GetTextLineHeightWithSpacing(); // dimension of left bar depends on FONT_LARGE
pannel_width_ = 5.f * width_; // pannel is 5x the bar
padding_width_ = 2.f * style.WindowPadding.x; // panning for alighment
height_ = ImGui::GetIO().DisplaySize.y; // cover vertically
const float icon_width = width_ - 2.f * style.WindowPadding.x; // icons keep padding
const ImVec2 iconsize(icon_width, icon_width);
const float sourcelist_height = height_ - 6.5f * icon_width - 6.f * style.WindowPadding.y; // space for 4 icons of view
// hack to show more sources if not enough space; make source icons smaller...
ImVec2 sourceiconsize(icon_width, icon_width);
if (sourcelist_height - 2.f * icon_width < Mixer::manager().session()->size() * icon_width )
sourceiconsize.y *= 0.75f;
// Left bar top
ImGui::SetNextWindowPos( ImVec2(0, 0), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2(width_, sourcelist_height), ImGuiCond_Always );
ImGui::SetNextWindowBgAlpha(0.95f); // Transparent background
if (ImGui::Begin( ICON_FA_BARS " Navigator", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing))
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (Settings::application.current_view != View::TRANSITION) {
// the vimix icon for menu
if (ImGuiToolkit::SelectableIcon(2, 16, "", selected_button[NAV_MENU], iconsize)) {
selected_button[NAV_MENU] = true;
applyButtonSelection(NAV_MENU);
}
if (ImGui::IsItemHovered())
tooltip = {TOOLTIP_MAIN, SHORTCUT_MAIN, nullptr};
// the "+" icon for action of creating new source
if (ImGui::Selectable( source_to_replace != nullptr ? ICON_FA_PLUS_SQUARE : ICON_FA_PLUS,
&selected_button[NAV_NEW], 0, iconsize)) {
Mixer::manager().unsetCurrentSource();
applyButtonSelection(NAV_NEW);
}
if (ImGui::IsItemHovered())
tooltip = {TOOLTIP_NEW_SOURCE, SHORTCUT_NEW_SOURCE, nullptr};
//
// the list of INITIALS for sources
//
int index = 0;
SourceList::iterator iter;
for (iter = Mixer::manager().session()->begin(); iter != Mixer::manager().session()->end(); ++iter, ++index)
{
Source *s = (*iter);
// Show failed sources in RED
bool pushed = false;
if (s->failed()){
pushed = true;
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_FAILED, 1.));
ImGui::PushStyleColor(ImGuiCol_Header, ImGui::GetColorU32(ImGuiCol_Button));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiCol_ButtonActive));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiCol_ButtonHovered));
}
// draw an indicator for selected sources (a dot) and for the current source (a line)
if (s->mode() > Source::VISIBLE) {
// source is SELECTED or CURRENT
const ImVec2 p1 = ImGui::GetCursorScreenPos() +
ImVec2(icon_width, (s->mode() > Source::SELECTED ? 0.f : 0.5f * sourceiconsize.y - 2.5f) );
const ImVec2 p2 = ImVec2(p1.x, p1.y + (s->mode() > Source::SELECTED ? sourceiconsize.y : 5.f) );
const ImU32 color = ImGui::GetColorU32(ImGuiCol_Text);
draw_list->AddLine(p1, p2, color, 5.f);
}
// draw select box
ImGui::PushID(std::to_string(s->group(View::RENDERING)->id()).c_str());
if (ImGui::Selectable(s->initials(), &selected_button[index], 0, sourceiconsize))
{
applyButtonSelection(index);
if (selected_button[index])
Mixer::manager().setCurrentIndex(index);
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
std::string label = s->name().size() < 16 ? s->name()
: s->name().substr(0, 15) + "..";
tooltip = { label, "#" + std::to_string(index), s };
}
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
{
ImGui::SetDragDropPayload("DND_SOURCE", &index, sizeof(int));
ImGui::Text( ICON_FA_SORT " %s ", s->initials());
ImGui::EndDragDropSource();
}
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_SOURCE"))
{
if ( payload->DataSize == sizeof(int) ) {
bool status_current_index = selected_button[Mixer::manager().indexCurrentSource()];
// drop means move index and reorder
int payload_index = *(const int*)payload->Data;
Mixer::manager().moveIndex(payload_index, index);
// index of current source changed
selected_button[Mixer::manager().indexCurrentSource()] = status_current_index;
applyButtonSelection(Mixer::manager().indexCurrentSource());
}
}
ImGui::EndDragDropTarget();
}
if (pushed)
ImGui::PopStyleColor(4);
ImGui::PopID();
}
}
else {
// the ">" icon for transition menu
if (ImGui::Selectable( ICON_FA_ARROW_CIRCLE_RIGHT, &selected_button[NAV_TRANS], 0, iconsize))
{
Mixer::manager().unsetCurrentSource();
applyButtonSelection(NAV_TRANS);
}
}
ImGui::End();
}
// Left bar bottom
ImGui::SetNextWindowPos( ImVec2(0, sourcelist_height), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2(width_, height_ - sourcelist_height + 1.f), ImGuiCond_Always );
ImGui::SetNextWindowBgAlpha(0.95f); // Transparent background
if (ImGui::Begin("##navigatorViews", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoScrollWithMouse))
{
// Mouse pointer selector
if ( RenderMousePointerSelector(iconsize) )
tooltip = {TOOLTIP_SNAP_CURSOR, ALT_MOD, nullptr};
// List of icons for View selection
static uint view_options_timeout = 0;
static ImVec2 view_options_pos = ImGui::GetCursorScreenPos();
bool selected_view[View::INVALID] = {0};
selected_view[ Settings::application.current_view ] = true;
int previous_view = Settings::application.current_view;
if (ImGui::Selectable( ICON_FA_BULLSEYE, &selected_view[View::MIXING], 0, iconsize))
{
UserInterface::manager().setView(View::MIXING);
if (previous_view == Settings::application.current_view) {
ImGui::OpenPopup( "PopupViewOptions" );
view_options_pos = ImGui::GetCursorScreenPos();
}
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
tooltip = {Settings::application.views[View::MIXING].name, "F1", nullptr};
view_options_timeout = 0;
}
if (ImGui::Selectable( ICON_FA_OBJECT_UNGROUP , &selected_view[View::GEOMETRY], 0, iconsize))
{
UserInterface::manager().setView(View::GEOMETRY);
if (previous_view == Settings::application.current_view) {
ImGui::OpenPopup( "PopupViewOptions" );
view_options_pos = ImGui::GetCursorScreenPos();
}
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
tooltip = {Settings::application.views[View::GEOMETRY].name, "F2", nullptr};
view_options_timeout = 0;
}
if (ImGuiToolkit::SelectableIcon(ICON_WORKSPACE, "", selected_view[View::LAYER], iconsize))
{
Settings::application.current_view = View::LAYER;
UserInterface::manager().setView(View::LAYER);
if (previous_view == Settings::application.current_view) {
ImGui::OpenPopup( "PopupViewOptions" );
view_options_pos = ImGui::GetCursorScreenPos();
}
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
tooltip = {Settings::application.views[View::LAYER].name, "F3", nullptr};
view_options_timeout = 0;
}
if (ImGui::Selectable( ICON_FA_CHESS_BOARD, &selected_view[View::TEXTURE], 0, iconsize))
{
UserInterface::manager().setView(View::TEXTURE);
if (previous_view == Settings::application.current_view) {
ImGui::OpenPopup( "PopupViewOptions" );
view_options_pos = ImGui::GetCursorScreenPos();
}
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
tooltip = {Settings::application.views[View::TEXTURE].name, "F4", nullptr};
view_options_timeout = 0;
}
int j = Settings::application.render.disabled ? 8 : 7;
if (ImGuiToolkit::SelectableIcon(10, j, "", selected_view[View::DISPLAYS], iconsize))
{
UserInterface::manager().setView(View::DISPLAYS);
Settings::application.current_view = View::DISPLAYS;
if (previous_view == Settings::application.current_view) {
ImGui::OpenPopup( "PopupViewOptions" );
view_options_pos = ImGui::GetCursorScreenPos();
}
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
tooltip = {Settings::application.views[View::DISPLAYS].name, "F5", nullptr};
view_options_timeout = 0;
}
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPos(pos + ImVec2(0.f, style.WindowPadding.y));
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
// icon for Fullscreen
if ( ImGuiToolkit::IconButton( Rendering::manager().mainWindow().isFullscreen() ? ICON_FA_COMPRESS_ALT : ICON_FA_EXPAND_ALT ) )
Rendering::manager().mainWindow().toggleFullscreen();
if (ImGui::IsItemHovered())
tooltip = {TOOLTIP_FULLSCREEN, SHORTCUT_FULLSCREEN, nullptr};
// icon for toggle always visible / auto hide pannel
ImGui::SetCursorPos(pos + ImVec2(width_ * 0.5f, style.WindowPadding.y));
if ( ImGuiToolkit::IconButton( Settings::application.pannel_always_visible ? ICON_FA_TOGGLE_ON : ICON_FA_TOGGLE_OFF ) )
togglePannelAutoHide();
if (ImGui::IsItemHovered())
tooltip = { Settings::application.pannel_always_visible ? TOOLTIP_PANEL_VISIBLE : TOOLTIP_PANEL_AUTO, SHORTCUT_PANEL_MODE, nullptr };
ImGui::PopFont();
// render the "PopupViewOptions"
RenderViewOptions(&view_options_timeout, view_options_pos, iconsize);
ImGui::End();
}
// show tooltip
if (!std::get<0>(tooltip).empty()) {
// pseudo timeout for showing tooltip
if (_timeout_tooltip > IMGUI_TOOLTIP_TIMEOUT) {
// if a pointer to a Source is provided in tupple
Source *_s = std::get<2>(tooltip);
if (_s != nullptr) {
ImGui::BeginTooltip();
const ImVec2 image_top = ImGui::GetCursorPos();
const ImVec2 thumbnail_size = ImVec2(width_, width_ / _s->frame()->aspectRatio()) * 3.f;
// Render source frame in tooltip
ImGui::Image((void *) (uintptr_t) _s->frame()->texture(), thumbnail_size);
// Draw label and shortcut from tupple
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
ImGui::TextUnformatted(std::get<0>(tooltip).c_str());
ImGui::SameLine();
ImGui::SetCursorPosX(thumbnail_size.x + style.WindowPadding.x
- ImGui::GetTextLineHeight() );
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6, 0.6, 0.6, 0.9f));
ImGui::TextUnformatted(std::get<1>(tooltip).c_str());
ImGui::PopStyleColor();
// Draw source icon at the top right corner
ImGui::SetCursorPos(image_top + ImVec2( thumbnail_size.x - ImGui::GetTextLineHeight()
- style.ItemSpacing.x, style.ItemSpacing.y ));
ImGuiToolkit::Icon( _s->icon().x, _s->icon().y);
ImGui::PopFont();
ImGui::EndTooltip();
}
// otherwise just show a standard tooltip [action - shortcut key]
else
ImGuiToolkit::ToolTip(std::get<0>(tooltip).c_str(), std::get<1>(tooltip).c_str());
}
else
++_timeout_tooltip;
}
else
_timeout_tooltip = 0;
ImGui::PopStyleVar();
ImGui::PopFont();
// Rendering of the side pannel
if ( Settings::application.pannel_always_visible || pannel_visible_ ){
// slight differences if temporari vixible or always visible panel
if (Settings::application.pannel_always_visible)
pannel_alpha_ = 0.95f;
else {
pannel_alpha_ = 0.85f;
view_pannel_visible = false;
}
// pannel menu
if (selected_button[NAV_MENU])
{
RenderMainPannel(iconsize);
}
// pannel to manage transition
else if (selected_button[NAV_TRANS])
{
RenderTransitionPannel(iconsize);
}
// pannel to create a source
else if (selected_button[NAV_NEW])
{
RenderNewPannel(iconsize);
}
// pannel to configure a selected source
else
{
if ( selected_index < 0 )
showPannelSource(NAV_MENU);
// most often, render current sources
else if ( selected_index == Mixer::manager().indexCurrentSource())
RenderSourcePannel(Mixer::manager().currentSource(), iconsize);
// rarely its not the current source that is selected
else {
SourceList::iterator cs = Mixer::manager().session()->at( selected_index );
if (cs != Mixer::manager().session()->end() )
RenderSourcePannel(*cs, iconsize);
}
}
}
ImGui::PopStyleColor(2);
ImGui::PopStyleVar();
}
void Navigator::RenderViewOptions(uint *timeout, const ImVec2 &pos, const ImVec2 &size)
{
ImGuiContext& g = *GImGui;
ImGui::SetNextWindowPos( pos + ImVec2(size.x + g.Style.WindowPadding.x, -size.y), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2(size.x * 7.f, size.y), ImGuiCond_Always );
if (ImGui::BeginPopup( "PopupViewOptions" ))
{
// vertical padding
ImGui::SetCursorPosY( ImGui::GetCursorPosY() + g.Style.WindowPadding.y * 0.5f );
// reset zoom
if (ImGuiToolkit::IconButton(8,7)) {
Mixer::manager().view((View::Mode)Settings::application.current_view)->recenter();
}
// percent zoom slider
int percent_zoom = Mixer::manager().view((View::Mode)Settings::application.current_view)->size();
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(-1.f);
if (ImGui::SliderInt("##zoom", &percent_zoom, 0, 100, "%d %%" )) {
Mixer::manager().view((View::Mode)Settings::application.current_view)->resize(percent_zoom);
}
// timer to close popup like a tooltip
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
*timeout=0;
else if ( (*timeout)++ > 10)
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
}
// Source pannel : *s was checked before
void Navigator::RenderSourcePannel(Source *s, const ImVec2 &iconsize)
{
if (s == nullptr || Settings::application.current_view == View::TRANSITION)
return;
// Next window is a side pannel
const ImGuiStyle& style = ImGui::GetStyle();
ImGui::SetNextWindowPos( ImVec2(width_, 0), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2(pannel_width_, height_), ImGuiCond_Always );
ImGui::SetNextWindowBgAlpha( pannel_alpha_ ); // Transparent background
if (ImGui::Begin("##navigatorSource", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
// TITLE
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetCursorPosY(0.5f * (iconsize.y - ImGui::GetTextLineHeight()));
ImGui::Text("Source");
// index indicator
ImGui::SetCursorPos(ImVec2(pannel_width_ - 2 * ImGui::GetTextLineHeight(), IMGUI_TOP_ALIGN));
ImGui::TextDisabled("#%d", Mixer::manager().indexCurrentSource());
ImGui::PopFont();
// name
std::string sname = s->name();
ImGui::SetCursorPosY(width_ - style.WindowPadding.x);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGuiToolkit::InputText("Name", &sname) ){
Mixer::manager().renameSource(s, sname);
}
// Source pannel
static ImGuiVisitor v;
s->accept(v);
ImGui::Text(" ");
// clone button
if ( s->failed() ) {
ImGuiToolkit::ButtonDisabled( ICON_FA_SHARE_SQUARE " Clone & Filter", ImVec2(ImGui::GetContentRegionAvail().x, 0));
}
else if ( ImGui::Button( ICON_FA_SHARE_SQUARE " Clone & Filter", ImVec2(ImGui::GetContentRegionAvail().x, 0)) )
Mixer::manager().addSource ( Mixer::manager().createSourceClone() );
// replace button
if ( s->cloned() ) {
ImGuiToolkit::ButtonDisabled( ICON_FA_PLUS_SQUARE " Replace", ImVec2(ImGui::GetContentRegionAvail().x, 0));
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Cannot replace if source is cloned");
}
else if ( ImGui::Button( ICON_FA_PLUS_SQUARE " Replace", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) {
// prepare panel for new source of same type
MediaSource *file = dynamic_cast<MediaSource *>(s);
MultiFileSource *sequence = dynamic_cast<MultiFileSource *>(s);
PatternSource *generated = dynamic_cast<PatternSource *>(s);
if (file != nullptr)
Settings::application.source.new_type = SOURCE_FILE;
else if (sequence != nullptr)
Settings::application.source.new_type = SOURCE_SEQUENCE;
else if (generated != nullptr)
Settings::application.source.new_type = SOURCE_GENERATED;
else
Settings::application.source.new_type = SOURCE_CONNECTED;
// switch to panel new source
showPannelSource(NAV_NEW);
// set source to be replaced
source_to_replace = s;
}
// delete button
if ( ImGui::Button( ACTION_DELETE, ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) {
Mixer::manager().deleteSource(s);
Action::manager().store(sname + std::string(": deleted"));
}
if ( Mixer::manager().session()->failedSources().size() > 1 ) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_FAILED, 1.));
if ( ImGui::Button( ICON_FA_BACKSPACE " Delete all failed", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) {
auto failedsources = Mixer::manager().session()->failedSources();
for (auto sit = failedsources.cbegin(); sit != failedsources.cend(); ++sit) {
Mixer::manager().deleteSource( Mixer::manager().findSource( (*sit)->id() ) );
}
}
ImGui::PopStyleColor(1);
}
ImGui::End();
}
}
void Navigator::setNewMedia(MediaCreateMode mode, std::string path)
{
Settings::application.source.new_type = Navigator::SOURCE_FILE;
// change mode
new_media_mode = mode;
new_media_mode_changed = true;
// mode dependent actions
switch (new_media_mode) {
case MEDIA_RECENT:
// set filename
sourceMediaFileCurrent = path;
// set combo to 'recent files'
Settings::application.recentImportFolders.path = IMGUI_LABEL_RECENT_FILES;
break;
case MEDIA_RECORDING:
// set filename
sourceMediaFileCurrent = path;
// set combo to 'recent recordings'
Settings::application.recentImportFolders.path = IMGUI_LABEL_RECENT_RECORDS;
break;
default:
case MEDIA_FOLDER:
// reset filename
sourceMediaFileCurrent.clear();
// set combo: a path was selected
if (!path.empty())
Settings::application.recentImportFolders.path.assign(path);
break;
}
// clear preview
new_source_preview_.setSource();
}
void Navigator::RenderNewPannel(const ImVec2 &iconsize)
{
if (Settings::application.current_view == View::TRANSITION)
return;
const ImGuiStyle& style = ImGui::GetStyle();
// Next window is a side pannel
ImGui::SetNextWindowPos( ImVec2(width_, 0), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2(pannel_width_, height_), ImGuiCond_Always );
ImGui::SetNextWindowBgAlpha( pannel_alpha_ ); // Transparent background
if (ImGui::Begin("##navigatorNewSource", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
// TITLE
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetCursorPosY(0.5f * (iconsize.y - ImGui::GetTextLineHeight()));
if (source_to_replace != nullptr)
ImGui::Text("Replace");
else
ImGui::Text("Insert");
//
// News Source selection pannel
//
ImGui::SetCursorPosY(width_ - style.WindowPadding.x);
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.5f));
ImGui::Columns(5, NULL, false);
bool selected_type[5] = {0};
selected_type[Settings::application.source.new_type] = true;
if (ImGuiToolkit::SelectableIcon( 2, 5, "##SOURCE_FILE", selected_type[SOURCE_FILE], iconsize)) {
Settings::application.source.new_type = SOURCE_FILE;
clearNewPannel();
}
ImGui::NextColumn();
if (ImGuiToolkit::SelectableIcon( ICON_SOURCE_SEQUENCE, "##SOURCE_SEQUENCE", selected_type[SOURCE_SEQUENCE], iconsize)) {
Settings::application.source.new_type = SOURCE_SEQUENCE;
clearNewPannel();
}
ImGui::NextColumn();
if (ImGuiToolkit::SelectableIcon( 10, 9, "##SOURCE_CONNECTED", selected_type[SOURCE_CONNECTED], iconsize)) {
Settings::application.source.new_type = SOURCE_CONNECTED;
clearNewPannel();
}
ImGui::NextColumn();
if (ImGuiToolkit::SelectableIcon( ICON_SOURCE_PATTERN, "##SOURCE_GENERATED", selected_type[SOURCE_GENERATED], iconsize)) {
Settings::application.source.new_type = SOURCE_GENERATED;
clearNewPannel();
}
ImGui::NextColumn();
ImGui::Columns(1);
ImGui::PopStyleVar();
ImGui::PopFont();
// Edit menu
ImGui::SetCursorPosY(2.f * width_ - style.WindowPadding.x);
// File Source creation
if (Settings::application.source.new_type == SOURCE_FILE) {
static DialogToolkit::OpenFileDialog fileimportdialog("Open Media",
MEDIA_FILES_TYPE,
MEDIA_FILES_PATTERN );
static DialogToolkit::OpenFolderDialog folderimportdialog("Select Folder");
ImGui::Text("Video, image & session files");
// clic button to load file
if ( ImGui::Button( ICON_FA_FOLDER_OPEN " Open", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) )
fileimportdialog.open();
// Indication
ImGui::SameLine();
ImGuiToolkit::HelpToolTip("Create a source from a file:\n"
ICON_FA_CARET_RIGHT " Video (*.mpg, *mov, *.avi, etc.)\n"
ICON_FA_CARET_RIGHT " Image (*.jpg, *.png, etc.)\n"
ICON_FA_CARET_RIGHT " Vector graphics (*.svg)\n"
ICON_FA_CARET_RIGHT " Vimix session (*.mix)\n"
"\nNB: Equivalent to dropping the file in the workspace");
// get media file if dialog finished
if (fileimportdialog.closed()){
// get the filename from this file dialog
std::string importpath = fileimportdialog.path();
// switch to recent files
setNewMedia(MEDIA_RECENT, importpath);
// open file
if (!importpath.empty()) {
// replace or open source
if (source_to_replace != nullptr)
Mixer::manager().replaceSource(source_to_replace, Mixer::manager().createSourceFile(sourceMediaFileCurrent));
else
Mixer::manager().addSource( Mixer::manager().createSourceFile(sourceMediaFileCurrent) );
// close NEW pannel
togglePannelNew();
}
}
// combo to offer lists
ImGui::Spacing();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##SelectionNewMedia", BaseToolkit::truncated(Settings::application.recentImportFolders.path, 25).c_str() ))
{
// Mode MEDIA_RECENT : recent files
if (ImGui::Selectable( ICON_FA_LIST_OL IMGUI_LABEL_RECENT_FILES) ) {
setNewMedia(MEDIA_RECENT);
}
// Mode MEDIA_RECORDING : recent recordings
if (ImGui::Selectable( ICON_FA_LIST IMGUI_LABEL_RECENT_RECORDS) ) {
setNewMedia(MEDIA_RECORDING);
}
// Mode MEDIA_FOLDER : known folders
for(auto foldername = Settings::application.recentImportFolders.filenames.begin();
foldername != Settings::application.recentImportFolders.filenames.end(); foldername++) {
std::string f = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::truncated( *foldername, 40);
if (ImGui::Selectable( f.c_str() )) {
setNewMedia(MEDIA_FOLDER, *foldername);
}
}
// Add a folder for MEDIA_FOLDER
if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " List directory") ) {
folderimportdialog.open();
}
ImGui::EndCombo();
}
// return from thread for folder openning
if (folderimportdialog.closed() && !folderimportdialog.path().empty()) {
Settings::application.recentImportFolders.push(folderimportdialog.path());
setNewMedia(MEDIA_FOLDER, folderimportdialog.path());
}
// position on top of list
ImVec2 pos_top = ImGui::GetCursorPos();
// change session list if changed
if (new_media_mode_changed || Settings::application.recentImport.changed || Settings::application.recentRecordings.changed) {
// MODE RECENT
if ( new_media_mode == MEDIA_RECENT) {
// show list of recent imports
Settings::application.recentImport.validate();
sourceMediaFiles = Settings::application.recentImport.filenames;
// done changed
Settings::application.recentImport.changed = false;
}
// MODE RECORDINGS
else if ( new_media_mode == MEDIA_RECORDING) {
// show list of recent records
Settings::application.recentRecordings.validate();
sourceMediaFiles = Settings::application.recentRecordings.filenames;
// in auto
if (Settings::application.recentRecordings.load_at_start
&& Settings::application.recentRecordings.changed
&& Settings::application.recentRecordings.filenames.size() > 0){
sourceMediaFileCurrent = sourceMediaFiles.front();
std::string label = BaseToolkit::transliterate( sourceMediaFileCurrent );
new_source_preview_.setSource( Mixer::manager().createSourceFile(sourceMediaFileCurrent), label);
}
// done changed
Settings::application.recentRecordings.changed = false;
}
// MODE LIST FOLDER
else if ( new_media_mode == MEDIA_FOLDER) {
// show list of media files in folder
sourceMediaFiles = SystemToolkit::list_directory( Settings::application.recentImportFolders.path, { MEDIA_FILES_PATTERN },
(SystemToolkit::Ordering) Settings::application.recentImportFolders.ordering);
}
// indicate the list changed (do not change at every frame)
new_media_mode_changed = false;
}
// different labels for each mode
static const char *listboxname[3] = { "##NewSourceMediaRecent", "##NewSourceMediaRecording", "##NewSourceMediafolder"};
// display the import-list and detect if one was selected
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::ListBoxHeader(listboxname[new_media_mode], sourceMediaFiles.size(), CLAMP(sourceMediaFiles.size(), 4, 6)) ) {
static int tooltip = 0;
static std::string filenametooltip;
// loop over list of files
for(auto it = sourceMediaFiles.begin(); it != sourceMediaFiles.end(); ++it) {
// build displayed file name
std::string filename = BaseToolkit::transliterate(*it);
std::string label = BaseToolkit::truncated(SystemToolkit::filename(filename), 25);
// add selectable item to ListBox; open if clickec
if (ImGui::Selectable( label.c_str(), sourceMediaFileCurrent.compare(*it) == 0 )) {
// set new source preview
new_source_preview_.setSource( Mixer::manager().createSourceFile(*it), filename);
// remember current list item
sourceMediaFileCurrent = *it;
}
// smart tooltip : displays only after timout when item changed
if (ImGui::IsItemHovered()){
if (filenametooltip.compare(filename)==0){
++tooltip;
if (tooltip>30) {
ImGui::BeginTooltip();
ImGui::Text("%s", filenametooltip.c_str());
ImGui::EndTooltip();
}
}
else {
filenametooltip.assign(filename);
tooltip = 0;
}
}
}
ImGui::ListBoxFooter();
}
// Supplementary icons to manage the list
ImVec2 pos_bot = ImGui::GetCursorPos();
if (new_media_mode == MEDIA_RECORDING) {
// Clear list
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y) );
if (ImGuiToolkit::IconButton( 12, 14, "Clear list")) {
Settings::application.recentRecordings.filenames.clear();
Settings::application.recentRecordings.front_is_valid = false;
setNewMedia(MEDIA_RECORDING);
}
// Bottom Right side of the list: helper and options of Recent Recordings
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
ImGuiToolkit::HelpToolTip("Recently recorded videos (lastest on top). Clic on a filename to open.\n\n"
ICON_FA_CHEVRON_CIRCLE_RIGHT " Auto-preload prepares this panel with the "
"most recent recording after 'Stop Record' or 'Save & continue'.");
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) );
if (ImGuiToolkit::ButtonToggle( ICON_FA_CHEVRON_CIRCLE_RIGHT, &Settings::application.recentRecordings.load_at_start, "Auto-preload" ) ){
// demonstrate action
if (Settings::application.recentRecordings.load_at_start
&& Settings::application.recentRecordings.filenames.size() > 0) {
sourceMediaFileCurrent = sourceMediaFiles.front();
std::string label = BaseToolkit::transliterate( sourceMediaFileCurrent );
new_source_preview_.setSource( Mixer::manager().createSourceFile(sourceMediaFileCurrent), label);
}
}
}
else if (new_media_mode == MEDIA_FOLDER) {
ImGui::PushID("##new_media_directory_actions");
// close list
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y) );
if (ImGuiToolkit::IconButton( 4, 5, "Close directory")) {
Settings::application.recentImportFolders.filenames.remove(Settings::application.recentImportFolders.path);
if (Settings::application.recentImportFolders.filenames.empty())
// revert mode RECENT
setNewMedia(MEDIA_RECENT);
else
setNewMedia(MEDIA_FOLDER, Settings::application.recentImportFolders.filenames.front());
}
// ordering list
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y + ImGui::GetFrameHeightWithSpacing()) );
if ( ImGuiToolkit::IconMultistate(icons_ordering_files, &Settings::application.recentImportFolders.ordering, tooltips_ordering_files) )
new_media_mode_changed = true;
ImGui::PopID();
}
else if ( new_media_mode == MEDIA_RECENT ) {
// Clear list
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y) );
if (ImGuiToolkit::IconButton( 12, 14, "Clear list")) {
Settings::application.recentImport.filenames.clear();
Settings::application.recentImport.front_is_valid = false;
setNewMedia(MEDIA_RECENT);
}
}
// come back...
ImGui::SetCursorPos(pos_bot);
}
// Sequence Source creator
else if (Settings::application.source.new_type == SOURCE_SEQUENCE){
static DialogToolkit::OpenManyFilesDialog _selectImagesDialog("Select multiple images",
IMAGES_FILES_TYPE,
IMAGES_FILES_PATTERN);
static MultiFileSequence _numbered_sequence;
static MultiFileRecorder _video_recorder;
static int _fps = 25;
ImGui::Text("Image sequence");
// clic button to load file
if ( ImGui::Button( ICON_FA_FOLDER_OPEN " Open multiple", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) {
sourceSequenceFiles.clear();
new_source_preview_.setSource();
_selectImagesDialog.open();
}
// Indication
ImGui::SameLine();
ImGuiToolkit::HelpToolTip("Create a source displaying a sequence of images;\n"
ICON_FA_CARET_RIGHT " files numbered consecutively\n"
ICON_FA_CARET_RIGHT " create a video from many images\n"
"Supports PNG, JPG or TIF.");
// return from thread for folder openning
if (_selectImagesDialog.closed()) {
// clear
new_source_preview_.setSource();
// store list of files from dialog
sourceSequenceFiles = _selectImagesDialog.files();
if (sourceSequenceFiles.empty())
Log::Notify("No file selected.");
// set sequence
_numbered_sequence = MultiFileSequence(sourceSequenceFiles);
// automatically create a MultiFile Source if possible
if (_numbered_sequence.valid()) {
std::string label = BaseToolkit::transliterate( BaseToolkit::common_pattern(sourceSequenceFiles) );
new_source_preview_.setSource( Mixer::manager().createSourceMultifile(sourceSequenceFiles, _fps), label);
}
}
// multiple files selected
if (sourceSequenceFiles.size() > 1) {
ImGui::Spacing();
// show info sequence
ImGuiTextBuffer info;
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
info.appendf("%d %s", (int) sourceSequenceFiles.size(), _numbered_sequence.codec.c_str());
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::InputText("Images", (char *)info.c_str(), info.size(), ImGuiInputTextFlags_ReadOnly);
info.clear();
if (_numbered_sequence.location.empty())
info.append("Not consecutively numbered");
else
info.appendf("%s", SystemToolkit::base_filename(_numbered_sequence.location).c_str());
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::InputText("Filenames", (char *)info.c_str(), info.size(), ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor(1);
// offer to open file browser at location
std::string path = SystemToolkit::path_filename(sourceSequenceFiles.front());
std::string label = BaseToolkit::truncated(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Folder");
// set framerate
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps");
if (ImGui::IsItemDeactivatedAfterEdit()){
if (new_source_preview_.filled()) {
std::string label = BaseToolkit::transliterate( BaseToolkit::common_pattern(sourceSequenceFiles) );
new_source_preview_.setSource( Mixer::manager().createSourceMultifile(sourceSequenceFiles, _fps), label);
}
}
ImGui::Spacing();
// Offer to create video from sequence
if ( ImGui::Button( ICON_FA_FILM " Make a video", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) {
// start video recorder
_video_recorder.setFiles( sourceSequenceFiles );
_video_recorder.setFramerate( _fps );
_video_recorder.setProfile( (VideoRecorder::Profile) Settings::application.record.profile );
_video_recorder.start();
// dialog
ImGui::OpenPopup(LABEL_VIDEO_SEQUENCE);
}
// video recorder finished: inform and open pannel to import video source from recent recordings
if ( _video_recorder.finished() ) {
// video recorder failed if it does not return a valid filename
if ( _video_recorder.filename().empty() )
Log::Warning("Failed to generate an image sequence.");
else {
Log::Notify("Image sequence saved to %s.", _video_recorder.filename().c_str());
// open the file as new recording
// if (Settings::application.recentRecordings.load_at_start)
UserInterface::manager().navigator.setNewMedia(Navigator::MEDIA_RECORDING, _video_recorder.filename());
}
}
else if (ImGui::BeginPopupModal(LABEL_VIDEO_SEQUENCE, NULL, ImGuiWindowFlags_NoResize))
{
ImGui::Spacing();
ImGui::Text("Please wait while the video is being encoded :\n");
ImGui::Text("Resolution :");ImGui::SameLine(150);
ImGui::Text("%d x %d", _video_recorder.width(), _video_recorder.height() );
ImGui::Text("Framerate :");ImGui::SameLine(150);
ImGui::Text("%d fps", _video_recorder.framerate() );
ImGui::Text("Codec :");ImGui::SameLine(150);
ImGui::Text("%s", VideoRecorder::profile_name[ _video_recorder.profile() ] );
ImGui::Text("Frames :");ImGui::SameLine(150);
ImGui::Text("%lu / %lu", (unsigned long)_video_recorder.numFrames(),
(unsigned long)_video_recorder.files().size() );
ImGui::Spacing();
ImGui::ProgressBar(_video_recorder.progress());
ImGui::Spacing();
if (ImGui::Button(ICON_FA_TIMES " Cancel"))
_video_recorder.cancel();
ImGui::EndPopup();
}
}
// single file selected
else if (sourceSequenceFiles.size() > 0) {
// open image file as source
std::string label = BaseToolkit::transliterate( sourceSequenceFiles.front() );
new_source_preview_.setSource( Mixer::manager().createSourceFile(sourceSequenceFiles.front()), label);
// done with sequence
sourceSequenceFiles.clear();
}
}
// Generated patterns Source creator
else if (Settings::application.source.new_type == SOURCE_GENERATED){
static DialogToolkit::OpenFileDialog subtitleopenialog("Open Subtitle",
SUBTITLE_FILES_TYPE,
SUBTITLE_FILES_PATTERN );
bool update_new_source = false;
ImGui::Text("Patterns & generated graphics");
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##Pattern", "Select", ImGuiComboFlags_HeightLarge))
{
if ( ImGui::Selectable("Custom gstreamer " ICON_FA_CODE) ) {
update_new_source = true;
generated_type = 0;
pattern_type = -1;
}
if ( ImGui::Selectable("Text " ICON_FA_CODE) ) {
update_new_source = true;
generated_type = 1;
pattern_type = -1;
}
for (int p = 0; p < (int) Pattern::count(); ++p){
pattern_descriptor pattern = Pattern::get(p);
std::string label = pattern.label + (pattern.animated ? " " ICON_FA_PLAY_CIRCLE : " ");
if (pattern.available && ImGui::Selectable( label.c_str(), p == pattern_type )) {
update_new_source = true;
generated_type = 2;
pattern_type = p;
}
}
ImGui::EndCombo();
}
static ImVec2 fieldsize(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 100);
static int numlines = 0;
const ImGuiContext& g = *GImGui;
fieldsize.y = MAX(3, numlines) * g.FontSize + g.Style.ItemSpacing.y + g.Style.FramePadding.y;
// Indication
ImGui::SameLine();
ImGuiToolkit::HelpToolTip("Create a source with patterns or graphics generated algorithmically. "
"Entering text or a custom gstreamer pipeline is also possible.");
ImGui::Spacing();
if (generated_type == 0) {
static std::vector< std::pair< std::string, std::string> > _examples = { {"Videotest", "videotestsrc horizontal-speed=1 ! video/x-raw, width=640, height=480 " },
{"Checker", "videotestsrc pattern=checkers-8 ! video/x-raw, width=64, height=64 "},
{"Color", "videotestsrc pattern=gradient foreground-color= 0xff55f54f background-color= 0x000000 "},
{"Text", "videotestsrc pattern=black ! textoverlay text=\"vimix\" halignment=center valignment=center font-desc=\"Sans,72\" "},
{"GStreamer Webcam", "udpsrc port=5000 buffer-size=200000 ! h264parse ! avdec_h264 "},
{"SRT listener", "srtsrc uri=\"srt://:5000?mode=listener\" ! decodebin "}
};
static std::string _description = _examples[0].second;
// Editor
if ( ImGuiToolkit::InputCodeMultiline("Pipeline", &_description, fieldsize, &numlines) )
update_new_source = true;
// Local menu for list of examples
ImVec2 pos_bot = ImGui::GetCursorPos();
ImGui::SetCursorPos( pos_bot + ImVec2(fieldsize.x + IMGUI_SAME_LINE, -ImGui::GetFrameHeightWithSpacing()));
if (ImGui::BeginCombo("##Examples", "Examples", ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLarge)) {
ImGui::TextDisabled("Examples");
for (auto it = _examples.begin(); it != _examples.end(); ++it) {
if (ImGui::Selectable( it->first.c_str() ) ) {
_description = it->second;
update_new_source = true;
}
}
ImGui::Separator();
ImGui::TextDisabled("Explore online");
if (ImGui::Selectable( ICON_FA_EXTERNAL_LINK_ALT " Documentation" ) )
SystemToolkit::open("https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html?gi-language=c#pipeline-description");
if (ImGui::Selectable( ICON_FA_EXTERNAL_LINK_ALT " Video test source" ) )
SystemToolkit::open("https://gstreamer.freedesktop.org/documentation/videotestsrc/index.html?gi-language=c#videotestsrc-page");
ImGui::EndCombo();
}
ImGui::SetCursorPos(pos_bot);
// take action
if (update_new_source)
new_source_preview_.setSource( Mixer::manager().createSourceStream(_description), "Custom");
}
// if text source selected
else if (generated_type == 1) {
static std::vector<std::pair<std::string, std::string> > _examples
= {{"Hello", "Hello world!"},
{"Rich text", "Text in <i>italics</i> or <b>bold</b>"},
{"Multiline", "One\nTwo\nThree\nFour\nFive"} };
static std::string _contents = _examples[0].second;
// Editor
if ( (SystemToolkit::has_extension(_contents, "srt") || SystemToolkit::has_extension(_contents, "sub") )
&& SystemToolkit::file_exists(_contents)) {
static char dummy_str[1024];
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
snprintf(dummy_str, 1024, "%s", _contents.c_str());
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
ImGui::InputText("##Filesubtitle",
dummy_str,
IM_ARRAYSIZE(dummy_str),
ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor(1);
} else if (ImGuiToolkit::InputTextMultiline("Text", &_contents, fieldsize, &numlines))
update_new_source = true;
// Local menu for list of examples
ImVec2 pos_bot = ImGui::GetCursorPos();
ImGui::SetCursorPos(
pos_bot
+ ImVec2(fieldsize.x + IMGUI_SAME_LINE, -ImGui::GetFrameHeightWithSpacing()));
if (ImGui::BeginCombo("##Examples",
"Examples",
ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLarge)) {
if (ImGui::Selectable(ICON_FA_FOLDER_OPEN " Open subtitle"))
subtitleopenialog.open();
ImGui::Separator();
ImGui::TextDisabled("Examples");
for (auto it = _examples.begin(); it != _examples.end(); ++it) {
if (ImGui::Selectable(it->first.c_str())) {
_contents = it->second;
update_new_source = true;
}
}
ImGui::Separator();
ImGui::TextDisabled("Explore online");
if (ImGui::Selectable(ICON_FA_EXTERNAL_LINK_ALT " Pango syntax"))
SystemToolkit::open("https://docs.gtk.org/Pango/pango_markup.html");
if (ImGui::Selectable(ICON_FA_EXTERNAL_LINK_ALT " SubRip file format"))
SystemToolkit::open("https://en.wikipedia.org/wiki/SubRip");
ImGui::EndCombo();
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGuiToolkit::Indication("Format and layout options will be available after source creation.", ICON_FA_INFO_CIRCLE);
ImGui::SetCursorPos(pos_bot);
// resolution
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Ratio",
&Settings::application.source.ratio,
GlmToolkit::aspect_ratio_names,
IM_ARRAYSIZE(GlmToolkit::aspect_ratio_names)))
update_new_source = true;
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Height",
&Settings::application.source.res,
GlmToolkit::height_names,
IM_ARRAYSIZE(GlmToolkit::height_names)))
update_new_source = true;
// get subtitle file if dialog finished
if (subtitleopenialog.closed()) {
// get the filename from this file dialog
std::string importpath = subtitleopenialog.path();
// open file
if (!importpath.empty()) {
_contents = importpath;
update_new_source = true;
}
}
// take action
if (update_new_source) {
glm::ivec2 res = GlmToolkit::resolutionFromDescription(Settings::application.source.ratio, Settings::application.source.res);
new_source_preview_.setSource(Mixer::manager().createSourceText(_contents, res), "Text");
}
}
// if pattern selected
else {
// resolution
if (pattern_type >= 0) {
static char dummy_str[1024];
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
pattern_descriptor pattern = Pattern::get(pattern_type);
snprintf(dummy_str, 1024, "%s", pattern.label.c_str());
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
ImGui::InputText("Pattern", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor(1);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Ratio", &Settings::application.source.ratio,
GlmToolkit::aspect_ratio_names, IM_ARRAYSIZE(GlmToolkit::aspect_ratio_names) ) )
update_new_source = true;
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Height", &Settings::application.source.res,
GlmToolkit::height_names, IM_ARRAYSIZE(GlmToolkit::height_names) ) )
update_new_source = true;
}
// create preview
if (update_new_source) {
glm::ivec2 res = GlmToolkit::resolutionFromDescription(Settings::application.source.ratio, Settings::application.source.res);
new_source_preview_.setSource( Mixer::manager().createSourcePattern(pattern_type, res),
Pattern::get(pattern_type).label);
}
}
}
// Input and connected source creator
else if (Settings::application.source.new_type == SOURCE_CONNECTED){
ImGui::Text("Input devices & streams");
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##ExternalConnected", "Select "))
{
// 1. Loopback source
if ( ImGuiToolkit::SelectableIcon(ICON_SOURCE_RENDER, "Display Loopback", false) ) {
custom_connected = false;
custom_screencapture = false;
new_source_preview_.setSource( Mixer::manager().createSourceRender(), "Display Loopback");
}
// 2. Screen capture (open selector if more than one window)
if (ScreenCapture::manager().numWindow() > 0) {
std::string namewin = ScreenCapture::manager().name(0);
if ( ImGuiToolkit::SelectableIcon(ICON_SOURCE_DEVICE_SCREEN, namewin.c_str(), false) ) {
custom_connected = false;
if (ScreenCapture::manager().numWindow() > 1) {
new_source_preview_.setSource();
custom_screencapture = true;
}
else {
new_source_preview_.setSource( Mixer::manager().createSourceScreen(namewin), namewin);
custom_screencapture = false;
}
}
}
// 3. Network connected SRT
if ( ImGuiToolkit::SelectableIcon(ICON_SOURCE_SRT, "SRT Broadcast", false) ) {
new_source_preview_.setSource();
custom_connected = true;
custom_screencapture = false;
}
// 4. Devices
ImGui::Separator();
for (int d = 0; d < Device::manager().numDevices(); ++d){
std::string namedev = Device::manager().name(d);
if (ImGui::Selectable( namedev.c_str() )) {
custom_connected = false;
custom_screencapture = false;
new_source_preview_.setSource( Mixer::manager().createSourceDevice(namedev), namedev);
}
}
// 5. Network connected vimix
for (int d = 1; d < Connection::manager().numHosts(); ++d){
std::string namehost = Connection::manager().info(d).name;
if (ImGui::Selectable( namehost.c_str() )) {
custom_connected = false;
custom_screencapture = false;
new_source_preview_.setSource( Mixer::manager().createSourceNetwork(namehost), namehost);
}
}
ImGui::EndCombo();
}
// Indication
ImGui::SameLine();
ImVec2 pos = ImGui::GetCursorPos();
if (ImGuiToolkit::IconButton(5,15,"Reload list"))
Device::manager().reload();
ImGui::SameLine();
ImGuiToolkit::HelpToolTip("Create a source capturing video streams from connected devices or machines;\n"
ICON_FA_CARET_RIGHT " vimix display loopback\n"
ICON_FA_CARET_RIGHT " screen capture\n"
ICON_FA_CARET_RIGHT " broadcasted with SRT over network.\n"
ICON_FA_CARET_RIGHT " webcams or frame grabbers\n"
ICON_FA_CARET_RIGHT " vimix Peer-to-peer in local network.");
ImGui::Dummy(ImVec2(1, 1));
if (custom_connected) {
bool valid_ = false;
static std::string url_;
static std::string ip_ = Settings::application.recentSRT.hosts.empty() ? Settings::application.recentSRT.default_host.first : Settings::application.recentSRT.hosts.front().first;
static std::string port_ = Settings::application.recentSRT.hosts.empty() ? Settings::application.recentSRT.default_host.second : Settings::application.recentSRT.hosts.front().second;
static std::regex ipv4("(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])");
static std::regex numport("([0-9]){4,6}");
ImGui::Spacing();
ImGuiToolkit::Icon(ICON_SOURCE_SRT);
ImGui::SameLine();
ImGui::Text("SRT broadcast");
ImGui::SameLine();
ImGui::SetCursorPosX(pos.x);
ImGuiToolkit::HelpToolTip("Set the IP and Port for connecting with Secure Reliable Transport (SRT) protocol to a video broadcaster that is waiting for connections (listener mode).");
// Entry field for IP
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGuiToolkit::InputText("IP", &ip_, ImGuiInputTextFlags_CharsDecimal);
valid_ = std::regex_match(ip_, ipv4);
// Entry field for port
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGuiToolkit::InputText("Port", &port_, ImGuiInputTextFlags_CharsDecimal);
valid_ &= std::regex_match(port_, numport);
// URL generated from protorol, IP and port
url_ = Settings::application.recentSRT.protocol + ip_ + ":" + port_;
// push style for disabled text entry
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.8f));
// display default IP & port
if (Settings::application.recentSRT.hosts.empty()) {
ImGuiToolkit::InputText("##url", &url_, ImGuiInputTextFlags_ReadOnly);
}
// display most recent host & offer list of known hosts
else {
if (ImGui::BeginCombo("##SRThosts", url_.c_str())) {
for (auto it = Settings::application.recentSRT.hosts.begin(); it != Settings::application.recentSRT.hosts.end(); ++it) {
if (ImGui::Selectable( std::string(Settings::application.recentSRT.protocol + it->first + ":" + it->second).c_str() ) ) {
ip_ = it->first;
port_ = it->second;
}
}
ImGui::EndCombo();
}
// icons to clear lists
ImVec2 pos_top = ImGui::GetCursorPos();
ImGui::SameLine();
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.7);
if (ImGuiToolkit::IconButton( ICON_FA_BACKSPACE, "Clear list of recent uri")) {
Settings::application.recentSRT.hosts.clear();
ip_ = Settings::application.recentSRT.default_host.first;
port_ = Settings::application.recentSRT.default_host.second;
}
ImGui::PopStyleVar();
ImGui::SetCursorPos(pos_top);
}
// pop disabled style
ImGui::PopStyleColor(1);
// push a RED color style if host is not valid
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(0.0f, valid_ ? 0.0f : 0.6f, 0.4f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(0.0f, valid_ ? 0.0f : 0.7f, 0.3f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(0.0f, valid_ ? 0.0f : 0.8f, 0.2f));
// create a new SRT source if host is valid
if ( ImGui::Button("Call", ImVec2(IMGUI_RIGHT_ALIGN, 0)) && valid_ ) {
// set preview source
new_source_preview_.setSource( Mixer::manager().createSourceSrt(ip_, port_), url_);
// remember known host
Settings::application.recentSRT.push(ip_, port_);
}
ImGui::PopStyleColor(3);
}
if (custom_screencapture) {
ImGui::Spacing();
ImGuiToolkit::Icon(ICON_SOURCE_DEVICE_SCREEN);
ImGui::SameLine();
ImGui::Text("Screen Capture");
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##ScreenCaptureSelect", "Select window", ImGuiComboFlags_HeightLarge))
{
for (int d = 0; d < ScreenCapture::manager().numWindow(); ++d){
std::string namewin = ScreenCapture::manager().name(d);
if (ImGui::Selectable( namewin.c_str() )) {
new_source_preview_.setSource( Mixer::manager().createSourceScreen(namewin), namewin);
}
}
ImGui::EndCombo();
}
}
}
ImGui::NewLine();
// if a new source was added
if (new_source_preview_.filled()) {
// show preview
new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
// ask to import the source in the mixer
ImGui::NewLine();
if (new_source_preview_.ready() && ImGui::Button( ICON_FA_CHECK " Ok", ImVec2(pannel_width_ - padding_width_, 0)) ) {
// take out the source from the preview
Source *s = new_source_preview_.getSource();
// restart and add the source.
if (source_to_replace != nullptr)
Mixer::manager().replaceSource(source_to_replace, s);
else
Mixer::manager().addSource(s);
s->replay();
// close NEW pannel
togglePannelNew();
}
}
ImGui::End();
}
}
bool Navigator::RenderMousePointerSelector(const ImVec2 &size)
{
ImGuiContext& g = *GImGui;
ImVec2 top = ImGui::GetCursorPos();
bool enabled = Settings::application.current_view != View::TRANSITION;
bool ret = false;
///
/// interactive button of the given size: show menu if clic or mouse over
///
static uint counter_menu_timeout = 0;
if ( ImGui::InvisibleButton("##MenuMousePointerButton", size) /*|| ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)*/ ) {
if (enabled)
ImGui::OpenPopup( "MenuMousePointer" );
}
ImVec2 bottom = ImGui::GetCursorScreenPos();
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) {
ret = true;
counter_menu_timeout=0;
}
// Change color of icons depending on context menu status
const ImVec4* colors = ImGui::GetStyle().Colors;
if (enabled)
ImGui::PushStyleColor( ImGuiCol_Text, ImGui::IsPopupOpen("MenuMousePointer") ? colors[ImGuiCol_DragDropTarget] : colors[ImGuiCol_Text] );
else
ImGui::PushStyleColor( ImGuiCol_Text, colors[ImGuiCol_TextDisabled] );
// Draw centered icon of Mouse pointer
ImVec2 margin = (size - ImVec2(g.FontSize, g.FontSize)) * 0.5f;
ImGui::SetCursorPos( top + margin );
if ( UserInterface::manager().altModifier() || Settings::application.mouse_pointer_lock) {
// icon with corner erased
ImGuiToolkit::Icon(ICON_POINTER_OPTION);
// Draw sub-icon of Mouse pointer type
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
ImVec2 t = top + size - ImVec2(g.FontSize, g.FontSize) - ImVec2(g.Style.FramePadding.y, g.Style.FramePadding.y);
ImGui::SetCursorPos( t );
std::tuple<int, int, std::string, std::string> mode = Pointer::Modes.at( (size_t) Settings::application.mouse_pointer);
ImGuiToolkit::Icon(std::get<0>(mode), std::get<1>(mode));
ImGui::PopFont();
}
else
// standard icon
ImGuiToolkit::Icon(ICON_POINTER_DEFAULT);
// Revert
ImGui::PopStyleColor(1);
ImGui::SetCursorScreenPos(bottom);
///
/// Render the Popup menu selector
///
ImGui::SetNextWindowPos( bottom + ImVec2(size.x + g.Style.WindowPadding.x, -size.y), ImGuiCond_Always );
if (ImGui::BeginPopup( "MenuMousePointer" ))
{
// loop over all mouse pointer modes
for ( size_t m = Pointer::POINTER_GRID; m < Pointer::POINTER_INVALID; ++m) {
bool on = m == (size_t) Settings::application.mouse_pointer;
const std::tuple<int, int, std::string, std::string> mode = Pointer::Modes.at(m);
// show icon of mouse mode and set mouse pointer if selected
if (ImGuiToolkit::IconToggle( std::get<0>(mode), std::get<1>(mode), &on, std::get<2>(mode).c_str()) )
Settings::application.mouse_pointer = (int) m;
// space between icons
ImGui::SameLine(0, IMGUI_SAME_LINE);
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
// button to lock the ALT activation
ImGui::SetCursorPosY(margin.y);
ImGui::SameLine(0, IMGUI_SAME_LINE * 3);
ImGuiToolkit::ButtonToggle(Settings::application.mouse_pointer_lock ? ICON_FA_LOCK ALT_LOCK : ICON_FA_UNLOCK ALT_LOCK,
&Settings::application.mouse_pointer_lock,
"Activate the selected Snap mouse cursor by pressing the [" ALT_MOD "] key.\n\n"
ICON_FA_LOCK ALT_LOCK " keeps the Snap mouse cursor active.");
// slider to adjust strength of the mouse pointer
float *val = &Settings::application.mouse_pointer_strength[ Settings::application.mouse_pointer ];
// General case
if (Settings::application.mouse_pointer != Pointer::POINTER_GRID) {
int percent = *val * 100.f;
ImGui::SetNextItemWidth( IMGUI_RIGHT_ALIGN );
if (ImGui::SliderInt( "##sliderstrenght", &percent, 0, 100, percent < 1 ? "Min" : "%d%%") )
*val = 0.01f * (float) percent;
if (ImGui::IsItemHovered() && g.IO.MouseWheel != 0.f ){
*val += 0.1f * g.IO.MouseWheel;
*val = CLAMP( *val, 0.f, 1.f);
}
}
// special case of GRID
else {
// toggle proportional grid
const char *tooltip_lock[2] = {"Square grid", "Aspect-ratio grid"};
if ( ImGuiToolkit::IconToggle(19, 2, 18, 2, &Settings::application.proportional_grid, tooltip_lock) )
View::need_deep_update_++;
ImGui::SameLine(0, IMGUI_SAME_LINE);
// slider of 5 text values
static const char* grid_names[Grid::UNIT_ONE+1] = { "Precise", "Small", "Default", "Large", "Huge"};
int grid_current = (Grid::Units) round( *val * 4.f) ;
const char* grid_current_name = (grid_current >= 0 && grid_current <= Grid::UNIT_ONE) ?
grid_names[grid_current] : "Unknown";
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::SliderInt("##slidergrid", &grid_current, 0, Grid::UNIT_ONE, grid_current_name) )
*val = (float) grid_current * 0.25f;
if (ImGui::IsItemHovered() && g.IO.MouseWheel != 0.f ){
*val += 0.25f * g.IO.MouseWheel;
*val = CLAMP( *val, 0.f, 1.f);
}
}
// Label & reset button
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton( std::get<3>(Pointer::Modes.at(Settings::application.mouse_pointer)).c_str() ))
*val = 0.5f;
ImGui::PopFont();
// timer to close menu like a tooltip
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
counter_menu_timeout=0;
else if (++counter_menu_timeout > 10)
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
return ret;
}
void Navigator::RenderMainPannelSession()
{
const float preview_width = ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN;
const float preview_height = 4.5f * ImGui::GetFrameHeightWithSpacing();
const float space = ImGui::GetStyle().ItemSpacing.y;
//
// Session
//
ImGui::Text("Session");
std::string sessions_current = Mixer::manager().session()->filename();
if (sessions_current.empty())
sessions_current = "<unsaved>";
else
sessions_current = SystemToolkit::filename(sessions_current);
//
// Show combo box of recent files
//
static std::list<std::string> sessions_list;
// get list of recent sessions when it changed, not at every frame
if (Settings::application.recentSessions.changed) {
Settings::application.recentSessions.changed = false;
Settings::application.recentSessions.validate();
sessions_list = Settings::application.recentSessions.filenames;
}
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##RecentSessions", sessions_current.c_str() )) {
// list all sessions in recent list
for(auto it = sessions_list.begin(); it != sessions_list.end(); ++it) {
if (ImGui::Selectable( SystemToolkit::filename(*it).c_str() ) ) {
Mixer::manager().open( *it );
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text( "%s", (*it).c_str() );
ImGui::EndTooltip();
}
}
ImGui::EndCombo();
}
ImVec2 pos = ImGui::GetCursorPos();
// ImGui::SameLine();
// if ( ImGuiToolkit::IconButton(ICON_FA_FILE_DOWNLOAD, "Save"))
// UserInterface::manager().saveOrSaveAs();
//// if ( Mixer::manager().session()->filename().empty()) {
//// if ( ImGuiToolkit::IconButton(ICON_FA_FILE_DOWNLOAD, "Save as"))
//// UserInterface::manager().saveOrSaveAs();
//// } else {
//// if (ImGuiToolkit::IconButton(3, 5, "Show in finder"))
//// SystemToolkit::open(SystemToolkit::path_filename(Mixer::manager().session()->filename()));
//// }
// ImGui::SetCursorPos(pos);
//
// Preview session
//
Session *se = Mixer::manager().session();
float width = preview_width;
float height = se->frame()->projectionSize().y * width / ( se->frame()->projectionSize().x * se->frame()->aspectRatio());
if (height > preview_height - space) {
height = preview_height - space;
width = height * se->frame()->aspectRatio() * ( se->frame()->projectionSize().x / se->frame()->projectionSize().y);
}
// centered image
ImGui::SetCursorPos( ImVec2(pos.x + 0.5f * (preview_width-width), pos.y + 0.5f * (preview_height-height-space)) );
ImGui::Image((void*)(uintptr_t) se->frame()->texture(), ImVec2(width, height));
// right side options for session
if (!Mixer::manager().session()->filename().empty()) {
//
// Right align icon top : heart to add to favorites
//
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + space) );
// if session is in favorites
if ( UserInterface::manager().favorites.has( Mixer::manager().session()->filename() ) > 0 ) {
// offer to remove from favorites
if ( ImGuiToolkit::IconButton( 15, 4 , "Remove from favorites")) {
UserInterface::manager().favorites.remove( Mixer::manager().session()->filename() );
}
}
// else session is not in favorites, offer to add
else if ( ImGuiToolkit::IconButton( 16, 4 , "Add to favorites")) {
UserInterface::manager().favorites.add( Mixer::manager().session()->filename() );
}
//
// Right align icon middle : sticky note
//
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + preview_height - 2.f * ImGui::GetFrameHeightWithSpacing()) );
if ( ImGuiToolkit::IconButton( ICON_FA_STICKY_NOTE " +", "Add a sticky note")) {
Mixer::manager().session()->addNote();
}
//
// Right align bottom icon : thumbnail of session file, on/off
//
static Thumbnail _session_thumbnail;
static FrameBufferImage *_thumbnail = nullptr;
bool _user_thumbnail = Mixer::manager().session()->thumbnail() != nullptr;
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + preview_height - ImGui::GetFrameHeightWithSpacing()) );
if (ImGuiToolkit::IconToggle(2, 8, 7, 8, &_user_thumbnail)) {
if (_user_thumbnail)
Mixer::manager().session()->setThumbnail();
else {
Mixer::manager().session()->resetThumbnail();
_session_thumbnail.reset();
}
_thumbnail = nullptr;
}
if (ImGui::IsItemHovered()){
// thumbnail changed
if (_thumbnail != Mixer::manager().session()->thumbnail()) {
_session_thumbnail.reset();
_thumbnail = Mixer::manager().session()->thumbnail();
if (_thumbnail != nullptr)
_session_thumbnail.fill( _thumbnail );
}
ImGui::BeginTooltip();
if (_session_thumbnail.filled()) {
_session_thumbnail.Render(230);
ImGui::Text(" Custom thumbnail");
}
else {
ImGui::Text(" No thumbnail ");
}
ImGui::EndTooltip();
}
}
//
// Menu for actions on current session
ImGui::SetCursorPos( ImVec2( pos.x, pos.y + preview_height));
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("##Selectpanelsession", &Settings::application.pannel_current_session_mode,
ICON_FA_CODE_BRANCH " Versions\0" ICON_FA_HISTORY " Undo history\0" ICON_FA_BORDER_STYLE " Resolution\0");
ImVec2 pos_bot = ImGui::GetCursorPos();
//
// Current 2. RESOLUTION
//
if (Settings::application.pannel_current_session_mode > 1) {
// Information and resolution
const FrameBuffer *output = Mixer::manager().session()->frame();
if (output) {
// change resolution (height only)
// get parameters to edit resolution
glm::ivec2 preset = RenderView::presetFromResolution(output->resolution());
glm::ivec2 custom = glm::ivec2(output->resolution());
if (preset.x > -1) {
// cannot change resolution when recording
if ( UserInterface::manager().outputcontrol.isRecording() ) {
// show static info (same size than combo)
static char dummy_str[512];
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
snprintf(dummy_str, 512, "%s", RenderView::ratio_preset_name[preset.x]);
ImGui::InputText("Ratio", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
if (preset.x < RenderView::AspectRatio_Custom) {
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
snprintf(dummy_str, 512, "%s", RenderView::height_preset_name[preset.y]);
ImGui::InputText("Height", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
} else {
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
snprintf(dummy_str, 512, "%d", custom.x);
ImGui::InputText("Width", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
snprintf(dummy_str, 512, "%d", custom.y);
ImGui::InputText("Height", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
}
ImGui::PopStyleColor(1);
}
// offer to change filename, ratio and resolution
else {
// combo boxes to select aspect rario
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Ratio", &preset.x, RenderView::ratio_preset_name, IM_ARRAYSIZE(RenderView::ratio_preset_name) ) ) {
// change to custom aspect ratio: propose 1:1
glm::vec3 res = glm::vec3(custom.y, custom.y, 0.f);
// else, change to preset aspect ratio
if (preset.x < RenderView::AspectRatio_Custom)
res = RenderView::resolutionFromPreset(preset.x, preset.y);
// change resolution
Mixer::manager().setResolution(res);
}
// - preset aspect ratio : propose preset height
if (preset.x < RenderView::AspectRatio_Custom) {
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Height", &preset.y, RenderView::height_preset_name, IM_ARRAYSIZE(RenderView::height_preset_name) ) ) {
glm::vec3 res = RenderView::resolutionFromPreset(preset.x, preset.y);
Mixer::manager().setResolution(res);
}
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
static char dummy_str[512];
snprintf(dummy_str, 512, "%d", custom.x );
ImGui::InputText("Width", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor(1);
}
// - custom aspect ratio : input width and height
else {
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::InputInt("Height", &custom.y, 100, 500);
if (ImGui::IsItemDeactivatedAfterEdit())
Mixer::manager().setResolution( glm::vec3(custom, 0.f));
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::InputInt("Width", &custom.x, 100, 500);
if (ImGui::IsItemDeactivatedAfterEdit())
Mixer::manager().setResolution( glm::vec3(custom, 0.f));
}
}
}
}
}
//
// Current 1. UNDO History
//
else if (Settings::application.pannel_current_session_mode > 0) {
static uint _over = 0;
static uint64_t _displayed_over = 0;
static bool _tooltip = 0;
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y -ImGui::GetFrameHeight() ));
if ( Action::manager().current() > 1 ) {
if ( ImGuiToolkit::IconButton( ICON_FA_UNDO, "Undo" ) )
Action::manager().undo();
} else
ImGui::TextDisabled( ICON_FA_UNDO );
ImGui::SameLine();
if ( Action::manager().current() < Action::manager().max() ) {
if ( ImGuiToolkit::IconButton( ICON_FA_REDO, "Redo" ))
Action::manager().redo();
} else
ImGui::TextDisabled( ICON_FA_REDO );
// come back...
ImGui::SetCursorPos(pos_bot);
ImVec2 pos_top = ImGui::GetCursorPos();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::ListBoxHeader("##UndoHistory", Action::manager().max(), CLAMP(Action::manager().max(), 4, 8)) ) {
int count_over = 0;
ImVec2 size = ImVec2( ImGui::GetContentRegionAvailWidth(), ImGui::GetTextLineHeight() );
for (uint i = Action::manager().max(); i > 0; --i) {
if (ImGui::Selectable( Action::manager().label(i).c_str(), i == Action::manager().current(), ImGuiSelectableFlags_AllowDoubleClick, size )) {
// go to on double clic
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
Action::manager().stepTo(i);
else
// show tooltip on clic
_tooltip = true;
}
// mouse over
if (ImGui::IsItemHovered())
_over = i;
// if mouse over (only once)
if (_tooltip && _over > 0 && count_over < 1) {
static std::string text = "";
static Thumbnail _undo_thumbnail;
// load label and thumbnail only if current changed
if (_displayed_over != _over) {
_displayed_over = _over;
text = Action::manager().label(_over);
if (text.find_first_of(':') < text.size())
text = text.insert( text.find_first_of(':') + 1, 1, '\n');
FrameBufferImage *im = Action::manager().thumbnail(_over);
if (im) {
// set image content to thumbnail display
_undo_thumbnail.fill( im );
delete im;
}
else
_undo_thumbnail.reset();
}
// draw thumbnail in tooltip
ImGui::BeginTooltip();
_undo_thumbnail.Render(size.x);
ImGui::Text("%s", text.c_str());
ImGui::EndTooltip();
++count_over; // prevents display twice on item overlap
}
}
ImGui::ListBoxFooter();
}
// cancel tooltip and mouse over on mouse exit
if ( !ImGui::IsItemHovered()) {
_tooltip = false;
_displayed_over = _over = 0;
}
pos_bot = ImGui::GetCursorPos();
// right buttons
if ( Action::manager().max() > 1 ) {
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y ));
if (ImGuiToolkit::IconButton( 12, 14, "Clear history"))
Action::manager().init();
}
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
ImGuiToolkit::HelpToolTip("History of actions (latest on top). "
"Double-clic on an action to restore its status.\n\n"
ICON_FA_MAP_MARKED_ALT " Enable Show in view to automatically "
"navigate to the view when the action is undone/redone.");
// toggle button for shhow in view
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) );
ImGuiToolkit::ButtonToggle(ICON_FA_MAP_MARKED_ALT, &Settings::application.action_history_follow_view, "Show in view");
}
//
// Current 0. VERSIONS
//
else {
static uint64_t _over = 0;
static bool _tooltip = 0;
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y -ImGui::GetFrameHeight() ));
if ( ImGuiToolkit::IconButton( ICON_FA_FILE_DOWNLOAD " +")) {
UserInterface::manager().saveOrSaveAs(true);
}
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Save & Keep version");
// come back...
ImGui::SetCursorPos(pos_bot);
// list snapshots
std::list<uint64_t> snapshots = Action::manager().snapshots();
ImVec2 pos_top = ImGui::GetCursorPos();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::ListBoxHeader("##Snapshots", snapshots.size(), CLAMP(snapshots.size(), 4, 8)) ) {
static uint64_t _selected = 0;
static Thumbnail _snap_thumbnail;
static std::string _snap_label = "";
static std::string _snap_date = "";
int count_over = 0;
ImVec2 size = ImVec2( ImGui::GetContentRegionAvailWidth(), ImGui::GetTextLineHeight() );
for (auto snapit = snapshots.rbegin(); snapit != snapshots.rend(); ++snapit)
{
// entry
ImVec2 pos = ImGui::GetCursorPos();
// context menu icon on currently hovered item
if ( _over == *snapit ) {
// open context menu
ImGui::SetCursorPos(ImVec2(size.x-ImGui::GetTextLineHeight()/2.f, pos.y));
if ( ImGuiToolkit::IconButton( ICON_FA_CHEVRON_DOWN ) ) {
// current list item
Action::manager().open(*snapit);
// open menu
ImGui::OpenPopup( "MenuSnapshot" );
}
// show tooltip and select on mouse over menu icon
if (ImGui::IsItemHovered()) {
_selected = *snapit;
_tooltip = true;
}
ImGui::SetCursorPos(pos);
}
// snapshot item
if (ImGui::Selectable( Action::manager().label(*snapit).c_str(), (*snapit == _selected), ImGuiSelectableFlags_AllowDoubleClick, size )) {
// shot tooltip on clic
_tooltip = true;
// trigger snapshot on double clic
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
Action::manager().restore(*snapit);
}
// mouse over
if (ImGui::IsItemHovered()) {
_over = *snapit;
_selected = 0;
}
// if mouse over (only once)
if (_tooltip && _over > 0 && count_over < 1) {
static uint64_t current_over = 0;
// load label and thumbnail only if current changed
if (current_over != _over) {
_snap_label = Action::manager().label(_over);
_snap_date = "Version of " + readable_date_time_string(Action::manager().date(_over));
FrameBufferImage *im = Action::manager().thumbnail(_over);
if (im) {
// set image content to thumbnail display
_snap_thumbnail.fill( im );
delete im;
}
else
_snap_thumbnail.reset();
current_over = _over;
}
// draw thumbnail in tooltip
ImGui::BeginTooltip();
_snap_thumbnail.Render(size.x);
ImGui::Text("%s", _snap_date.c_str());
ImGui::EndTooltip();
++count_over; // prevents display twice on item overlap
}
}
// context menu on currently open snapshot
uint64_t current = Action::manager().currentSnapshot();
if (ImGui::BeginPopup( "MenuSnapshot" ) && current > 0 )
{
_selected = current;
// snapshot thumbnail
_snap_thumbnail.Render(size.x);
// snapshot editable label
ImGui::SetNextItemWidth(size.x);
if ( ImGuiToolkit::InputText("##Rename", &_snap_label ) )
Action::manager().setLabel( current, _snap_label);
// snapshot actions
if (ImGui::Selectable( ICON_FA_ANGLE_DOUBLE_RIGHT " Restore", false, 0, size ))
Action::manager().restore();
if (ImGui::Selectable( ICON_FA_CODE_BRANCH "- Remove", false, 0, size ))
Action::manager().remove();
// export option if possible
std::string filename = Mixer::manager().session()->filename();
if (filename.size()>0) {
if (ImGui::Selectable( ICON_FA_FILE_DOWNLOAD " Export", false, 0, size )) {
Action::manager().saveas(filename);
}
}
ImGui::EndPopup();
}
else
_selected = 0;
// end list snapshots
ImGui::ListBoxFooter();
}
// cancel tooltip and mouse over on mouse exit
if ( !ImGui::IsItemHovered()) {
_tooltip = false;
_over = 0;
}
// Right panel buton
pos_bot = ImGui::GetCursorPos();
// right buttons
if (!snapshots.empty()) {
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y ));
if (ImGuiToolkit::IconButton( 12, 14, "Clear list"))
Action::manager().clearSnapshots();
}
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
ImGuiToolkit::HelpToolTip("Previous versions of the session (latest on top). "
"Double-clic on a version to restore it.\n\n"
ICON_FA_CODE_BRANCH " With iterative saving enabled, a new version "
"is kept each time the session is saved.");
// toggle button for versioning
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - ImGui::GetFrameHeightWithSpacing()) );
ImGuiToolkit::ButtonToggle(" " ICON_FA_CODE_BRANCH " ", &Settings::application.save_version_snapshot,"Iterative saving");
ImGui::SetCursorPos( pos_bot );
}
}
#define PLAYLIST_FAVORITES ICON_FA_HEART " Favorites"
void Navigator::RenderMainPannelPlaylist()
{
//
// SESSION panel
//
ImGui::Text("Playlists");
// currently active playlist and folder
static std::string playlist_header = PLAYLIST_FAVORITES;
static Playlist active_playlist;
static std::list<std::string> folder_session_files;
// file dialogs to open / save playlist files and folders
static DialogToolkit::OpenFolderDialog customFolder("Open Folder");
static DialogToolkit::OpenManyFilesDialog selectSessions("Select vimix sessions",
VIMIX_FILE_TYPE,
VIMIX_FILE_PATTERN);
// static DialogToolkit::OpenPlaylistDialog openPlaylist("Open Playlist");
// static DialogToolkit::SavePlaylistDialog savePlaylist("Save Playlist");
// // return from thread for playlist file openning
// if (openPlaylist.closed() && !openPlaylist.path().empty()) {
// Settings::application.recentPlaylists.push(openPlaylist.path());
// Settings::application.recentPlaylists.assign(openPlaylist.path());
// Settings::application.pannel_playlist_mode = 1;
// }
// ImGui::SameLine();
// ImGui::SetCursorPosX( pannel_width_ IMGUI_RIGHT_ALIGN);
// if ( ImGuiToolkit::IconButton( 16, 3, "Create playlist")) {
// savePlaylist.open();
// }
// if (savePlaylist.closed() && !savePlaylist.path().empty()) {
// Settings::application.recentPlaylists.push(savePlaylist.path());
// Settings::application.recentPlaylists.assign(savePlaylist.path());
// Settings::application.pannel_playlist_mode = 1;
// }
// return from thread for folder openning
if (customFolder.closed() && !customFolder.path().empty()) {
Settings::application.recentFolders.push(customFolder.path());
Settings::application.recentFolders.assign(customFolder.path());
Settings::application.pannel_playlist_mode = 2;
}
// load the list of session in playlist, only once when list changed
if (Settings::application.recentPlaylists.changed) {
Settings::application.recentPlaylists.changed = false;
Settings::application.recentPlaylists.validate();
// load list
if ( !Settings::application.recentPlaylists.path.empty())
active_playlist.load( Settings::application.recentPlaylists.path );
}
// get list of vimix files in folder, only once when list changed
if (Settings::application.recentFolders.changed) {
Settings::application.recentFolders.changed = false;
Settings::application.recentFolders.validate();
// list directory
if ( !Settings::application.recentFolders.path.empty())
folder_session_files = SystemToolkit::list_directory( Settings::application.recentFolders.path, { VIMIX_FILE_PATTERN },
(SystemToolkit::Ordering) Settings::application.recentFolders.ordering);
}
//
// Show combo box of quick selection of recent playlist / directory
//
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##SelectionPlaylist", playlist_header.c_str(), ImGuiComboFlags_HeightLarge )) {
// Mode 0 : Favorite playlist
if (ImGuiToolkit::SelectableIcon( 16, 4, "Favorites", false) ) {
Settings::application.pannel_playlist_mode = 0;
}
// Mode 1 : Playlists
for(auto playlistname = Settings::application.recentPlaylists.filenames.begin();
playlistname != Settings::application.recentPlaylists.filenames.end(); playlistname++) {
if (ImGuiToolkit::SelectableIcon( 12, 3, SystemToolkit::base_filename( *playlistname ).c_str(), false )) {
// remember which path was selected
Settings::application.recentPlaylists.assign(*playlistname);
// set mode
Settings::application.pannel_playlist_mode = 1;
}
}
// Mode 2 : known folders
for(auto foldername = Settings::application.recentFolders.filenames.begin();
foldername != Settings::application.recentFolders.filenames.end(); foldername++) {
if (ImGuiToolkit::SelectableIcon( 6, 5, BaseToolkit::truncated( *foldername, 40).c_str(), false) ) {
// remember which path was selected
Settings::application.recentFolders.assign(*foldername);
// set mode
Settings::application.pannel_playlist_mode = 2;
}
}
ImGui::EndCombo();
}
//
// icon to create new playlist
//
ImVec2 pos_top = ImGui::GetCursorPos();
ImVec2 pos_right = ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y - ImGui::GetFrameHeight());
ImGui::SetCursorPos( pos_right );
if (ImGuiToolkit::IconButton( 13, 3, "Create playlist")) {
ImGui::OpenPopup("new_playlist_popup");
}
//
// icon to list directory
//
pos_right.x += ImGui::GetTextLineHeightWithSpacing() + IMGUI_SAME_LINE;
ImGui::SetCursorPos( pos_right );
if (ImGuiToolkit::IconButton( 5, 5, "List directory")) {
customFolder.open();
}
ImGui::SetCursorPos(pos_top);
const ImGuiStyle& style = ImGui::GetStyle();
const ImVec2 list_size = ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN -2.f * style.WindowPadding.x,
7.f * (ImGui::GetTextLineHeightWithSpacing() + style.FramePadding.y ) + style.FramePadding.y);
ImVec2 item_size = ImVec2( list_size.x -2.f * style.FramePadding.x, ImGui::GetTextLineHeightWithSpacing());
std::string session_hovered_ = "";
std::string session_triggered_ = "";
static uint session_tooltip_ = 0;
++session_tooltip_;
//
// Show session list depending on the mode
//
// selection MODE 0 ; FAVORITES
//
if ( Settings::application.pannel_playlist_mode == 0) {
// set header
playlist_header = PLAYLIST_FAVORITES;
// how many session files in favorite playlist
size_t index_max = UserInterface::manager().favorites.size();
item_size.x -= index_max > 7 ? style.ScrollbarSize : 0.f;
// display the sessions list and detect if one was selected (double clic)
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::ListBoxHeader("##Favorites", list_size) ) {
// list session files in favorite playlist
for (size_t index = 0; index < index_max; ++index) {
// get name of session file at index
std::string session_file = UserInterface::manager().favorites.at(index);
// unique ID for item (filename can be at different index)
ImGui::PushID( session_file.c_str() );
// item to select
ImGui::BeginGroup();
if (ImGui::Selectable( SystemToolkit::filename(session_file).c_str(), false,
ImGuiSelectableFlags_AllowDoubleClick, item_size )) {
// trigger on double clic
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
session_triggered_ = session_file;
}
// show tooltips on single clic
else
session_tooltip_ = 100;
}
if (ImGui::IsItemActive()) {
ImGui::SameLine( item_size.x - 2.f * style.ScrollbarSize );
ImGuiToolkit::Icon( 8, 15 );
}
ImGui::EndGroup();
ImGui::PopID();
// what item is hovered for tooltip
if (ImGui::IsItemHovered())
session_hovered_ = session_file;
// simple drag to reorder
else if (ImGui::IsItemActive())
{
size_t index_next = index + (ImGui::GetMouseDragDelta(0).y < -2.f * style.ItemSpacing.y ? -1 : ImGui::GetMouseDragDelta(0).y > 2.f * style.ItemSpacing.y ? 1 : 0);
if ( index_next < index_max && index != index_next ) {
// reorder in list
UserInterface::manager().favorites.move(index, index_next);
UserInterface::manager().favorites.save();
// cancel tooltip during drag
session_tooltip_ = 0;
// reset drag
ImGui::ResetMouseDragDelta();
}
}
}
ImGui::ListBoxFooter();
}
// cancel tooltip and mouse over on mouse exit
if ( !ImGui::IsItemHovered())
session_tooltip_ = 0;
}
//
// selection MODE 1 : PLAYLISTS
//
else if ( Settings::application.pannel_playlist_mode == 1) {
// set header
if (Settings::application.recentPlaylists.path.empty())
Settings::application.pannel_playlist_mode = 0;
else
playlist_header = std::string(ICON_FA_STAR) + " " + SystemToolkit::base_filename(Settings::application.recentPlaylists.path);
// how many session files in favorite playlist
size_t index_max = active_playlist.size();
size_t index_to_remove = index_max;
item_size.x -= ImGui::GetTextLineHeight() + style.ItemSpacing.x ;
item_size.x -= index_max > 6 ? style.ScrollbarSize : 0.f;
// display the sessions list and detect if one was selected (double clic)
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::ListBoxHeader("##Playlist", list_size) ) {
// list session files in favorite playlist
for (size_t index = 0; index < index_max; ++index) {
// get name of session file at index
std::string session_file = active_playlist.at(index);
// unique ID for item (filename can be at different index)
ImGui::PushID( session_file.c_str() );
// item to select
ImGui::BeginGroup();
if (ImGui::Selectable( SystemToolkit::filename(session_file).c_str(), false,
ImGuiSelectableFlags_AllowDoubleClick, item_size )) {
// trigger on double clic
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
session_triggered_ = session_file;
}
// show tooltips on single clic
else
session_tooltip_ = 100;
}
ImGui::SameLine();
if (ImGui::IsItemActive()) {
ImGuiToolkit::IconButton( 8, 15 );
}
else {
if ( ImGuiToolkit::IconButton( 19, 4, "Remove") )
index_to_remove = index;
}
ImGui::EndGroup();
ImGui::PopID();
// what item is hovered for tooltip
if (ImGui::IsItemHovered())
session_hovered_ = session_file;
// simple drag to reorder
else if (ImGui::IsItemActive())
{
size_t index_next = index + (ImGui::GetMouseDragDelta(0).y < -2.f * style.ItemSpacing.y ? -1 : ImGui::GetMouseDragDelta(0).y > 2.f * style.ItemSpacing.y ? 1 : 0);
if ( index_next < index_max && index != index_next ) {
// reorder in list and save new status
active_playlist.move(index, index_next);
active_playlist.save();
// cancel tooltip during drag
session_tooltip_ = 0;
// reset drag
ImGui::ResetMouseDragDelta();
}
}
}
ImGui::ListBoxFooter();
}
// cancel tooltip and mouse over on mouse exit
if ( !ImGui::IsItemHovered())
session_tooltip_ = 0;
// Remove
if ( index_to_remove < index_max ) {
active_playlist.remove( index_to_remove );
active_playlist.save();
}
// Right side of the list : close and save
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y));
if (ImGuiToolkit::IconButton( 14, 3, "Delete playlist")) {
ImGui::OpenPopup("delete_playlist_popup");
}
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y + 1.5f * ImGui::GetTextLineHeightWithSpacing()));
if ( ImGuiToolkit::IconButton( 18, 4, "Add sessions")) {
selectSessions.open();
}
// return from thread for sessions multiple selection
if (selectSessions.closed() && !selectSessions.files().empty()) {
active_playlist.add(selectSessions.files());
active_playlist.save();
}
}
//
// selection MODE 2 : LIST FOLDER
//
else if ( Settings::application.pannel_playlist_mode == 2) {
// set header
if (Settings::application.recentFolders.path.empty())
Settings::application.pannel_playlist_mode = 0;
else
playlist_header = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::truncated(Settings::application.recentFolders.path, 40);
// how many listed
item_size.x -= folder_session_files.size() > 7 ? style.ScrollbarSize : 0.f;
// display the sessions list and detect if one was selected (double clic)
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::ListBoxHeader("##FolderList", list_size) ) {
// list session files in folder
for(auto it = folder_session_files.begin(); it != folder_session_files.end(); ++it) {
// item to select
if (ImGui::Selectable( SystemToolkit::filename(*it).c_str(), false,
ImGuiSelectableFlags_AllowDoubleClick, item_size )) {
// trigger on double clic
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
session_triggered_ = *it;
}
// show tooltips on clic
else
session_tooltip_ = 100;
}
if (ImGui::IsItemHovered())
session_hovered_ = *it;
}
ImGui::ListBoxFooter();
}
// cancel tooltip and mouse over on mouse exit
if ( !ImGui::IsItemHovered())
session_tooltip_ = 0;
// Closing and ordering button
ImGui::PushID("##playlist_directory_actions");
// close list
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y) );
if (ImGuiToolkit::IconButton( 4, 5, "Close directory")) {
Settings::application.recentFolders.filenames.remove(Settings::application.recentFolders.path);
if (Settings::application.recentFolders.filenames.empty())
Settings::application.pannel_playlist_mode = 0;
else
Settings::application.recentFolders.assign( Settings::application.recentFolders.filenames.front() );
}
// ordering list
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y + ImGui::GetFrameHeightWithSpacing()));
if ( ImGuiToolkit::IconMultistate(icons_ordering_files, &Settings::application.recentFolders.ordering, tooltips_ordering_files) )
Settings::application.recentFolders.changed = true;
ImGui::PopID();
}
//
// Tooltip to show Session thumbnail
//
if (session_tooltip_ > 60 && !session_hovered_.empty()) {
static std::string _current_hovered = "";
static std::string _file_info = "";
static Thumbnail _file_thumbnail;
static bool with_tag_ = false;
// load info only if changed from the one already displayed
if (session_hovered_ != _current_hovered) {
_current_hovered = session_hovered_;
SessionInformation info = SessionCreator::info(_current_hovered);
_file_info = info.description;
if (info.thumbnail) {
// set image content to thumbnail display
_file_thumbnail.fill( info.thumbnail );
with_tag_ = info.user_thumbnail_;
delete info.thumbnail;
} else
_file_thumbnail.reset();
}
if ( !_file_info.empty()) {
ImGui::BeginTooltip();
ImVec2 p_ = ImGui::GetCursorScreenPos();
_file_thumbnail.Render(240);
ImGui::Text("%s", _file_info.c_str());
if (with_tag_) {
ImGui::SetCursorScreenPos(p_ + ImVec2(6, 6));
ImGui::Text(ICON_FA_TAG);
}
ImGui::EndTooltip();
}
}
//
// Double clic to trigger openning of session
//
if (!session_triggered_.empty()) {
Mixer::manager().open( session_triggered_, Settings::application.smooth_transition );
if (Settings::application.smooth_transition)
WorkspaceWindow::clearWorkspace();
}
// help indicator
pos_top.y += list_size.y;
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y - 2.f * ImGui::GetFrameHeightWithSpacing()));
ImGuiToolkit::HelpToolTip("Double-clic on a filename to open the session.\n\n"
ICON_FA_ARROW_CIRCLE_RIGHT " enable Smooth transition "
"to perform a cross fading with the current session.");
// toggle button for smooth transition
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y - ImGui::GetFrameHeightWithSpacing()) );
ImGuiToolkit::ButtonToggle(ICON_FA_ARROW_CIRCLE_RIGHT, &Settings::application.smooth_transition, "Smooth transition");
//
// Popup window to create playlist
//
ImGui::SetNextWindowSize(ImVec2(0.8f * pannel_width_, 2.2f*ImGui::GetFrameHeightWithSpacing()), ImGuiCond_Always );
if (ImGui::BeginPopup("new_playlist_popup", ImGuiWindowFlags_NoMove))
{
static bool withcopy = false;
char text_buf[64] = "";
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::InputTextWithHint("Name", "[Enter] to validate", text_buf, 64, ImGuiInputTextFlags_EnterReturnsTrue) ) {
std::string filename = std::string(text_buf);
if ( !filename.empty() ) {
filename += "." VIMIX_PLAYLIST_FILE_EXT;
filename = SystemToolkit::full_filename( UserInterface::manager().playlists_path, filename);
// create and fill the playlist
Playlist tmp;
if (withcopy) {
if (Settings::application.pannel_playlist_mode == 0)
tmp = UserInterface::manager().favorites;
else if (Settings::application.pannel_playlist_mode == 1)
tmp = active_playlist;
else if (Settings::application.pannel_playlist_mode == 2)
tmp.add(folder_session_files);
}
tmp.saveAs( filename );
// set mode to Playlist mode
Settings::application.recentPlaylists.push(filename);
Settings::application.recentPlaylists.assign(filename);
Settings::application.pannel_playlist_mode = 1;
ImGui::CloseCurrentPopup();
}
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC);
ImGuiToolkit::ButtonSwitch("Duplicate current", &withcopy);
ImGui::PopFont();
ImGui::EndPopup();
}
//
// Popup window to delete playlist
//
if (ImGui::BeginPopup("delete_playlist_popup", ImGuiWindowFlags_NoMove))
{
std::string question = "Yes, delete '";
question += SystemToolkit::base_filename(Settings::application.recentPlaylists.path) + "' ";
if ( ImGui::Button( question.c_str() )) {
// delete the file
SystemToolkit::remove_file(Settings::application.recentPlaylists.path);
// remove from the list
Settings::application.recentPlaylists.filenames.remove(Settings::application.recentPlaylists.path);
if (Settings::application.recentPlaylists.filenames.empty())
Settings::application.pannel_playlist_mode = 0;
else
Settings::application.recentPlaylists.assign( Settings::application.recentPlaylists.filenames.front() );
ImGui::CloseCurrentPopup();
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC);
ImGui::Text("This cannot be undone");
ImGui::PopFont();
ImGui::EndPopup();
}
}
void Navigator::RenderMainPannelSettings()
{
//
// Appearance
//
ImGui::Text("Settings");
int v = Settings::application.accent_color;
ImGui::Spacing();
ImGui::SetCursorPosX(0.5f * width_);
if (ImGui::RadioButton("##Color", &v, v)){
Settings::application.accent_color = (v+1)%3;
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.accent_color));
// ask Views to update
View::need_deep_update_++;
}
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip("Change accent color");
ImGui::SameLine();
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::InputFloat("##Scale", &Settings::application.scale, 0.1f, 0.1f, "%.1f")) {
Settings::application.scale = CLAMP(Settings::application.scale, 0.5f, 5.f);
ImGui::GetIO().FontGlobalScale = Settings::application.scale;
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Scale")) {
Settings::application.scale = 1.f;
ImGui::GetIO().FontGlobalScale = Settings::application.scale;
}
//
// Recording preferences
//
ImGuiToolkit::Spacing();
ImGui::TextDisabled("Recording");
// select CODEC and FPS
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("##Codec",
&Settings::application.record.profile,
VideoRecorder::profile_name,
IM_ARRAYSIZE(VideoRecorder::profile_name));
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Codec"))
Settings::application.record.profile = 0;
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("##Framerate",
&Settings::application.record.framerate_mode,
VideoRecorder::framerate_preset_name,
IM_ARRAYSIZE(VideoRecorder::framerate_preset_name));
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Framerate"))
Settings::application.record.framerate_mode = 1;
// compute number of frames in buffer and show warning sign if too low
const FrameBuffer *output = Mixer::manager().session()->frame();
if (output) {
guint64 nb = 0;
nb = VideoRecorder::buffering_preset_value[Settings::application.record.buffering_mode] / (output->width() * output->height() * 4);
char buf[512]; snprintf(buf, 512, "Buffer of %s can contain %ld frames (%dx%d), i.e. %.1f sec",
VideoRecorder::buffering_preset_name[Settings::application.record.buffering_mode],
(unsigned long)nb, output->width(), output->height(),
(float)nb / (float) VideoRecorder::framerate_preset_value[Settings::application.record.framerate_mode] );
ImGuiToolkit::Indication(buf, 4, 6);
ImGui::SameLine(0);
}
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderInt("##Buffer", &Settings::application.record.buffering_mode, 0,
IM_ARRAYSIZE(VideoRecorder::buffering_preset_name)-1,
VideoRecorder::buffering_preset_name[Settings::application.record.buffering_mode]);
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Buffer"))
Settings::application.record.buffering_mode = 2;
ImGuiToolkit::Indication("Priority when buffer is full and recorder has to skip frames;\n"
ICON_FA_CARET_RIGHT " Duration: Correct duration, variable framerate."
ICON_FA_CARET_RIGHT " Framerate: Correct framerate, shorter duration.\n",
ICON_FA_CHECK_DOUBLE);
ImGui::SameLine(0);
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("##Priority", &Settings::application.record.priority_mode, "Duration\0Framerate\0");
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Priority"))
Settings::application.record.priority_mode = 1;
//
// AUDIO
//
if (Settings::application.accept_audio) {
// Displayed name of current audio device
std::string current_audio = "None";
if (!Settings::application.record.audio_device.empty()) {
if (Audio::manager().exists(Settings::application.record.audio_device))
current_audio = Settings::application.record.audio_device;
else
Settings::application.record.audio_device = "";
}
// help indication
ImGuiToolkit::Indication("Select the audio to merge into the recording;\n"
ICON_FA_MICROPHONE_ALT_SLASH " no audio\n "
ICON_FA_MICROPHONE_ALT " a microphone input\n "
ICON_FA_VOLUME_DOWN " an audio output",
ICON_FA_MUSIC);
ImGui::SameLine(0);
// Combo selector of audio device
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##Audio", current_audio.c_str())) {
// No audio selection
if (ImGui::Selectable(ICON_FA_MICROPHONE_ALT_SLASH " None"))
Settings::application.record.audio_device = "";
// list of devices from Audio manager
for (int d = 0; d < Audio::manager().numDevices(); ++d) {
std::string namedev = Audio::manager().name(d);
std::string labeldev = (Audio::manager().is_monitor(d) ? ICON_FA_VOLUME_DOWN " "
: ICON_FA_MICROPHONE_ALT " ")
+ namedev;
if (ImGui::Selectable(labeldev.c_str())) {
Settings::application.record.audio_device = namedev;
}
}
ImGui::EndCombo();
}
if (!Settings::application.record.audio_device.empty() && ImGui::IsItemHovered())
ImGuiToolkit::ToolTip(current_audio.c_str());
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Audio"))
Settings::application.record.audio_device = "";
}
//
// Steaming preferences
//
ImGuiToolkit::Spacing();
ImGui::TextDisabled("Stream");
ImGuiToolkit::Indication("Peer-to-peer sharing local network\n\n"
"vimix can stream JPEG (default) or H264 (less bandwidth, higher encoding cost)", ICON_FA_SHARE_ALT_SQUARE);
ImGui::SameLine(0);
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("##P2P codec", &Settings::application.stream_protocol, "JPEG\0H264\0");
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("P2P codec"))
Settings::application.stream_protocol = 0;
if (VideoBroadcast::available()) {
char msg[256];
ImFormatString(msg, IM_ARRAYSIZE(msg), "SRT Broadcast\n\n"
"vimix listens to SRT requests on Port %d. "
"Example network addresses to call:\n"
" srt//%s:%d (localhost)\n"
" srt//%s:%d (local IP)",
Settings::application.broadcast_port,
NetworkToolkit::host_ips()[0].c_str(), Settings::application.broadcast_port,
NetworkToolkit::host_ips()[1].c_str(), Settings::application.broadcast_port );
ImGuiToolkit::Indication(msg, ICON_FA_GLOBE);
ImGui::SameLine(0);
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
char bufport[7] = "";
snprintf(bufport, 7, "%d", Settings::application.broadcast_port);
ImGui::InputTextWithHint("##SRT Port", "7070", bufport, 6, ImGuiInputTextFlags_CharsDecimal);
if (ImGui::IsItemDeactivatedAfterEdit()){
if ( BaseToolkit::is_a_number(bufport, &Settings::application.broadcast_port))
Settings::application.broadcast_port = CLAMP(Settings::application.broadcast_port, 1029, 49150);
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("SRT Port"))
Settings::application.broadcast_port = 7070;
}
if (ShmdataBroadcast::available()) {
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));
char msg[256];
if (ShmdataBroadcast::available(ShmdataBroadcast::SHM_SHMDATASINK)) {
ImFormatString(msg, IM_ARRAYSIZE(msg), "Shared Memory\n\n"
"vimix can share to RAM with "
"gstreamer default 'shmsink' "
"and with 'shmdatasink'.\n"
"Socket file to connect to:\n%s\n",
_shm_socket_file.c_str());
}
else {
ImFormatString(msg, IM_ARRAYSIZE(msg), "Shared Memory\n\n"
"vimix can share to RAM with "
"gstreamer 'shmsink'.\n"
"Socket file to connect to:\n%s\n",
_shm_socket_file.c_str());
}
ImGuiToolkit::Indication(msg, ICON_FA_MEMORY);
ImGui::SameLine(0);
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
char bufsocket[64] = "";
snprintf(bufsocket, 64, "%s", Settings::application.shm_socket_path.c_str());
ImGui::InputTextWithHint("##SHM path", SystemToolkit::home_path().c_str(), bufsocket, 64);
if (ImGui::IsItemDeactivatedAfterEdit()) {
Settings::application.shm_socket_path = bufsocket;
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("SHM path"))
Settings::application.shm_socket_path = "";
if (ShmdataBroadcast::available(ShmdataBroadcast::SHM_SHMDATASINK)) {
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("SHM plugin", &Settings::application.shm_method, "shmsink\0shmdatasink\0");
}
}
//
// OSC preferences
//
ImGuiToolkit::Spacing();
ImGui::TextDisabled("OSC");
char msg[256];
ImFormatString(msg, IM_ARRAYSIZE(msg), "Open Sound Control\n\n"
"vimix accepts OSC messages sent by UDP on Port %d and replies on Port %d."
"Example network addresses:\n"
" udp//%s:%d (localhost)\n"
" udp//%s:%d (local IP)",
Settings::application.control.osc_port_receive,
Settings::application.control.osc_port_send,
NetworkToolkit::host_ips()[0].c_str(), Settings::application.control.osc_port_receive,
NetworkToolkit::host_ips()[1].c_str(), Settings::application.control.osc_port_receive );
ImGuiToolkit::Indication(msg, ICON_FA_NETWORK_WIRED);
ImGui::SameLine(0);
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
char bufreceive[7] = "";
snprintf(bufreceive, 7, "%d", Settings::application.control.osc_port_receive);
ImGui::InputTextWithHint("##Port in", "7000", bufreceive, 7, ImGuiInputTextFlags_CharsDecimal);
if (ImGui::IsItemDeactivatedAfterEdit()){
if ( BaseToolkit::is_a_number(bufreceive, &Settings::application.control.osc_port_receive)){
Settings::application.control.osc_port_receive = CLAMP(Settings::application.control.osc_port_receive, 1029, 49150);
Control::manager().init();
}
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Port in"))
Settings::application.control.osc_port_receive = OSC_PORT_RECV_DEFAULT;
ImGui::SetCursorPosX(width_);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
char bufsend[7] = "";
snprintf(bufsend, 7, "%d", Settings::application.control.osc_port_send);
ImGui::InputTextWithHint("##Port out", "7001", bufsend, 7, ImGuiInputTextFlags_CharsDecimal);
if (ImGui::IsItemDeactivatedAfterEdit()){
if ( BaseToolkit::is_a_number(bufsend, &Settings::application.control.osc_port_send)){
Settings::application.control.osc_port_send = CLAMP(Settings::application.control.osc_port_send, 1029, 49150);
Control::manager().init();
}
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Port out"))
Settings::application.control.osc_port_send = OSC_PORT_SEND_DEFAULT;
ImGui::SetCursorPosX(width_);
const float w = IMGUI_RIGHT_ALIGN - ImGui::GetFrameHeightWithSpacing();
ImGuiToolkit::ButtonOpenUrl( "Edit", Settings::application.control.osc_filename.c_str(), ImVec2(w, 0) );
ImGui::SameLine(0, 6);
if ( ImGuiToolkit::IconButton(15, 12, "Reload") )
Control::manager().init();
ImGui::SameLine();
ImGui::Text("Translator");
//
// System preferences
//
ImGuiToolkit::Spacing();
ImGui::TextDisabled("System");
static bool need_restart = false;
static bool vsync = (Settings::application.render.vsync > 0);
static bool multi = (Settings::application.render.multisampling > 0);
static bool gpu = Settings::application.render.gpu_decoding;
static bool audio = Settings::application.accept_audio;
bool change = false;
// hardware support deserves more explanation
ImGuiToolkit::Indication("If enabled, tries to find a platform adapted hardware-accelerated "
"driver to decode (read) or encode (record) videos.", ICON_FA_MICROCHIP);
ImGui::SameLine(0);
if (Settings::application.render.gpu_decoding_available)
change |= ImGuiToolkit::ButtonSwitch( "Hardware en/decoding", &gpu);
else
ImGui::TextDisabled("Hardware en/decoding unavailable");
// audio support deserves more explanation
ImGuiToolkit::Indication("If enabled, tries to find audio in openned videos "
"and allows recording audio.", ICON_FA_VOLUME_OFF);
ImGui::SameLine(0);
change |= ImGuiToolkit::ButtonSwitch( "Audio (experimental)", &audio);
#ifndef NDEBUG
change |= ImGuiToolkit::ButtonSwitch( "Vertical synchronization", &vsync);
change |= ImGuiToolkit::ButtonSwitch( "Multisample antialiasing", &multi);
#endif
if (change) {
need_restart = ( vsync != (Settings::application.render.vsync > 0) ||
multi != (Settings::application.render.multisampling > 0) ||
gpu != Settings::application.render.gpu_decoding ||
audio != Settings::application.accept_audio );
}
if (need_restart) {
ImGuiToolkit::Spacing();
if (ImGui::Button( ICON_FA_POWER_OFF " Quit & restart to apply", ImVec2(ImGui::GetContentRegionAvail().x - 50, 0))) {
Settings::application.render.vsync = vsync ? 1 : 0;
Settings::application.render.multisampling = multi ? 3 : 0;
Settings::application.render.gpu_decoding = gpu;
Settings::application.accept_audio = audio;
if (UserInterface::manager().TryClose())
Rendering::manager().close();
}
}
}
void Navigator::RenderTransitionPannel(const ImVec2 &iconsize)
{
if (Settings::application.current_view != View::TRANSITION) {
discardPannel();
return;
}
// Next window is a side pannel
ImGui::SetNextWindowPos( ImVec2(width_, 0), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2(pannel_width_, height_), ImGuiCond_Always );
ImGui::SetNextWindowBgAlpha( pannel_alpha_ ); // Transparent background
if (ImGui::Begin("##navigatorTrans", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
// TITLE
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetCursorPosY(0.5f * (iconsize.y - ImGui::GetTextLineHeight()));
ImGui::Text("Transition");
ImGui::PopFont();
// Transition options
ImGuiToolkit::Spacing();
ImGui::Text("Transition");
if (ImGuiToolkit::IconButton(0, 8)) Settings::application.transition.cross_fade = true;
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
int mode = Settings::application.transition.cross_fade ? 0 : 1;
if (ImGui::Combo("Fading", &mode, "Cross fading\0Fade to black\0") )
Settings::application.transition.cross_fade = mode < 1;
if (ImGuiToolkit::IconButton(4, 13)) Settings::application.transition.duration = 1.f;
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderFloat("Duration", &Settings::application.transition.duration, TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s");
if (ImGuiToolkit::IconButton(9, 1)) Settings::application.transition.profile = 0;
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("Curve", &Settings::application.transition.profile, "Linear\0Quadratic\0");
// specific transition actions
ImGuiToolkit::Spacing();
ImGui::Text("Actions");
if ( ImGui::Button( ICON_FA_TIMES " Cancel ", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
TransitionView *tv = static_cast<TransitionView *>(Mixer::manager().view(View::TRANSITION));
if (tv) tv->cancel();
}
if ( ImGui::Button( ICON_FA_PLAY " Play ", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
TransitionView *tv = static_cast<TransitionView *>(Mixer::manager().view(View::TRANSITION));
if (tv) tv->play(false);
}
ImGui::SameLine();
ImGui::Text("Animation");
if ( ImGui::Button( ICON_FA_FILE_UPLOAD " Open ", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
TransitionView *tv = static_cast<TransitionView *>(Mixer::manager().view(View::TRANSITION));
if (tv) tv->open();
}
ImGui::SameLine();
ImGui::Text("Session");
// General transition actions
ImGui::Text(" ");
if ( ImGui::Button( ICON_FA_PLAY " Play & " ICON_FA_FILE_UPLOAD " Open ", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ){
TransitionView *tv = static_cast<TransitionView *>(Mixer::manager().view(View::TRANSITION));
if (tv) tv->play(true);
}
if ( ImGui::Button( ICON_FA_DOOR_OPEN " Exit", ImVec2(ImGui::GetContentRegionAvail().x, 0)) )
UserInterface::manager().setView(View::MIXING);
ImGui::End();
}
}
void Navigator::RenderMainPannel(const ImVec2 &iconsize)
{
const ImGuiStyle& style = ImGui::GetStyle();
if (Settings::application.current_view == View::TRANSITION)
return;
// Next window is a side pannel
ImGui::SetNextWindowPos( ImVec2(width_, 0), ImGuiCond_Always );
ImGui::SetNextWindowSize( ImVec2(pannel_width_, height_), ImGuiCond_Always );
ImGui::SetNextWindowBgAlpha( pannel_alpha_ ); // Transparent background
if (ImGui::Begin("##navigatorMain", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
// Temporary fix for preventing horizontal scrolling (https://github.com/ocornut/imgui/issues/2915)
ImGui::SetScrollX(0);
//
// TITLE
//
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetCursorPosY(0.5f * (iconsize.y - ImGui::GetTextLineHeight()));
ImGui::Text("Vimix");
//
// Panel Mode selector
//
//
ImGui::SetCursorPosY(width_ - style.WindowPadding.x);
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.5f));
ImGui::Columns(5, NULL, false);
bool selected_panel_mode[5] = {0};
selected_panel_mode[pannel_main_mode_] = true;
if (ImGuiToolkit::SelectableIcon( 7, 1, "##SESSION_FILE", selected_panel_mode[0], iconsize))
Settings::application.pannel_main_mode = pannel_main_mode_ = 0;
ImGui::NextColumn();
if (ImGuiToolkit::SelectableIcon( 4, 8, "##SESSION_PLAYLIST", selected_panel_mode[1], iconsize))
Settings::application.pannel_main_mode = pannel_main_mode_ = 1;
ImGui::NextColumn();
if (ImGuiToolkit::SelectableIcon( 13, 5, "##SETTINGS", selected_panel_mode[2], iconsize))
pannel_main_mode_ = 2;
ImGui::Columns(1);
ImGui::PopStyleVar();
ImGui::PopFont();
//
// Panel Menu
//
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, IMGUI_TOP_ALIGN) );
if (ImGui::BeginMenu("File")) {
UserInterface::manager().showMenuFile();
ImGui::EndMenu();
}
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, IMGUI_TOP_ALIGN + ImGui::GetTextLineHeightWithSpacing()) );
if (ImGui::BeginMenu("Edit")) {
UserInterface::manager().showMenuEdit();
ImGui::EndMenu();
}
ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, IMGUI_TOP_ALIGN + 2.f * ImGui::GetTextLineHeightWithSpacing()) );
if (ImGui::BeginMenu("View")) {
UserInterface::manager().showMenuWindows();
ImGui::EndMenu();
}
ImGui::SetCursorPosY(2.f * width_ - style.WindowPadding.x);
//
// Panel content
//
if (pannel_main_mode_ == 0)
RenderMainPannelSession();
else if (pannel_main_mode_ == 1)
RenderMainPannelPlaylist();
else
RenderMainPannelSettings();
//
// About vimix
//
ImGuiContext& g = *GImGui;
const ImVec2 rightcorner(pannel_width_ + width_, height_);
const float remaining_height = height_ - ImGui::GetCursorPosY();
const float button_height = g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
const float icon_height = 128;
// About vimix button (if enough room)
if (remaining_height > button_height + g.Style.ItemSpacing.y) {
int index_label = 0;
const char *button_label[2] = {ICON_FA_CROW " About vimix", "About vimix"};
// Logo (if enougth room)
if (remaining_height > icon_height + button_height + g.Style.ItemSpacing.y) {
static unsigned int vimixicon = Resource::getTextureImage("images/vimix_256x256.png");
const ImVec2 draw_pos = rightcorner
- ImVec2((icon_height + pannel_width_) * 0.5f,
icon_height + button_height + g.Style.ItemSpacing.y);
ImGui::SetCursorScreenPos(draw_pos);
ImGui::Image((void *) (intptr_t) vimixicon, ImVec2(icon_height, icon_height));
// Hidden action: add a source with vimix logo if double clic on vimix logo
const ImRect bb(draw_pos, draw_pos + ImVec2(icon_height, icon_height));
const ImGuiID id = ImGui::GetCurrentWindow()->GetID("##easteregg");
bool hovered, held;
if (ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_PressedOnDoubleClick) )
Mixer::manager().paste( Resource::getText("images/logo.vmx") );
index_label = 1;
}
// Button About
ImGui::SetCursorScreenPos( rightcorner - ImVec2(pannel_width_ * 0.75f, button_height) );
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4());
if ( ImGui::Button( button_label[index_label], ImVec2(pannel_width_ * 0.5f, 0)) ) {
UserInterface::manager().show_vimix_about = true;
WorkspaceWindow::restoreWorkspace(true);
}
ImGui::PopStyleColor();
}
ImGui::End();
}
}
///
/// SOURCE PREVIEW
///
SourcePreview::SourcePreview() : source_(nullptr), label_(""), reset_(0)
{
}
void SourcePreview::setSource(Source *s, const std::string &label)
{
if(source_)
delete source_;
source_ = s;
label_ = label;
reset_ = true;
}
Source * SourcePreview::getSource()
{
Source *s = source_;
source_ = nullptr;
return s;
}
void SourcePreview::Render(float width)
{
if(source_) {
// cancel if failed
if (source_->failed()) {
// remove from list of recent import files if relevant
MediaSource *failedFile = dynamic_cast<MediaSource *>(source_);
if (failedFile != nullptr) {
Settings::application.recentImport.remove( failedFile->path() );
}
setSource();
}
else
{
// render framebuffer
if ( reset_ && source_->ready() ) {
// trick to ensure a minimum of 2 frames are rendered actively
source_->setActive(true);
source_->update( Mixer::manager().dt() );
source_->render();
source_->setActive(false);
reset_ = false;
}
else {
// update source
source_->update( Mixer::manager().dt() );
source_->render();
}
// draw preview
FrameBuffer *frame = source_->frame();
ImVec2 preview_size(width, width / frame->aspectRatio());
ImGui::Image((void*)(uintptr_t) frame->texture(), preview_size);
bool mouseover = ImGui::IsItemHovered();
if (mouseover) {
ImGui::BeginTooltip();
ImGui::TextUnformatted(label_.c_str());
ImGui::EndTooltip();
}
// if the source is playable and once its ready
if (source_->playable() && source_->ready()) {
// activate the source on mouse over
if (source_->active() != mouseover)
source_->setActive(mouseover);
// show icon '>' to indicate if we can activate it
if (!mouseover) {
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPos(pos + preview_size * ImVec2(0.5f, -0.6f));
ImGuiToolkit::Icon(12,7);
ImGui::SetCursorPos(pos);
}
}
// information text
ImGuiToolkit::Icon(source_->icon().x, source_->icon().y);
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("%s", source_->info().c_str());
if (source_->ready()) {
static InfoVisitor _info;
source_->accept(_info);
ImGui::Text("%s", _info.str().c_str());
}
else
ImGui::Text("loading...");
}
}
}
bool SourcePreview::ready() const
{
return source_ != nullptr && source_->ready();
}
///
/// THUMBNAIL
///
Thumbnail::Thumbnail() : aspect_ratio_(-1.f), texture_(0)
{
}
Thumbnail::~Thumbnail()
{
if (texture_)
glDeleteTextures(1, &texture_);
}
bool Thumbnail::filled()
{
return aspect_ratio_ > 0.f;
}
void Thumbnail::reset()
{
aspect_ratio_ = -1.f;
}
void Thumbnail::fill(const FrameBufferImage *image)
{
if (!texture_) {
glGenTextures(1, &texture_);
glBindTexture( GL_TEXTURE_2D, texture_);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, SESSION_THUMBNAIL_HEIGHT * 3, SESSION_THUMBNAIL_HEIGHT);
}
aspect_ratio_ = static_cast<float>(image->width) / static_cast<float>(image->height);
glBindTexture( GL_TEXTURE_2D, texture_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height, GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Thumbnail::Render(float width)
{
if (filled())
ImGui::Image((void*)(intptr_t)texture_, ImVec2(width, width/aspect_ratio_), ImVec2(0,0), ImVec2(0.333f*aspect_ratio_, 1.f));
}
///
/// UTILITY
///
#define SEGMENT_ARRAY_MAX 1000
#define MAXSIZE 65535
#include <glm/gtc/type_ptr.hpp>
void ShowSandbox(bool* p_open)
{
ImGui::SetNextWindowSize(ImVec2(400, 260), ImGuiCond_FirstUseEver);
if (!ImGui::Begin( ICON_FA_BABY_CARRIAGE " Sandbox", p_open))
{
ImGui::End();
return;
}
ImGui::Text("Testing sandox");
ImGui::Separator();
ImGui::Text("Source list");
Session *se = Mixer::manager().session();
for (auto sit = se->begin(); sit != se->end(); ++sit) {
ImGui::Text("[%s] %s ", std::to_string((*sit)->id()).c_str(), (*sit)->name().c_str());
}
ImGui::Separator();
ImGui::Text("Current source");
Source *so = Mixer::manager().currentSource();
if (so) {
glm::vec2 v = so->attractor(0);
if (ImGui::SliderFloat2("LL corner", glm::value_ptr(v), 0.0, 2.0))
so->setAttractor(0,v);
v = so->attractor(1);
if (ImGui::SliderFloat2("UL corner", glm::value_ptr(v), 0.0, 2.0))
so->setAttractor(1,v);
v = so->attractor(2);
if (ImGui::SliderFloat2("LR corner", glm::value_ptr(v), 0.0, 2.0))
so->setAttractor(2,v);
v = so->attractor(3);
if (ImGui::SliderFloat2("UR corner", glm::value_ptr(v), 0.0, 2.0))
so->setAttractor(3,v);
}
// ImGui::Separator();
// static char str[128] = "";
// ImGui::InputText("Command", str, IM_ARRAYSIZE(str));
// if ( ImGui::Button("Execute") )
// SystemToolkit::execute(str);
// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
// ImGui::Text("This text is in BOLD");
// ImGui::PopFont();
// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
// ImGui::Text("This text is in REGULAR");
// ImGui::PopFont();
// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_ITALIC);
// ImGui::Text("This text is in ITALIC");
// ImGui::PopFont();
// ImGui::Text("IMAGE of Font");
// ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_DEFAULT, 'v');
// ImGui::SameLine();
// ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_BOLD, 'i');
// ImGui::SameLine();
// ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_ITALIC, 'm');
// ImGui::SameLine();
// ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_MONO, 'i');
// ImGui::SameLine();
// ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_LARGE, 'x');
// static char str0[128] = "àöäüèáû вторая строчка";
// ImGui::InputText("##inputtext", str0, IM_ARRAYSIZE(str0));
// std::string tra = BaseToolkit::transliterate(std::string(str0));
// ImGui::Text("Transliteration: '%s'", tra.c_str());
// ImGui::Separator();
// static bool selected[25] = { true, false, false, false, false,
// true, false, false, false, false,
// true, false, false, true, false,
// false, false, false, true, false,
// false, false, false, true, false };
// ImVec2 keyIconSize = ImVec2(60,60);
// ImGuiContext& g = *GImGui;
// ImVec2 itemsize = keyIconSize + g.Style.ItemSpacing;
// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
// ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.50f));
// ImVec2 frame_top = ImGui::GetCursorScreenPos();
//// static int key = 0;
// static ImVec2 current(-1.f, -1.f);
// for (int i = 0; i < 25; ++i) {
// ImGui::PushID(i);
// char letter[2];
// sprintf(letter, "%c", 'A' + i);
// if (ImGui::Selectable(letter, selected[i], 0, keyIconSize))
// {
// current = ImVec2(i % 5, i / 5);
//// key = GLFW_KEY_A + i;
// }
// ImGui::PopID();
// if ((i % 5) < 4) ImGui::SameLine();
// }
// ImGui::PopStyleVar();
// ImGui::PopFont();
// if (current.x > -1 && current.y > -1) {
// ImVec2 pos = frame_top + itemsize * current;
// ImDrawList* draw_list = ImGui::GetWindowDrawList();
// draw_list->AddRect(pos, pos + keyIconSize, ImGui::GetColorU32(ImGuiCol_Text), 6.f, ImDrawCornerFlags_All, 3.f);
// }
ImGui::End();
}
void ShowAboutOpengl(bool* p_open)
{
ImGui::SetNextWindowPos(ImVec2(520, 320), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("About OpenGL", p_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::End();
return;
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
ImGui::Text("OpenGL %s", glGetString(GL_VERSION) );
ImGui::PopFont();
ImGui::Separator();
ImGui::Text("OpenGL is the premier environment for developing portable, \ninteractive 2D and 3D graphics applications.");
ImGuiToolkit::ButtonOpenUrl("Visit website", "https://www.opengl.org");
ImGui::SameLine();
static bool show_opengl_info = false;
ImGui::SetNextItemWidth(-100.f);
ImGui::Text(" Details");
ImGui::SameLine();
ImGuiToolkit::IconToggle(10,0,11,0,&show_opengl_info);
if (show_opengl_info)
{
ImGui::Separator();
bool copy_to_clipboard = ImGui::Button(MENU_COPY);
ImGui::SameLine(0.f, 60.f);
static char _openglfilter[64] = "";
ImGui::InputText("Filter", _openglfilter, 64);
ImGui::SameLine();
if ( ImGuiToolkit::ButtonIcon( 12, 14 ) )
_openglfilter[0] = '\0';
std::string filter(_openglfilter);
ImGui::BeginChildFrame(ImGui::GetID("gstinfos"), ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18), ImGuiWindowFlags_NoMove);
if (copy_to_clipboard)
{
ImGui::LogToClipboard();
ImGui::LogText("```\n");
}
ImGui::Text("OpenGL %s", glGetString(GL_VERSION) );
ImGui::Text("%s %s", glGetString(GL_RENDERER), glGetString(GL_VENDOR));
ImGui::Text("Extensions (runtime) :");
GLint numExtensions = 0;
glGetIntegerv( GL_NUM_EXTENSIONS, &numExtensions );
for (int i = 0; i < numExtensions; ++i){
std::string ext( (char*) glGetStringi(GL_EXTENSIONS, i) );
if ( filter.empty() || ext.find(filter) != std::string::npos )
ImGui::Text("%s", ext.c_str());
}
if (copy_to_clipboard)
{
ImGui::LogText("\n```\n");
ImGui::LogFinish();
}
ImGui::EndChildFrame();
}
ImGui::End();
}
void ShowAboutGStreamer(bool* p_open)
{
ImGui::SetNextWindowPos(ImVec2(430, 20), ImGuiCond_Appearing);
ImGui::SetNextWindowSize(ImVec2(600, 200), ImGuiCond_Appearing);
if (ImGui::Begin("About Gstreamer", p_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings))
{
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
ImGui::Text("GStreamer %s", GstToolkit::gst_version().c_str());
ImGui::PopFont();
ImGui::Separator();
ImGui::Text("A flexible, fast and multiplatform multimedia framework.");
ImGui::Text("GStreamer is licensed under the LGPL License.");
ImGuiToolkit::ButtonOpenUrl("Visit website", "https://gstreamer.freedesktop.org/");
ImGui::SameLine();
static bool show_config_info = false;
ImGui::SetNextItemWidth(-100.f);
ImGui::Text(" Details");
ImGui::SameLine();
ImGuiToolkit::IconToggle(10,0,11,0,&show_config_info);
if (show_config_info)
{
ImGui::Separator();
bool copy_to_clipboard = ImGui::Button(MENU_COPY);
ImGui::SameLine(0.f, 60.f);
static char _filter[64] = ""; ImGui::InputText("Filter", _filter, 64);
ImGui::SameLine();
if ( ImGuiToolkit::ButtonIcon( 12, 14 ) )
_filter[0] = '\0';
std::string filter(_filter);
ImGui::BeginChildFrame(ImGui::GetID("gstinfos"), ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18), ImGuiWindowFlags_NoMove);
if (copy_to_clipboard)
{
ImGui::LogToClipboard();
ImGui::LogText("```\n");
}
ImGui::Text("GStreamer %s", GstToolkit::gst_version().c_str());
ImGui::Text("Plugins & features (runtime) :");
std::list<std::string> filteredlist;
static std::list<std::string> pluginslist;
static std::map<std::string, std::list<std::string> > featureslist;
if (pluginslist.empty()) {
pluginslist = GstToolkit::all_plugins();
for (auto const& i: pluginslist) {
// list features
featureslist[i] = GstToolkit::all_plugin_features(i);
}
}
// filter list
if ( filter.empty() )
filteredlist = pluginslist;
else {
for (auto const& i: pluginslist) {
// add plugin if plugin name matches
if ( i.find(filter) != std::string::npos )
filteredlist.push_back( i );
// check in features
for (auto const& j: featureslist[i]) {
// add plugin if feature name matches
if ( j.find(filter) != std::string::npos )
filteredlist.push_back( i );
}
}
filteredlist.unique();
}
// display list
for (auto const& t: filteredlist) {
ImGui::Text("> %s", t.c_str());
for (auto const& j: featureslist[t]) {
if ( j.find(filter) != std::string::npos )
{
ImGui::Text(" - %s", j.c_str());
}
}
}
if (copy_to_clipboard)
{
ImGui::LogText("\n```\n");
ImGui::LogFinish();
}
ImGui::EndChildFrame();
}
ImGui::End();
}
}
void SetMouseCursor(ImVec2 mousepos, View::Cursor c)
{
// Hack if GLFW does not have all cursors, ask IMGUI to redraw cursor
#if GLFW_HAS_NEW_CURSORS == 0
ImGui::GetIO().MouseDrawCursor = (c.type > 0); // only redraw non-arrow cursor
#endif
ImGui::SetMouseCursor(c.type);
if ( !c.info.empty()) {
float d = 0.5f * ImGui::GetFrameHeight() ;
ImVec2 window_pos = ImVec2( mousepos.x - d, mousepos.y - d );
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always);
ImGui::SetNextWindowBgAlpha(0.75f); // Transparent background
if (ImGui::Begin("MouseInfoContext", NULL, ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
{
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
ImGui::Text(" %s", c.info.c_str());
ImGui::PopFont();
ImGui::End();
}
}
}