Plugin properties rework.
This commit is contained in:
Vendored
+1
-1
@@ -22,7 +22,7 @@
|
|||||||
// Resolved by CMake Tools:
|
// Resolved by CMake Tools:
|
||||||
"program": "${command:cmake.launchTargetPath}",
|
"program": "${command:cmake.launchTargetPath}",
|
||||||
"args": [
|
"args": [
|
||||||
"[ProfileThumbnails]"
|
"--gplot-downloads"
|
||||||
],
|
],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
|
|||||||
+4
-2
@@ -153,12 +153,13 @@ bool Pedalboard::SetControlValue(int64_t pedalItemId, const std::string &symbol,
|
|||||||
return item->SetControlValue(symbol,value);
|
return item->SetControlValue(symbol,value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pedalboard::SetItemTitle(int64_t pedalItemId, const std::string &title)
|
bool Pedalboard::SetItemTitle(int64_t pedalItemId, const std::string &title, const std::string&iconColor)
|
||||||
{
|
{
|
||||||
PedalboardItem*item = GetItem(pedalItemId);
|
PedalboardItem*item = GetItem(pedalItemId);
|
||||||
if (!item) return false;
|
if (!item) return false;
|
||||||
if (item->title() == title) return false; // no change.
|
if (item->title() == title && item->iconColor() == iconColor) return false; // no change.
|
||||||
item->title(title);
|
item->title(title);
|
||||||
|
item->iconColor(iconColor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,6 +533,7 @@ JSON_MAP_BEGIN(PedalboardItem)
|
|||||||
JSON_MAP_REFERENCE(PedalboardItem,pathProperties)
|
JSON_MAP_REFERENCE(PedalboardItem,pathProperties)
|
||||||
JSON_MAP_REFERENCE(PedalboardItem,title)
|
JSON_MAP_REFERENCE(PedalboardItem,title)
|
||||||
JSON_MAP_REFERENCE(PedalboardItem,useModUi)
|
JSON_MAP_REFERENCE(PedalboardItem,useModUi)
|
||||||
|
JSON_MAP_REFERENCE(PedalboardItem,iconColor)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -106,6 +106,7 @@ public:
|
|||||||
std::map<std::string,std::string> pathProperties_;
|
std::map<std::string,std::string> pathProperties_;
|
||||||
std::string title_;
|
std::string title_;
|
||||||
bool useModUi_ = false;
|
bool useModUi_ = false;
|
||||||
|
std::string iconColor_;
|
||||||
|
|
||||||
// non persistent state.
|
// non persistent state.
|
||||||
PropertyMap patchProperties;
|
PropertyMap patchProperties;
|
||||||
@@ -135,6 +136,7 @@ public:
|
|||||||
GETTER_SETTER(stateUpdateCount)
|
GETTER_SETTER(stateUpdateCount)
|
||||||
GETTER_SETTER_REF(lv2State)
|
GETTER_SETTER_REF(lv2State)
|
||||||
GETTER_SETTER_REF(title)
|
GETTER_SETTER_REF(title)
|
||||||
|
GETTER_SETTER_REF(iconColor)
|
||||||
GETTER_SETTER(useModUi)
|
GETTER_SETTER(useModUi)
|
||||||
|
|
||||||
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
|
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
|
||||||
@@ -232,7 +234,7 @@ public:
|
|||||||
static constexpr int64_t INPUT_VOLUME_ID = -2; // synthetic PedalboardItem for input volume.
|
static constexpr int64_t INPUT_VOLUME_ID = -2; // synthetic PedalboardItem for input volume.
|
||||||
static constexpr int64_t OUTPUT_VOLUME_ID = -3; // synthetic PedalboardItem for output volume.
|
static constexpr int64_t OUTPUT_VOLUME_ID = -3; // synthetic PedalboardItem for output volume.
|
||||||
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
|
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
|
||||||
bool SetItemTitle(int64_t pedalItemId, const std::string &title);
|
bool SetItemTitle(int64_t pedalItemId, const std::string &title, const std::string&iconColor);
|
||||||
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
|
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
|
||||||
bool SetItemUseModUi(int64_t pedalItemId, bool enabled);
|
bool SetItemUseModUi(int64_t pedalItemId, bool enabled);
|
||||||
void SetCurrentSnapshotModified(bool modified);
|
void SetCurrentSnapshotModified(bool modified);
|
||||||
|
|||||||
@@ -3226,10 +3226,10 @@ void PiPedalModel::MoveAudioFile(
|
|||||||
AudioDirectoryInfo::Ptr dir = AudioDirectoryInfo::Create(directory);
|
AudioDirectoryInfo::Ptr dir = AudioDirectoryInfo::Create(directory);
|
||||||
dir->MoveAudioFile(directory, fromPosition, toPosition);
|
dir->MoveAudioFile(directory, fromPosition, toPosition);
|
||||||
}
|
}
|
||||||
void PiPedalModel::SetPedalboardItemTitle(int64_t instanceId, const std::string &title)
|
void PiPedalModel::SetPedalboardItemTitle(int64_t instanceId, const std::string &title, const std::string &colorKey)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
if (!this->pedalboard.SetItemTitle(instanceId, title))
|
if (!this->pedalboard.SetItemTitle(instanceId, title, colorKey))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -486,7 +486,7 @@ namespace pipedal
|
|||||||
int32_t from,
|
int32_t from,
|
||||||
int32_t to);
|
int32_t to);
|
||||||
|
|
||||||
void SetPedalboardItemTitle(int64_t instanceId, const std::string &title);
|
void SetPedalboardItemTitle(int64_t instanceId, const std::string &title, const std::string&iconColor);
|
||||||
|
|
||||||
void SetTone3000Auth(const std::string &apiKey);
|
void SetTone3000Auth(const std::string &apiKey);
|
||||||
bool HasTone3000Auth() const;
|
bool HasTone3000Auth() const;
|
||||||
|
|||||||
@@ -179,12 +179,14 @@ class SetPedalboardItemTitleBody
|
|||||||
public:
|
public:
|
||||||
uint64_t instanceId_;
|
uint64_t instanceId_;
|
||||||
std::string title_;
|
std::string title_;
|
||||||
|
std::string colorKey_;
|
||||||
DECLARE_JSON_MAP(SetPedalboardItemTitleBody);
|
DECLARE_JSON_MAP(SetPedalboardItemTitleBody);
|
||||||
};
|
};
|
||||||
|
|
||||||
JSON_MAP_BEGIN(SetPedalboardItemTitleBody)
|
JSON_MAP_BEGIN(SetPedalboardItemTitleBody)
|
||||||
JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, instanceId)
|
JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, instanceId)
|
||||||
JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, title)
|
JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, title)
|
||||||
|
JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, colorKey)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
|
||||||
@@ -1569,7 +1571,7 @@ public:
|
|||||||
{
|
{
|
||||||
SetPedalboardItemTitleBody body;
|
SetPedalboardItemTitleBody body;
|
||||||
pReader->read(&body);
|
pReader->read(&body);
|
||||||
model.SetPedalboardItemTitle(body.instanceId_, body.title_);
|
model.SetPedalboardItemTitle(body.instanceId_, body.title_, body.colorKey_);
|
||||||
}
|
}
|
||||||
else if (message == "getPatchProperty")
|
else if (message == "getPatchProperty")
|
||||||
{
|
{
|
||||||
|
|||||||
+129
-15
@@ -43,10 +43,11 @@ std::string psystem(const std::string &command)
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("popen() failed!");
|
throw std::runtime_error("popen() failed!");
|
||||||
}
|
}
|
||||||
Finally ff{[pipe]() {
|
Finally ff{[pipe]()
|
||||||
pclose(pipe);
|
{
|
||||||
}};
|
pclose(pipe);
|
||||||
while (fgets(buffer.data(), buffer.size()-1, pipe) != nullptr)
|
}};
|
||||||
|
while (fgets(buffer.data(), buffer.size() - 1, pipe) != nullptr)
|
||||||
{
|
{
|
||||||
result += buffer.data();
|
result += buffer.data();
|
||||||
}
|
}
|
||||||
@@ -68,7 +69,8 @@ void SignPackage()
|
|||||||
{
|
{
|
||||||
packagePath = SS("build/pipedal_" << PROJECT_VER << "_amd64.deb");
|
packagePath = SS("build/pipedal_" << PROJECT_VER << "_amd64.deb");
|
||||||
packagePath = fs::absolute(packagePath);
|
packagePath = fs::absolute(packagePath);
|
||||||
if (!fs::exists(packagePath)) {
|
if (!fs::exists(packagePath))
|
||||||
|
{
|
||||||
packagePath = SS("build/pipedal_" << PROJECT_VER << "_*.deb");
|
packagePath = SS("build/pipedal_" << PROJECT_VER << "_*.deb");
|
||||||
throw std::runtime_error(SS("File does not exist: " << packagePath));
|
throw std::runtime_error(SS("File does not exist: " << packagePath));
|
||||||
}
|
}
|
||||||
@@ -82,8 +84,8 @@ void SignPackage()
|
|||||||
|
|
||||||
std::string signCmd =
|
std::string signCmd =
|
||||||
SS("/usr/bin/gpg --pinentry-mode loopback --yes --default-key " << UPDATE_GPG_FINGERPRINT2
|
SS("/usr/bin/gpg --pinentry-mode loopback --yes --default-key " << UPDATE_GPG_FINGERPRINT2
|
||||||
<< " --armor --output " << packagePath << ".asc"
|
<< " --armor --output " << packagePath << ".asc"
|
||||||
<< " --detach-sign " << packagePath.c_str());
|
<< " --detach-sign " << packagePath.c_str());
|
||||||
int result = system(signCmd.c_str());
|
int result = system(signCmd.c_str());
|
||||||
if (result != EXIT_SUCCESS)
|
if (result != EXIT_SUCCESS)
|
||||||
{
|
{
|
||||||
@@ -226,6 +228,8 @@ public:
|
|||||||
bool draft = false, prerelease = false;
|
bool draft = false, prerelease = false;
|
||||||
std::vector<DownloadCountAsset> assets;
|
std::vector<DownloadCountAsset> assets;
|
||||||
std::string published_at;
|
std::string published_at;
|
||||||
|
double release_downloads = 0;
|
||||||
|
double average_daily_downloads = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string timePointToISO8601(const std::chrono::system_clock::time_point &tp)
|
static std::string timePointToISO8601(const std::chrono::system_clock::time_point &tp)
|
||||||
@@ -233,11 +237,33 @@ static std::string timePointToISO8601(const std::chrono::system_clock::time_poin
|
|||||||
auto tt = std::chrono::system_clock::to_time_t(tp);
|
auto tt = std::chrono::system_clock::to_time_t(tp);
|
||||||
std::tm tm = *std::gmtime(&tt);
|
std::tm tm = *std::gmtime(&tt);
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::put_time(&tm,"%Y-%m-%dT%H:%M:%S") << "Z";
|
ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S") << "Z";
|
||||||
return ss.str();
|
return ss.str();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::chrono::system_clock::time_point iso8601ToTimePoint(const std::string&date)
|
||||||
|
{
|
||||||
|
std::istringstream ss(date);
|
||||||
|
std::tm tm = {};
|
||||||
|
char zulu;
|
||||||
|
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S") >> zulu;
|
||||||
|
if (ss.fail()) {
|
||||||
|
throw std::runtime_error("Failed to parse ISO8601 date: " + date);
|
||||||
|
}
|
||||||
|
return std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool endsWith(const std::string &value, const std::string &end)
|
||||||
|
{
|
||||||
|
if (end.length() > value.length())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value.compare(value.length() - end.length(), end.length(), end) == 0;
|
||||||
|
}
|
||||||
void GetDownloadCounts(bool gplotDownloads)
|
void GetDownloadCounts(bool gplotDownloads)
|
||||||
{
|
{
|
||||||
// error handling and temporary file use is different enough that it justifies
|
// error handling and temporary file use is different enough that it justifies
|
||||||
@@ -321,15 +347,65 @@ void GetDownloadCounts(bool gplotDownloads)
|
|||||||
return left.published_at < right.published_at; // date ascending
|
return left.published_at < right.published_at; // date ascending
|
||||||
});
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i < releases.size()-1; ++i)
|
uint64_t cumulativeCount = 0;
|
||||||
|
|
||||||
|
for (auto i = releases.begin(); i != releases.end(); /**/)
|
||||||
{
|
{
|
||||||
releases[i].published_at = releases[i+1].published_at;
|
if (i->name.find("Experimental") != string::npos
|
||||||
|
|| i->name.find("Retracted") != string::npos
|
||||||
|
|| i->name.find("Testing") != string::npos
|
||||||
|
)
|
||||||
|
{
|
||||||
|
i = releases.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &release : releases)
|
||||||
|
{
|
||||||
|
size_t releaseDownloads = 0;
|
||||||
|
for (const auto &asset : release.assets)
|
||||||
|
{
|
||||||
|
if (asset.name.ends_with(".deb"))
|
||||||
|
{
|
||||||
|
releaseDownloads += asset.downloads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release.release_downloads = releaseDownloads;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t i = 0; i < releases.size(); ++i)
|
||||||
|
{
|
||||||
|
std::chrono::system_clock::time_point nextTime;
|
||||||
|
if (i == releases.size()-1)
|
||||||
|
{
|
||||||
|
nextTime = std::chrono::system_clock::now();
|
||||||
|
} else {
|
||||||
|
nextTime = iso8601ToTimePoint(releases.at(i+1).published_at);
|
||||||
|
}
|
||||||
|
auto thisTime = iso8601ToTimePoint(releases.at(i).published_at);
|
||||||
|
|
||||||
|
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(nextTime-thisTime).count();
|
||||||
|
auto days = seconds/(60.0*60*24);
|
||||||
|
|
||||||
|
|
||||||
|
releases[i].average_daily_downloads = releases[i].release_downloads/
|
||||||
|
(days);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < releases.size() - 1; ++i)
|
||||||
|
{
|
||||||
|
releases[i].published_at = releases[i + 1].published_at;
|
||||||
}
|
}
|
||||||
if (releases.size() >= 1)
|
if (releases.size() >= 1)
|
||||||
{
|
{
|
||||||
releases[releases.size()-1].published_at = timePointToISO8601(std::chrono::system_clock::now());
|
releases[releases.size() - 1].published_at = timePointToISO8601(std::chrono::system_clock::now());
|
||||||
}
|
}
|
||||||
uint64_t cumulativeCount = 0;
|
|
||||||
for (const auto &release : releases)
|
for (const auto &release : releases)
|
||||||
{
|
{
|
||||||
for (const auto &asset : release.assets)
|
for (const auto &asset : release.assets)
|
||||||
@@ -339,7 +415,10 @@ void GetDownloadCounts(bool gplotDownloads)
|
|||||||
cumulativeCount += asset.downloads;
|
cumulativeCount += asset.downloads;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cout << release.published_at << " " << cumulativeCount << endl;
|
cout << release.published_at << " " << cumulativeCount
|
||||||
|
<< " " << release.average_daily_downloads
|
||||||
|
<< " " << "\"" << release.name << "\""
|
||||||
|
<< endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -351,14 +430,49 @@ void GetDownloadCounts(bool gplotDownloads)
|
|||||||
{
|
{
|
||||||
return left.published_at > right.published_at; // latest date first.
|
return left.published_at > right.published_at; // latest date first.
|
||||||
});
|
});
|
||||||
|
|
||||||
|
size_t total = 0;
|
||||||
|
size_t amdTotal = 0;
|
||||||
|
size_t amdAscTotal = 0;
|
||||||
|
size_t aarch64Total = 0;
|
||||||
|
size_t aarch64AscTotal = 0;
|
||||||
|
size_t otherTotal = 0;
|
||||||
|
|
||||||
for (const auto &release : releases)
|
for (const auto &release : releases)
|
||||||
{
|
{
|
||||||
cout << release.name << endl;
|
cout << release.name << " " << release.published_at << endl;
|
||||||
for (const auto &asset : release.assets)
|
for (const auto &asset : release.assets)
|
||||||
{
|
{
|
||||||
|
total += asset.downloads;
|
||||||
|
if (endsWith(asset.name, "arm64.deb"))
|
||||||
|
{
|
||||||
|
aarch64Total += asset.downloads;
|
||||||
|
}
|
||||||
|
else if (endsWith(asset.name, "amd64.deb"))
|
||||||
|
{
|
||||||
|
amdTotal += asset.downloads;
|
||||||
|
}
|
||||||
|
else if (endsWith(asset.name, "arm64.deb.asc"))
|
||||||
|
{
|
||||||
|
aarch64AscTotal += asset.downloads;
|
||||||
|
}
|
||||||
|
else if (endsWith(asset.name, "amd64.deb.asc"))
|
||||||
|
{
|
||||||
|
amdAscTotal += asset.downloads;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
otherTotal += asset.downloads;
|
||||||
|
}
|
||||||
cout << " " << asset.name << ": " << asset.downloads << endl;
|
cout << " " << asset.name << ": " << asset.downloads << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cout << endl
|
||||||
|
<< "Total: " << total << endl;
|
||||||
|
|
||||||
|
cout << "arm64:" << aarch64Total << " asc: " << aarch64AscTotal << endl;
|
||||||
|
cout << "amd64:" << amdTotal << " asc: " << amdAscTotal << endl;
|
||||||
|
cout << "other:" << otherTotal << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { PiPedalStateError } from './PiPedalError';
|
|||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
|
|
||||||
|
|
||||||
const SELECT_SCALE = 1.5;
|
const SELECT_SCALE = 1.2;
|
||||||
|
|
||||||
const AUTOSCROLL_TICK_DELAY = 30;
|
const AUTOSCROLL_TICK_DELAY = 30;
|
||||||
const AUTOSCROLL_THRESHOLD = 48;
|
const AUTOSCROLL_THRESHOLD = 48;
|
||||||
@@ -58,6 +58,7 @@ export interface DraggableProps extends WithStyles<typeof styles> {
|
|||||||
|
|
||||||
children?: ReactNode | ReactNode[];
|
children?: ReactNode | ReactNode[];
|
||||||
draggable?: boolean;
|
draggable?: boolean;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -149,6 +150,7 @@ const Draggable =
|
|||||||
startClientY: number = 0;
|
startClientY: number = 0;
|
||||||
dragStarted: boolean = false;
|
dragStarted: boolean = false;
|
||||||
savedIndex: string = "";
|
savedIndex: string = "";
|
||||||
|
savedOpacity: string = "";
|
||||||
lastPointerDown: number = 0;
|
lastPointerDown: number = 0;
|
||||||
originalBounds?: DOMRect;
|
originalBounds?: DOMRect;
|
||||||
captureElement?: HTMLDivElement;
|
captureElement?: HTMLDivElement;
|
||||||
@@ -244,6 +246,8 @@ const Draggable =
|
|||||||
this.pointerId = e.pointerId;
|
this.pointerId = e.pointerId;
|
||||||
this.pointerType = e.pointerType;
|
this.pointerType = e.pointerType;
|
||||||
this.savedIndex = e.currentTarget.style.zIndex;
|
this.savedIndex = e.currentTarget.style.zIndex;
|
||||||
|
this.savedOpacity = e.currentTarget.style.opacity;
|
||||||
|
e.currentTarget.style.opacity = "0.9";
|
||||||
if (this.pointerType !== "touch") {
|
if (this.pointerType !== "touch") {
|
||||||
this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")";
|
this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")";
|
||||||
this.dragTarget.style.zIndex = "3";
|
this.dragTarget.style.zIndex = "3";
|
||||||
@@ -254,6 +258,7 @@ const Draggable =
|
|||||||
clearDragTransform(element: HTMLDivElement): void {
|
clearDragTransform(element: HTMLDivElement): void {
|
||||||
element.style.transform = "";
|
element.style.transform = "";
|
||||||
element.style.zIndex = this.savedIndex;
|
element.style.zIndex = this.savedIndex;
|
||||||
|
element.style.opacity = this.savedOpacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPointerCancel(e: PointerEvent<HTMLDivElement>) {
|
onPointerCancel(e: PointerEvent<HTMLDivElement>) {
|
||||||
@@ -486,7 +491,7 @@ const Draggable =
|
|||||||
render() {
|
render() {
|
||||||
const classes = withStyles.getClasses(this.props);
|
const classes = withStyles.getClasses(this.props);
|
||||||
return (
|
return (
|
||||||
<div className={classes.frame} style={{ transform: "" }}
|
<div className={classes.frame} style={{ transform: "", ...this.props.style }}
|
||||||
onPointerDown={this.onPointerDown}
|
onPointerDown={this.onPointerDown}
|
||||||
onPointerMove={this.onPointerMove}
|
onPointerMove={this.onPointerMove}
|
||||||
onPointerCancel={this.onPointerCancel}
|
onPointerCancel={this.onPointerCancel}
|
||||||
|
|||||||
@@ -21,13 +21,14 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import ButtonBase from '@mui/material/ButtonBase';
|
import ButtonBase from '@mui/material/ButtonBase';
|
||||||
import Menu from '@mui/material/Menu';
|
import Menu from '@mui/material/Menu';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import { getIconColorSelections,getIconColor,IconColorSelect } from './PluginIcon';
|
import PluginIcon, { getIconColorSelections, getIconColor, IconColorSelect } from './PluginIcon';
|
||||||
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
||||||
|
import { PluginType } from './Lv2Plugin';
|
||||||
|
|
||||||
|
|
||||||
export enum DropdownAlignment {
|
export enum DropdownAlignment {
|
||||||
@@ -37,6 +38,7 @@ export enum DropdownAlignment {
|
|||||||
interface ColorListDropdownButtonProps {
|
interface ColorListDropdownButtonProps {
|
||||||
colorKey: string;
|
colorKey: string;
|
||||||
colorList?: IconColorSelect[];
|
colorList?: IconColorSelect[];
|
||||||
|
pluginType?: PluginType;
|
||||||
|
|
||||||
onColorChange: (selection: IconColorSelect) => void;
|
onColorChange: (selection: IconColorSelect) => void;
|
||||||
dropdownAlignment: DropdownAlignment;
|
dropdownAlignment: DropdownAlignment;
|
||||||
@@ -71,19 +73,30 @@ const IconColorDropdownButton: React.FC<ColorListDropdownButtonProps> = (props:
|
|||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
style={{
|
style={{
|
||||||
height: 48, padding: 12, borderRadius: 18
|
height: 48, padding: 12, borderRadius: 8, margin: 4
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "center", gap: 0 }} >
|
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "center", gap: 0 }} >
|
||||||
<div style={{
|
{props.pluginType ? (
|
||||||
width: 24, height: 24,
|
<div style={{ margin: 4 }}>
|
||||||
background: selectedColor,
|
<PluginIcon
|
||||||
borderRadius: 6,
|
pluginType={props.pluginType}
|
||||||
borderColor: theme.palette.text.secondary,
|
color={selectedColor}
|
||||||
borderWidth: 1,
|
size={24}
|
||||||
borderStyle: "solid"
|
/>
|
||||||
}} />
|
</div>
|
||||||
<ArrowDropDownIcon />
|
) : (
|
||||||
|
<div style={{
|
||||||
|
width: 24, height: 24,
|
||||||
|
background: selectedColor,
|
||||||
|
borderRadius: 6,
|
||||||
|
borderColor: theme.palette.text.secondary,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderStyle: "solid"
|
||||||
|
}} />
|
||||||
|
|
||||||
|
)}
|
||||||
|
<ArrowDropDownIcon />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
@@ -93,20 +106,20 @@ const IconColorDropdownButton: React.FC<ColorListDropdownButtonProps> = (props:
|
|||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: 'bottom',
|
vertical: 'bottom',
|
||||||
horizontal: (dropdownAlignment === DropdownAlignment.SW ? 'right' : 'left')
|
horizontal: (dropdownAlignment === DropdownAlignment.SW ? 'right' : 'left')
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: (dropdownAlignment === DropdownAlignment.SW ? 'right' : 'left'),
|
horizontal: (dropdownAlignment === DropdownAlignment.SW ? 'right' : 'left'),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{display: "flex", flexFlow: "row nowrap", columnGap: 8,padding: 8}}>
|
<div style={{ display: "flex", flexFlow: "row nowrap", columnGap: 8, padding: 8 }}>
|
||||||
|
|
||||||
<MenuItem key={"default"} onClick={() => handleColorSelect({key: "default", value: undefined})}
|
<MenuItem key={"default"} onClick={() => handleColorSelect({ key: "default", value: undefined })}
|
||||||
style={{
|
style={{
|
||||||
width: 48, height: 48, borderRadius: 6, margin: 4,
|
width: 48, height: 48, borderRadius: 6, margin: 4,
|
||||||
borderStyle: "solid",
|
borderStyle: "solid",
|
||||||
borderWidth: colorKey ==="default" ? 2 : 0.25,
|
borderWidth: colorKey === "default" ? 2 : 0.25,
|
||||||
borderColor: colorKey == "default" ?
|
borderColor: colorKey == "default" ?
|
||||||
theme.palette.text.primary
|
theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { withStyles } from "tss-react/mui";
|
|||||||
import IconButtonEx from './IconButtonEx';
|
import IconButtonEx from './IconButtonEx';
|
||||||
import ButtonEx from './ButtonEx';
|
import ButtonEx from './ButtonEx';
|
||||||
import ButtonBase from '@mui/material/ButtonBase';
|
import ButtonBase from '@mui/material/ButtonBase';
|
||||||
import RenameDialog from './RenameDialog';
|
import PluginIcon, { getIconColor } from './PluginIcon';
|
||||||
|
|
||||||
import ToolTipEx from './ToolTipEx';
|
import ToolTipEx from './ToolTipEx';
|
||||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
@@ -64,9 +64,11 @@ import PipedalUiIcon from './svg/pp_ui.svg?react';
|
|||||||
import SnapshotDialog from './SnapshotDialog';
|
import SnapshotDialog from './SnapshotDialog';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import { setDefaultModGuiPreference } from './ModGuiHost';
|
import { setDefaultModGuiPreference } from './ModGuiHost';
|
||||||
|
import PluginNameDialog from './PluginNameDialog';
|
||||||
|
|
||||||
|
|
||||||
const SPLIT_CONTROLBAR_THRESHHOLD = 750;
|
const SPLIT_CONTROLBAR_THRESHHOLD = 750;
|
||||||
|
const SHOW_ICON_THRESHHOLD = 475;
|
||||||
const DISPLAY_AUTHOR_THRESHHOLD = 750;
|
const DISPLAY_AUTHOR_THRESHHOLD = 750;
|
||||||
const DISPLAY_AUTHOR_SPLIT_THRESHOLD = 500;
|
const DISPLAY_AUTHOR_SPLIT_THRESHOLD = 500;
|
||||||
const HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK = 500;
|
const HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK = 500;
|
||||||
@@ -128,6 +130,7 @@ interface MainState {
|
|||||||
splitControlBar: boolean;
|
splitControlBar: boolean;
|
||||||
displayAuthor: boolean;
|
displayAuthor: boolean;
|
||||||
horizontalScrollLayout: boolean;
|
horizontalScrollLayout: boolean;
|
||||||
|
canDisplayPluginIcon: boolean;
|
||||||
showMidiBindingsDialog: boolean;
|
showMidiBindingsDialog: boolean;
|
||||||
screenHeight: number;
|
screenHeight: number;
|
||||||
displayNameDialogOpen: boolean;
|
displayNameDialogOpen: boolean;
|
||||||
@@ -147,6 +150,9 @@ export const MainPage =
|
|||||||
getSplitToolbar() {
|
getSplitToolbar() {
|
||||||
return this.windowSize.width < SPLIT_CONTROLBAR_THRESHHOLD && this.windowSize.height >= HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK;
|
return this.windowSize.width < SPLIT_CONTROLBAR_THRESHHOLD && this.windowSize.height >= HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK;
|
||||||
}
|
}
|
||||||
|
getShowPluginIcon() {
|
||||||
|
return this.windowSize.width > SHOW_ICON_THRESHHOLD;
|
||||||
|
}
|
||||||
getDisplayAuthor() {
|
getDisplayAuthor() {
|
||||||
if (this.getSplitToolbar()) {
|
if (this.getSplitToolbar()) {
|
||||||
return this.windowSize.width >= DISPLAY_AUTHOR_SPLIT_THRESHOLD;
|
return this.windowSize.width >= DISPLAY_AUTHOR_SPLIT_THRESHOLD;
|
||||||
@@ -173,6 +179,7 @@ export const MainPage =
|
|||||||
addMenuAnchorEl: null,
|
addMenuAnchorEl: null,
|
||||||
splitControlBar: this.getSplitToolbar(),
|
splitControlBar: this.getSplitToolbar(),
|
||||||
displayAuthor: this.getDisplayAuthor(),
|
displayAuthor: this.getDisplayAuthor(),
|
||||||
|
canDisplayPluginIcon: this.getShowPluginIcon(),
|
||||||
horizontalScrollLayout: this.windowSize.height < HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK,
|
horizontalScrollLayout: this.windowSize.height < HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK,
|
||||||
showMidiBindingsDialog: false,
|
showMidiBindingsDialog: false,
|
||||||
screenHeight: this.windowSize.height,
|
screenHeight: this.windowSize.height,
|
||||||
@@ -304,6 +311,7 @@ export const MainPage =
|
|||||||
this.setState({
|
this.setState({
|
||||||
splitControlBar: this.getSplitToolbar(),
|
splitControlBar: this.getSplitToolbar(),
|
||||||
displayAuthor: this.getDisplayAuthor(),
|
displayAuthor: this.getDisplayAuthor(),
|
||||||
|
canDisplayPluginIcon: this.getShowPluginIcon(),
|
||||||
horizontalScrollLayout: this.windowSize.height < HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK,
|
horizontalScrollLayout: this.windowSize.height < HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK,
|
||||||
screenHeight: this.windowSize.height
|
screenHeight: this.windowSize.height
|
||||||
});
|
});
|
||||||
@@ -415,6 +423,7 @@ export const MainPage =
|
|||||||
return pedalboardItem.uri;
|
return pedalboardItem.uri;
|
||||||
}
|
}
|
||||||
titleBar(pedalboardItem: PedalboardItem | null, canShowModUi: boolean): React.ReactNode {
|
titleBar(pedalboardItem: PedalboardItem | null, canShowModUi: boolean): React.ReactNode {
|
||||||
|
let uiPlugin = pedalboardItem ? this.model.getUiPlugin(pedalboardItem?.uri) : null;
|
||||||
let title = "";
|
let title = "";
|
||||||
let author = "";
|
let author = "";
|
||||||
let infoPluginUri = "";
|
let infoPluginUri = "";
|
||||||
@@ -489,21 +498,45 @@ export const MainPage =
|
|||||||
this.handleEditPluginDisplayName();
|
this.handleEditPluginDisplayName();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ flex: "0 1 auto" }}>
|
<div style={{ flex: "0 1 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center", overflow: "hidden" }}>
|
||||||
<span>
|
{this.state.canDisplayPluginIcon && uiPlugin && (
|
||||||
<span className={classes.title}>{title}</span>
|
<div style={{ flex: "0 0 auto", marginRight: 8 }}>
|
||||||
{this.state.displayAuthor && (
|
<PluginIcon
|
||||||
<span className={classes.author}>{author}</span>
|
pluginType={uiPlugin.plugin_type}
|
||||||
)}
|
color={getIconColor(pedalboardItem?.iconColor ?? "")}
|
||||||
</span>
|
size={24}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div style={{ flex: "0 1 auto", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }}>
|
||||||
|
<span>
|
||||||
|
<span className={classes.title}>{title}</span>
|
||||||
|
{this.state.displayAuthor && (
|
||||||
|
<span className={classes.author}>{author}</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ButtonBase>)
|
</ButtonBase>)
|
||||||
: (
|
: (
|
||||||
<div style={{ flex: "0 1 auto", paddingLeft: 8, paddingRight: 8, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }}>
|
<div style={{ flex: "0 1 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center", overflow: "hidden" }}>
|
||||||
<span className={classes.title}>{title}</span>
|
{this.state.canDisplayPluginIcon && uiPlugin && (
|
||||||
{this.state.displayAuthor && (
|
<div style={{ flex: "0 0 auto", marginRight: 8 }}>
|
||||||
<span className={classes.author}>{author}</span>
|
<PluginIcon
|
||||||
|
pluginType={uiPlugin.plugin_type}
|
||||||
|
color={getIconColor(pedalboardItem?.iconColor ?? "")}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div style={{ flex: "0 1 auto", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }}>
|
||||||
|
<span>
|
||||||
|
<span className={classes.title}>{title}</span>
|
||||||
|
{this.state.displayAuthor && (
|
||||||
|
<span className={classes.author}>{author}</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
@@ -770,17 +803,14 @@ export const MainPage =
|
|||||||
<SnapshotDialog open={this.state.snapshotDialogOpen} onOk={() => this.onSnapshotDialogOk()} />
|
<SnapshotDialog open={this.state.snapshotDialogOpen} onOk={() => this.onSnapshotDialogOk()} />
|
||||||
)}
|
)}
|
||||||
{(this.state.displayNameDialogOpen) && (
|
{(this.state.displayNameDialogOpen) && (
|
||||||
<RenameDialog open={this.state.displayNameDialogOpen}
|
<PluginNameDialog open={this.state.displayNameDialogOpen}
|
||||||
|
pedalboardItem={pedalboardItem}
|
||||||
allowEmpty={true}
|
allowEmpty={true}
|
||||||
defaultName={pedalboardItem?.title ?? ""}
|
|
||||||
title="Plugin Display Name"
|
|
||||||
acceptActionName="OK"
|
|
||||||
onClose={() => this.setState({ displayNameDialogOpen: false })}
|
onClose={() => this.setState({ displayNameDialogOpen: false })}
|
||||||
onOk={(newName: string) => {
|
onApply={(newName: string, color: string) => {
|
||||||
if (pedalboardItem) {
|
if (pedalboardItem) {
|
||||||
this.model.setPedalboardItemTitle(pedalboardItem.instanceId, newName);
|
this.model.setPedalboardItemTitle(pedalboardItem.instanceId, newName, color);
|
||||||
}
|
}
|
||||||
this.setState({ displayNameDialogOpen: false });
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export class PedalboardItem implements Deserializable<PedalboardItem> {
|
|||||||
this.lilvPresetUri = input.lilvPresetUri;
|
this.lilvPresetUri = input.lilvPresetUri;
|
||||||
this.pathProperties = input.pathProperties;
|
this.pathProperties = input.pathProperties;
|
||||||
this.useModUi = input.useModUi ?? false;
|
this.useModUi = input.useModUi ?? false;
|
||||||
|
this.iconColor = input.iconColor??"";
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -215,6 +216,7 @@ export class PedalboardItem implements Deserializable<PedalboardItem> {
|
|||||||
lilvPresetUri: string = "";
|
lilvPresetUri: string = "";
|
||||||
pathProperties: {[Name: string]: string} = {};
|
pathProperties: {[Name: string]: string} = {};
|
||||||
useModUi: boolean = false; // true if this item should use the mod-ui.
|
useModUi: boolean = false; // true if this item should use the mod-ui.
|
||||||
|
iconColor: string = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SnapshotValue {
|
export class SnapshotValue {
|
||||||
|
|||||||
+899
-856
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@
|
|||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
import SaveIconOutline from '@mui/icons-material/Save';
|
import SaveIconOutline from '@mui/icons-material/Save';
|
||||||
import WithStyles from './WithStyles';
|
import WithStyles from './WithStyles';
|
||||||
import {createStyles} from './WithStyles';
|
import { createStyles } from './WithStyles';
|
||||||
|
|
||||||
import { withStyles } from "tss-react/mui";
|
import { withStyles } from "tss-react/mui";
|
||||||
import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel';
|
import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel';
|
||||||
@@ -41,7 +41,7 @@ import { BankIndex } from './Banks';
|
|||||||
import SnapshotEditor from './SnapshotEditor';
|
import SnapshotEditor from './SnapshotEditor';
|
||||||
import { Snapshot } from './Pedalboard';
|
import { Snapshot } from './Pedalboard';
|
||||||
import JackStatusView from './JackStatusView';
|
import JackStatusView from './JackStatusView';
|
||||||
import {IDialogStackable, popDialogStack, pushDialogStack} from './DialogStack';
|
import { IDialogStackable, popDialogStack, pushDialogStack } from './DialogStack';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
|
|
||||||
|
|
||||||
@@ -75,7 +75,8 @@ interface PerformanceViewProps extends WithStyles<typeof styles> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PerformanceViewState {
|
interface PerformanceViewState {
|
||||||
wrapSelects: boolean
|
wrapSelects: boolean;
|
||||||
|
largeAppBar: boolean;
|
||||||
presets: PresetIndex;
|
presets: PresetIndex;
|
||||||
banks: BankIndex;
|
banks: BankIndex;
|
||||||
showSnapshotEditor: boolean;
|
showSnapshotEditor: boolean;
|
||||||
@@ -86,7 +87,7 @@ interface PerformanceViewState {
|
|||||||
|
|
||||||
|
|
||||||
export const PerformanceView =
|
export const PerformanceView =
|
||||||
withStyles(
|
withStyles(
|
||||||
class extends ResizeResponsiveComponent<PerformanceViewProps, PerformanceViewState> implements IDialogStackable {
|
class extends ResizeResponsiveComponent<PerformanceViewProps, PerformanceViewState> implements IDialogStackable {
|
||||||
model: PiPedalModel;
|
model: PiPedalModel;
|
||||||
|
|
||||||
@@ -100,6 +101,7 @@ export const PerformanceView =
|
|||||||
presets: this.model.presets.get(),
|
presets: this.model.presets.get(),
|
||||||
banks: this.model.banks.get(),
|
banks: this.model.banks.get(),
|
||||||
wrapSelects: false,
|
wrapSelects: false,
|
||||||
|
largeAppBar: this.updateAppBarZoom(),
|
||||||
showSnapshotEditor: false,
|
showSnapshotEditor: false,
|
||||||
showStatusMonitor: this.model.showStatusMonitor.get(),
|
showStatusMonitor: this.model.showStatusMonitor.get(),
|
||||||
snapshotEditorIndex: 0,
|
snapshotEditorIndex: 0,
|
||||||
@@ -112,6 +114,9 @@ export const PerformanceView =
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAppBarZoom() {
|
||||||
|
return this.windowSize.width > 700;
|
||||||
|
}
|
||||||
|
|
||||||
showStatusMonitorHandler() {
|
showStatusMonitorHandler() {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -119,7 +124,7 @@ export const PerformanceView =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTag() { return "performView"}
|
getTag() { return "performView" }
|
||||||
isOpen() { return this.props.open }
|
isOpen() { return this.props.open }
|
||||||
onDialogStackClose() {
|
onDialogStackClose() {
|
||||||
this.props.onClose();
|
this.props.onClose();
|
||||||
@@ -128,15 +133,13 @@ export const PerformanceView =
|
|||||||
|
|
||||||
private hasHooks: boolean = false;
|
private hasHooks: boolean = false;
|
||||||
|
|
||||||
updateHooks() : void {
|
updateHooks(): void {
|
||||||
let wantHooks = this.mounted && this.props.open;
|
let wantHooks = this.mounted && this.props.open;
|
||||||
|
|
||||||
if (wantHooks !== this.hasHooks)
|
if (wantHooks !== this.hasHooks) {
|
||||||
{
|
|
||||||
this.hasHooks = wantHooks;
|
this.hasHooks = wantHooks;
|
||||||
|
|
||||||
if (this.hasHooks)
|
if (this.hasHooks) {
|
||||||
{
|
|
||||||
|
|
||||||
pushDialogStack(this);
|
pushDialogStack(this);
|
||||||
} else {
|
} else {
|
||||||
@@ -144,9 +147,12 @@ export const PerformanceView =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onWindowSizeChanged(width: number, height: number): void {
|
onWindowSizeChanged(width: number, height: number): void {
|
||||||
this.setState({ wrapSelects: width < 700 });
|
this.setState({
|
||||||
|
wrapSelects: width < 700,
|
||||||
|
largeAppBar: this.updateAppBarZoom(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onPresetsChanged(newValue: PresetIndex) {
|
onPresetsChanged(newValue: PresetIndex) {
|
||||||
@@ -156,7 +162,7 @@ export const PerformanceView =
|
|||||||
this.setState({ banks: this.model.banks.get() })
|
this.setState({ banks: this.model.banks.get() })
|
||||||
}
|
}
|
||||||
onPresetChangedChanged(newValue: boolean) {
|
onPresetChangedChanged(newValue: boolean) {
|
||||||
this.setState({ presetModified:newValue});
|
this.setState({ presetModified: newValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
private mounted: boolean = false;
|
private mounted: boolean = false;
|
||||||
@@ -216,28 +222,23 @@ export const PerformanceView =
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
handleSnapshotEditOk(index: number, name: string, color: string,newSnapshots: (Snapshot|null)[])
|
handleSnapshotEditOk(index: number, name: string, color: string, newSnapshots: (Snapshot | null)[]) {
|
||||||
{
|
|
||||||
// results have been sent to the server. we would perform a short-cut commit to our local state, to avoid laggy display updates.
|
// results have been sent to the server. we would perform a short-cut commit to our local state, to avoid laggy display updates.
|
||||||
// but we don't actually have that in our state.
|
// but we don't actually have that in our state.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviousBank()
|
handlePreviousBank() {
|
||||||
{
|
|
||||||
this.model.previousBank();
|
this.model.previousBank();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNextBank()
|
handleNextBank() {
|
||||||
{
|
|
||||||
this.model.nextBank();
|
this.model.nextBank();
|
||||||
}
|
}
|
||||||
handlePreviousPreset()
|
handlePreviousPreset() {
|
||||||
{
|
|
||||||
this.model.previousPreset();
|
this.model.previousPreset();
|
||||||
}
|
}
|
||||||
handleNextPreset()
|
handleNextPreset() {
|
||||||
{
|
|
||||||
this.model.nextPreset();
|
this.model.nextPreset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,54 +247,42 @@ export const PerformanceView =
|
|||||||
let wrapSelects = this.state.wrapSelects;
|
let wrapSelects = this.state.wrapSelects;
|
||||||
let presets = this.state.presets;
|
let presets = this.state.presets;
|
||||||
let banks = this.state.banks;
|
let banks = this.state.banks;
|
||||||
|
let appBarIconSize: "large" | undefined = this.state.largeAppBar ? undefined : "large";
|
||||||
return (
|
return (
|
||||||
<div className={classes.frame} style={{ overflow: "clip", height: "100%" }} >
|
<div className={classes.frame} style={{ overflow: "clip", height: "100%" }} >
|
||||||
<div className={classes.frame} style={{ overflow: "clip", height: "100%" }} >
|
<div className={classes.frame} style={{ overflow: "clip", height: "100%" }} >
|
||||||
<AppBar id="select-plugin-dialog-title"
|
<AppBar id="select-plugin-dialog-title"
|
||||||
style={{
|
style={{
|
||||||
position: "static", flex: "0 0 auto", height: wrapSelects ? 108 : 58,
|
position: "static", flex: "0 0 auto", height: undefined,
|
||||||
display: this.state.showSnapshotEditor ? "none" : undefined
|
display: this.state.showSnapshotEditor ? "none" : undefined
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{
|
{this.state.largeAppBar ? (
|
||||||
display: "flex", flexFlow: "row nowrap", alignContent: "center",
|
|
||||||
paddingTop: 3, paddingBottom: 3, paddingRight: 8, paddingLeft: 8,
|
|
||||||
alignItems: "start"
|
|
||||||
}}>
|
|
||||||
<IconButtonEx tooltip="Back" aria-label="menu" color="inherit"
|
|
||||||
onClick={() => { this.props.onClose(); }}
|
|
||||||
style={{
|
|
||||||
position: "relative",top: 3,
|
|
||||||
flex: "0 0 auto" }} >
|
|
||||||
<ArrowBackIcon />
|
|
||||||
</IconButtonEx>
|
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
flex: "1 1 1px", display: "flex",
|
display: "flex", flexFlow: "row nowrap", alignContent: "center",
|
||||||
flexFlow: wrapSelects ? "column nowrap" : "row nowrap",
|
paddingTop: 3, paddingBottom: 3, paddingRight: 8, paddingLeft: 8,
|
||||||
alignItems: wrapSelects ? "stretch" : undefined,
|
alignItems: "start", justifyContent: "space-between",
|
||||||
justifyContent: wrapSelects ? undefined : "space-evenly",
|
|
||||||
marginLeft: 8,
|
|
||||||
gap: 8
|
|
||||||
|
|
||||||
}}>
|
}}>
|
||||||
{/********* BANKS *******************/}
|
<IconButtonEx tooltip="Back" aria-label="menu" color="inherit"
|
||||||
<div style={{ flex: "1 1 1px", display: "flex", flexFlow: "row nowrap", maxWidth: 500 }}>
|
onClick={() => { this.props.onClose(); }}
|
||||||
<IconButtonEx tooltip="Previous bank"
|
style={{
|
||||||
aria-label="previous-bank"
|
position: "relative", top: 3,
|
||||||
onClick={() => { this.handlePreviousBank(); }}
|
flex: "0 0 auto"
|
||||||
size="medium"
|
}} >
|
||||||
color="inherit"
|
<ArrowBackIcon fontSize="large" />
|
||||||
style={{
|
</IconButtonEx>
|
||||||
borderTopRightRadius: "3px", borderBottomRightRadius: "3px", marginRight: 4
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ArrowBackIosIcon style={{ opacity: 0.75 }} />
|
|
||||||
</IconButtonEx>
|
|
||||||
|
|
||||||
|
{/********* BANKS *******************/}
|
||||||
|
<div style={{
|
||||||
|
flex: "1 1 auto", display: "flex", flexFlow: "row nowrap", maxWidth: 300, alignItems: "center",
|
||||||
|
}}>
|
||||||
<Select variant="standard"
|
<Select variant="standard"
|
||||||
className={classes.select}
|
className={classes.select}
|
||||||
style={{ flex: "1 1 1px", width: "100%", position: "relative", top: 0, color: "#FFFFFF" }} disabled={false}
|
style={{
|
||||||
|
flex: "1 1 1px", width: "100%", position: "relative", top: 0, color: "#FFFFFF",
|
||||||
|
fontSize: "2.0rem",
|
||||||
|
}} disabled={false}
|
||||||
displayEmpty
|
displayEmpty
|
||||||
onClose={(e) => this.handleBankSelectClose(e)}
|
onClose={(e) => this.handleBankSelectClose(e)}
|
||||||
value={banks.selectedBank === 0 ? undefined : banks.selectedBank}
|
value={banks.selectedBank === 0 ? undefined : banks.selectedBank}
|
||||||
@@ -312,17 +301,6 @@ export const PerformanceView =
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<IconButtonEx tooltip="Next bank"
|
|
||||||
aria-label="next-bank"
|
|
||||||
onClick={() => { this.handleNextBank(); }}
|
|
||||||
color="inherit"
|
|
||||||
style={{ borderTopLeftRadius: "3px", borderBottomLeftRadius: "3px", marginLeft: 4 }}
|
|
||||||
>
|
|
||||||
<ArrowForwardIosIcon style={{ opacity: 0.75 }} />
|
|
||||||
|
|
||||||
</IconButtonEx>
|
|
||||||
|
|
||||||
{/** spacer */}
|
{/** spacer */}
|
||||||
<IconButtonEx
|
<IconButtonEx
|
||||||
tooltip="Next bank"
|
tooltip="Next bank"
|
||||||
@@ -330,29 +308,31 @@ export const PerformanceView =
|
|||||||
onClick={() => { this.handleNextBank(); }}
|
onClick={() => { this.handleNextBank(); }}
|
||||||
size="medium"
|
size="medium"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
style={{visibility: "hidden"}}
|
style={{ visibility: "hidden" }}
|
||||||
>
|
>
|
||||||
<ArrowForwardIosIcon style={{ opacity: 0.75 }} />
|
<ArrowForwardIosIcon style={{ opacity: 0.75 }} fontSize="large" />
|
||||||
|
|
||||||
</IconButtonEx>
|
</IconButtonEx>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{/********* PRESETS *******************/}
|
{/********* PRESETS *******************/}
|
||||||
<div style={{ flex: "1 1 1px", display: "flex", flexFlow: "row nowrap", maxWidth: 500 }}>
|
<div style={{
|
||||||
|
flex: "2 2 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center",
|
||||||
|
|
||||||
|
}}>
|
||||||
<IconButtonEx tooltip="Previous preset"
|
<IconButtonEx tooltip="Previous preset"
|
||||||
aria-label="previous-preset"
|
aria-label="previous-preset"
|
||||||
onClick={() => { this.handlePreviousPreset(); }}
|
onClick={() => { this.handlePreviousPreset(); }}
|
||||||
color="inherit"
|
color="inherit"
|
||||||
style={{ borderTopRightRadius: "3px", borderBottomRightRadius: "3px", marginRight: 4 }}
|
style={{ borderTopRightRadius: "3px", borderBottomRightRadius: "3px", marginRight: 4 }}
|
||||||
>
|
>
|
||||||
<ArrowBackIosIcon style={{ opacity: 0.75 }} />
|
<ArrowBackIosIcon style={{ opacity: 0.75 }} fontSize={appBarIconSize} />
|
||||||
</IconButtonEx>
|
</IconButtonEx>
|
||||||
<Select variant="standard"
|
<Select variant="standard"
|
||||||
className={classes.select}
|
className={classes.select}
|
||||||
style={{
|
style={{
|
||||||
flex: "1 1 1px", width: "100%", color: "#FFFFFF"
|
flex: "1 1 1px", width: "100%", color: "#FFFFFF",
|
||||||
|
fontSize: (this.state.largeAppBar ? "2.0rem" : undefined),
|
||||||
}} disabled={false}
|
}} disabled={false}
|
||||||
displayEmpty
|
displayEmpty
|
||||||
onClose={(e) => this.handlePresetSelectClose(e)}
|
onClose={(e) => this.handlePresetSelectClose(e)}
|
||||||
@@ -367,8 +347,7 @@ export const PerformanceView =
|
|||||||
{
|
{
|
||||||
presets.presets.map((preset) => {
|
presets.presets.map((preset) => {
|
||||||
let name = preset.name;
|
let name = preset.name;
|
||||||
if (this.state.presets.selectedInstanceId === preset.instanceId && this.state.presetModified)
|
if (this.state.presets.selectedInstanceId === preset.instanceId && this.state.presetModified) {
|
||||||
{
|
|
||||||
name += "*";
|
name += "*";
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -386,7 +365,7 @@ export const PerformanceView =
|
|||||||
color="inherit"
|
color="inherit"
|
||||||
style={{ borderTopLeftRadius: "3px", borderBottomLeftRadius: "3px", marginLeft: 4 }}
|
style={{ borderTopLeftRadius: "3px", borderBottomLeftRadius: "3px", marginLeft: 4 }}
|
||||||
>
|
>
|
||||||
<ArrowForwardIosIcon style={{ opacity: 0.75 }} />
|
<ArrowForwardIosIcon style={{ opacity: 0.75 }} fontSize={appBarIconSize} />
|
||||||
</IconButtonEx>
|
</IconButtonEx>
|
||||||
|
|
||||||
<IconButtonEx tooltip="Save preset"
|
<IconButtonEx tooltip="Save preset"
|
||||||
@@ -394,16 +373,150 @@ export const PerformanceView =
|
|||||||
onClick={() => { this.model.saveCurrentPreset(); }}
|
onClick={() => { this.model.saveCurrentPreset(); }}
|
||||||
size="medium"
|
size="medium"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
style={{flexShrink: 0,visibility: this.state.presetModified? "visible": "hidden"}}
|
style={{ flexShrink: 0, visibility: this.state.presetModified ? "visible" : "hidden" }}
|
||||||
>
|
>
|
||||||
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" />
|
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" fontSize={appBarIconSize} />
|
||||||
</IconButtonEx>
|
</IconButtonEx>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
<div style={{
|
||||||
|
display: "flex", flexFlow: "row nowrap", alignContent: "center",
|
||||||
|
paddingTop: 3, paddingBottom: 3, paddingRight: 8, paddingLeft: 8,
|
||||||
|
alignItems: "start",
|
||||||
|
}}>
|
||||||
|
<IconButtonEx tooltip="Back" aria-label="menu" color="inherit"
|
||||||
|
onClick={() => { this.props.onClose(); }}
|
||||||
|
style={{
|
||||||
|
position: "relative", top: 3,
|
||||||
|
flex: "0 0 auto"
|
||||||
|
}} >
|
||||||
|
<ArrowBackIcon fontSize={appBarIconSize} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
<div style={{
|
||||||
|
flex: "1 1 1px", display: "flex",
|
||||||
|
flexFlow: wrapSelects ? "column nowrap" : "row nowrap",
|
||||||
|
alignItems: wrapSelects ? "stretch" : undefined,
|
||||||
|
justifyContent: wrapSelects ? undefined : "space-evenly",
|
||||||
|
marginLeft: 8,
|
||||||
|
gap: 8
|
||||||
|
|
||||||
|
}}>
|
||||||
|
{/********* BANKS *******************/}
|
||||||
|
<div style={{
|
||||||
|
flex: "1 1 auto", display: "flex", flexFlow: "row nowrap", maxWidth: 500,
|
||||||
|
}}>
|
||||||
|
<Select variant="standard"
|
||||||
|
className={classes.select}
|
||||||
|
style={{
|
||||||
|
flex: "1 1 1px", width: "100%", position: "relative", top: 0, color: "#FFFFFF",
|
||||||
|
fontSize: (this.state.largeAppBar ? "2.0rem" : undefined),
|
||||||
|
}} disabled={false}
|
||||||
|
displayEmpty
|
||||||
|
onClose={(e) => this.handleBankSelectClose(e)}
|
||||||
|
value={banks.selectedBank === 0 ? undefined : banks.selectedBank}
|
||||||
|
inputProps={{
|
||||||
|
classes: { icon: classes.select_icon },
|
||||||
|
'aria-label': "Select preset"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
banks.entries.map((entry) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={entry.instanceId} value={entry.instanceId} >
|
||||||
|
{entry.name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
{/** spacer */}
|
||||||
|
<IconButtonEx
|
||||||
|
tooltip="Next bank"
|
||||||
|
aria-label="next-bank"
|
||||||
|
onClick={() => { this.handleNextBank(); }}
|
||||||
|
size="medium"
|
||||||
|
color="inherit"
|
||||||
|
style={{ visibility: "hidden" }}
|
||||||
|
>
|
||||||
|
<ArrowForwardIosIcon style={{ opacity: 0.75 }} fontSize={appBarIconSize} />
|
||||||
|
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{/********* PRESETS *******************/}
|
||||||
|
<div style={{
|
||||||
|
flex: "2 2 auto", display: "flex", flexFlow: "row nowrap", maxWidth: 500,
|
||||||
|
|
||||||
|
}}>
|
||||||
|
<IconButtonEx tooltip="Previous preset"
|
||||||
|
aria-label="previous-preset"
|
||||||
|
onClick={() => { this.handlePreviousPreset(); }}
|
||||||
|
color="inherit"
|
||||||
|
style={{ borderTopRightRadius: "3px", borderBottomRightRadius: "3px", marginRight: 4 }}
|
||||||
|
>
|
||||||
|
<ArrowBackIosIcon style={{ opacity: 0.75 }} fontSize={appBarIconSize} />
|
||||||
|
</IconButtonEx>
|
||||||
|
<Select variant="standard"
|
||||||
|
className={classes.select}
|
||||||
|
style={{
|
||||||
|
flex: "1 1 1px", width: "100%", color: "#FFFFFF",
|
||||||
|
fontSize: (this.state.largeAppBar ? "2.0rem" : undefined),
|
||||||
|
}} disabled={false}
|
||||||
|
displayEmpty
|
||||||
|
onClose={(e) => this.handlePresetSelectClose(e)}
|
||||||
|
value={presets.selectedInstanceId === 0 ? '' : presets.selectedInstanceId}
|
||||||
|
|
||||||
|
inputProps={{
|
||||||
|
classes: { icon: classes.select_icon },
|
||||||
|
'aria-label': "Select preset"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
presets.presets.map((preset) => {
|
||||||
|
let name = preset.name;
|
||||||
|
if (this.state.presets.selectedInstanceId === preset.instanceId && this.state.presetModified) {
|
||||||
|
name += "*";
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<MenuItem key={preset.instanceId} value={preset.instanceId} >
|
||||||
|
{name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<IconButtonEx tooltip="Next preset"
|
||||||
|
aria-label="next-preset"
|
||||||
|
onClick={() => { this.handleNextPreset(); }}
|
||||||
|
color="inherit"
|
||||||
|
style={{ borderTopLeftRadius: "3px", borderBottomLeftRadius: "3px", marginLeft: 4 }}
|
||||||
|
>
|
||||||
|
<ArrowForwardIosIcon style={{ opacity: 0.75 }} fontSize={appBarIconSize} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
<IconButtonEx tooltip="Save preset"
|
||||||
|
aria-label="save-preset"
|
||||||
|
onClick={() => { this.model.saveCurrentPreset(); }}
|
||||||
|
size="medium"
|
||||||
|
color="inherit"
|
||||||
|
style={{ flexShrink: 0, visibility: this.state.presetModified ? "visible" : "hidden" }}
|
||||||
|
>
|
||||||
|
<SaveIconOutline style={{ opacity: 0.75 }} color="inherit" fontSize={appBarIconSize} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)}
|
||||||
</AppBar >
|
</AppBar >
|
||||||
<div style={{ flex: "1 0 auto", display: "flex", marginTop: 16,marginBottom: 20 }}>
|
<div style={{ flex: "1 0 auto", display: "flex", marginTop: 16, marginBottom: 20 }}>
|
||||||
<SnapshotPanel onEdit={(index) => { return this.handleOnEdit(index); }} />
|
<SnapshotPanel onEdit={(index) => { return this.handleOnEdit(index); }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -413,15 +526,15 @@ export const PerformanceView =
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{this.state.showSnapshotEditor && (
|
{this.state.showSnapshotEditor && (
|
||||||
<SnapshotEditor snapshotIndex={this.state.snapshotEditorIndex}
|
<SnapshotEditor snapshotIndex={this.state.snapshotEditorIndex}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
this.setState({ showSnapshotEditor: false });
|
this.setState({ showSnapshotEditor: false });
|
||||||
}}
|
}}
|
||||||
onOk={(index,name,color,newSnapshots)=>{
|
onOk={(index, name, color, newSnapshots) => {
|
||||||
this.setState({ showSnapshotEditor: false});
|
this.setState({ showSnapshotEditor: false });
|
||||||
this.handleSnapshotEditOk(index,name,color,newSnapshots);
|
this.handleSnapshotEditOk(index, name, color, newSnapshots);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div >
|
</div >
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -491,6 +491,9 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
webSocket?: PiPedalSocket;
|
webSocket?: PiPedalSocket;
|
||||||
|
|
||||||
|
|
||||||
|
static getInstance(): PiPedalModel {
|
||||||
|
return PiPedalModelFactory.getInstance();
|
||||||
|
}
|
||||||
hasTone3000Auth: ObservableProperty<boolean> = new ObservableProperty<boolean>(false);
|
hasTone3000Auth: ObservableProperty<boolean> = new ObservableProperty<boolean>(false);
|
||||||
|
|
||||||
canKeepScreenOn: boolean = false;
|
canKeepScreenOn: boolean = false;
|
||||||
@@ -3376,9 +3379,9 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
this.cancelOnNetworkChanging();
|
this.cancelOnNetworkChanging();
|
||||||
},
|
},
|
||||||
30 * 1000);
|
30 * 1000);
|
||||||
|
|
||||||
}
|
}
|
||||||
setPedalboardItemTitle(instanceId: number, title: string): void {
|
|
||||||
|
setPedalboardItemTitle(instanceId: number, title: string, iconColor: string): void {
|
||||||
let pedalboard = this.pedalboard.get();
|
let pedalboard = this.pedalboard.get();
|
||||||
if (!pedalboard) {
|
if (!pedalboard) {
|
||||||
throw new PiPedalStateError("Pedalboard not loaded.");
|
throw new PiPedalStateError("Pedalboard not loaded.");
|
||||||
@@ -3386,13 +3389,14 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
let newPedalboard = pedalboard.clone();
|
let newPedalboard = pedalboard.clone();
|
||||||
this.updateVst3State(newPedalboard);
|
this.updateVst3State(newPedalboard);
|
||||||
let item = newPedalboard.getItem(instanceId);
|
let item = newPedalboard.getItem(instanceId);
|
||||||
if (item.title === title) {
|
if (item.title === title && item.iconColor === iconColor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
item.title = title;
|
item.title = title;
|
||||||
|
item.iconColor = iconColor;
|
||||||
this.pedalboard.set(newPedalboard);
|
this.pedalboard.set(newPedalboard);
|
||||||
// notify the server.
|
// notify the server.
|
||||||
this.webSocket?.send("setPedalboardItemTitle", { instanceId: instanceId, title: title });
|
this.webSocket?.send("setPedalboardItemTitle", { instanceId: instanceId, title: title, colorKey: iconColor });
|
||||||
}
|
}
|
||||||
setAlsaSequencerConfiguration(alsaSequencerConfiguration: AlsaSequencerConfiguration): void {
|
setAlsaSequencerConfiguration(alsaSequencerConfiguration: AlsaSequencerConfiguration): void {
|
||||||
this.webSocket?.send("setAlsaSequencerConfiguration", alsaSequencerConfiguration);
|
this.webSocket?.send("setAlsaSequencerConfiguration", alsaSequencerConfiguration);
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ const PluginControl =
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
isValidPointer(e: PointerEvent<SVGSVGElement>): boolean {
|
isValidPointer(e: PointerEvent): boolean {
|
||||||
if (e.pointerType === "mouse") {
|
if (e.pointerType === "mouse") {
|
||||||
return e.button === 0;
|
return e.button === 0;
|
||||||
} else if (e.pointerType === "pen") {
|
} else if (e.pointerType === "pen") {
|
||||||
@@ -347,7 +347,7 @@ const PluginControl =
|
|||||||
pointerId: number = 0;
|
pointerId: number = 0;
|
||||||
pointerType: string = "";
|
pointerType: string = "";
|
||||||
|
|
||||||
isCapturedPointer(e: PointerEvent<SVGSVGElement>): boolean {
|
isCapturedPointer(e: PointerEvent): boolean {
|
||||||
return this.mouseDown
|
return this.mouseDown
|
||||||
&& e.pointerId === this.pointerId
|
&& e.pointerId === this.pointerId
|
||||||
&& e.pointerType === this.pointerType;
|
&& e.pointerType === this.pointerType;
|
||||||
@@ -361,7 +361,7 @@ const PluginControl =
|
|||||||
pointersDown: number = 0;
|
pointersDown: number = 0;
|
||||||
captureElement?: SVGSVGElement = undefined;
|
captureElement?: SVGSVGElement = undefined;
|
||||||
|
|
||||||
onPointerDown(e: PointerEvent<SVGSVGElement>): void {
|
onPointerDown(e: PointerEvent): void {
|
||||||
if (!this.mouseDown && this.isValidPointer(e)) {
|
if (!this.mouseDown && this.isValidPointer(e)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -387,7 +387,7 @@ const PluginControl =
|
|||||||
this.mouseDown = true;
|
this.mouseDown = true;
|
||||||
if (this.pointersDown === 1) {
|
if (this.pointersDown === 1) {
|
||||||
this.isTap = true;
|
this.isTap = true;
|
||||||
this.tapStartMs = Date.now();
|
this.tapStartMs = e.timeStamp;
|
||||||
} else {
|
} else {
|
||||||
this.isTap = false;
|
this.isTap = false;
|
||||||
}
|
}
|
||||||
@@ -429,7 +429,7 @@ const PluginControl =
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onPointerLostCapture(e: PointerEvent<SVGSVGElement>) {
|
onPointerLostCapture(e: PointerEvent) {
|
||||||
if (this.isCapturedPointer(e)) {
|
if (this.isCapturedPointer(e)) {
|
||||||
if (this.pointersDown !== 0) {
|
if (this.pointersDown !== 0) {
|
||||||
--this.pointersDown;
|
--this.pointersDown;
|
||||||
@@ -442,7 +442,7 @@ const PluginControl =
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRange(e: PointerEvent<SVGSVGElement>): number {
|
updateRange(e: PointerEvent): number {
|
||||||
|
|
||||||
let ultraHigh = false;
|
let ultraHigh = false;
|
||||||
let high = false;
|
let high = false;
|
||||||
@@ -486,9 +486,11 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPointerTap() {
|
onPointerTap(e: PointerEvent) {
|
||||||
let tapTime = Date.now();
|
let tapTime = e.timeStamp;
|
||||||
let dT = tapTime - this.lastTapMs;
|
let dT = tapTime - this.lastTapMs;
|
||||||
|
|
||||||
|
|
||||||
this.lastTapMs = tapTime;
|
this.lastTapMs = tapTime;
|
||||||
|
|
||||||
if (dT < 500) {
|
if (dT < 500) {
|
||||||
@@ -497,7 +499,7 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
private tapStartMs: number = 0;
|
private tapStartMs: number = 0;
|
||||||
|
|
||||||
onPointerUp(e: PointerEvent<SVGSVGElement>) {
|
onPointerUp(e: PointerEvent) {
|
||||||
|
|
||||||
if (this.isCapturedPointer(e)) {
|
if (this.isCapturedPointer(e)) {
|
||||||
if (this.pointersDown !== 0) {
|
if (this.pointersDown !== 0) {
|
||||||
@@ -514,9 +516,9 @@ const PluginControl =
|
|||||||
|
|
||||||
this.releaseCapture(e);
|
this.releaseCapture(e);
|
||||||
if (this.isTap) {
|
if (this.isTap) {
|
||||||
let ms = Date.now() - this.tapStartMs;
|
let ms = e.timeStamp - this.tapStartMs;
|
||||||
if (ms < 200) {
|
if (ms < 200) {
|
||||||
this.onPointerTap();
|
this.onPointerTap(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// prevent click from firing on other elements
|
// prevent click from firing on other elements
|
||||||
@@ -531,7 +533,7 @@ const PluginControl =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseCapture(e: PointerEvent<SVGSVGElement>) {
|
releaseCapture(e: PointerEvent) {
|
||||||
let img = this.imgRef.current;
|
let img = this.imgRef.current;
|
||||||
|
|
||||||
if (img && img.style) {
|
if (img && img.style) {
|
||||||
@@ -556,7 +558,7 @@ const PluginControl =
|
|||||||
clickSlop() {
|
clickSlop() {
|
||||||
return 5; // maybe larger on touch devices.
|
return 5; // maybe larger on touch devices.
|
||||||
}
|
}
|
||||||
onPointerMove(e: PointerEvent<SVGSVGElement>): void {
|
onPointerMove(e: PointerEvent): void {
|
||||||
if (this.isCapturedPointer(e)) {
|
if (this.isCapturedPointer(e)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let dRange = this.updateRange(e)
|
let dRange = this.updateRange(e)
|
||||||
@@ -1138,7 +1140,7 @@ const PluginControl =
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
isSamePointer(PointerEvent<SVGSVGElement> e): boolean
|
isSamePointer(PointerEvent e): boolean
|
||||||
{
|
{
|
||||||
return e.pointerId === this.pointerId
|
return e.pointerId === this.pointerId
|
||||||
&& e.pointerType === this.pointerType;
|
&& e.pointerType === this.pointerType;
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import WithStyles from './WithStyles';
|
|||||||
import {createStyles} from './WithStyles';
|
import {createStyles} from './WithStyles';
|
||||||
|
|
||||||
import { withStyles } from "tss-react/mui";
|
import { withStyles } from "tss-react/mui";
|
||||||
import { PiPedalModelFactory } from "./PiPedalModel";
|
|
||||||
import { PluginType } from './Lv2Plugin';
|
import { PluginType } from './Lv2Plugin';
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +66,7 @@ import FxEmptyIcon from './svg/fx_empty.svg?react';
|
|||||||
import FxTerminalIcon from './svg/fx_terminal.svg?react';
|
import FxTerminalIcon from './svg/fx_terminal.svg?react';
|
||||||
|
|
||||||
import { isDarkMode } from './DarkMode';
|
import { isDarkMode } from './DarkMode';
|
||||||
|
import { PiPedalModel } from './PiPedalModel';
|
||||||
|
|
||||||
export interface IconColorSelect {
|
export interface IconColorSelect {
|
||||||
key: string,
|
key: string,
|
||||||
@@ -80,21 +80,65 @@ export const getIconColorSelections = (darkMode?: boolean): IconColorSelect[] =>
|
|||||||
let l: number;
|
let l: number;
|
||||||
let c: number;
|
let c: number;
|
||||||
if (darkMode) {
|
if (darkMode) {
|
||||||
l = 0.8;
|
l = 0.8
|
||||||
c = 0.25;
|
c = 0.25;
|
||||||
} else {
|
} else {
|
||||||
l = 0.5;
|
l = 0.5;
|
||||||
c = 0.25;
|
c = 0.20;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let angle = 0; angle < 360; angle += 22.5) {
|
for (let angle = 0; angle < 360; angle += 22.5) {
|
||||||
result.push(
|
if (angle === 270-22.5)
|
||||||
{
|
{
|
||||||
key: "oklch" + Math.round(angle*10).toString(),
|
// for some reason, this one is useless. Patch in a more useful light blue. :-/
|
||||||
value: ("oklch(" + l.toString() + " " + c.toString() + " " + (angle).toString() + ")")
|
let a2 = 270;
|
||||||
|
let l2: number;
|
||||||
|
let c2: number;
|
||||||
|
if (darkMode) {
|
||||||
|
l2 = 0.9
|
||||||
|
c2 = 0.25;
|
||||||
|
} else {
|
||||||
|
l2 = 0.7;
|
||||||
|
c2 = 0.20;
|
||||||
}
|
}
|
||||||
);
|
result.push(
|
||||||
|
{
|
||||||
|
key: "oklch" + Math.round(angle*10).toString(),
|
||||||
|
value: ("oklch(" + l2.toString() + " " + c2.toString() + " " + (a2).toString() + ")")
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
} else if (angle === 270-45)
|
||||||
|
{
|
||||||
|
// for some reason, this one is useless. Patch in a more useful light blue. :-/
|
||||||
|
let a2 = 180;
|
||||||
|
let l2: number;
|
||||||
|
let c2: number;
|
||||||
|
if (darkMode) {
|
||||||
|
l2 = 0.95
|
||||||
|
c2 = 0.25;
|
||||||
|
} else {
|
||||||
|
l2 = 0.75;
|
||||||
|
c2 = 0.20;
|
||||||
|
}
|
||||||
|
result.push(
|
||||||
|
{
|
||||||
|
key: "oklch" + Math.round(angle*10).toString(),
|
||||||
|
value: ("oklch(" + l2.toString() + " " + c2.toString() + " " + (a2).toString() + ")")
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
result.push(
|
||||||
|
{
|
||||||
|
key: "oklch" + Math.round(angle*10).toString(),
|
||||||
|
value: ("oklch(" + l.toString() + " " + c.toString() + " " + (angle).toString() + ")")
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@@ -317,7 +361,7 @@ export function SelectBaseIcon(plugin_type: PluginType): string {
|
|||||||
|
|
||||||
export function SelectIconUri(plugin_type: PluginType) {
|
export function SelectIconUri(plugin_type: PluginType) {
|
||||||
let icon = SelectBaseIcon(plugin_type);
|
let icon = SelectBaseIcon(plugin_type);
|
||||||
return PiPedalModelFactory.getInstance().svgImgUrl(icon);
|
return PiPedalModel.getInstance().svgImgUrl(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PluginIcon = withStyles((props: PluginIconProps) => {
|
const PluginIcon = withStyles((props: PluginIconProps) => {
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright (c) Robin E. R. Davies
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER`
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import IconButtonEx from './IconButtonEx';
|
||||||
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||||
|
import IconColorDropdownButton, { DropdownAlignment } from './IconColorDropdownButton';
|
||||||
|
import InputLabel from '@mui/material/InputLabel';
|
||||||
|
import DialogEx from './DialogEx';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import DialogTitle from '@mui/material/DialogContent';
|
||||||
|
import { nullCast } from './Utility';
|
||||||
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
|
import { PiPedalModel } from './PiPedalModel';
|
||||||
|
//import TextFieldEx from './TextFieldEx';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import { PedalboardItem } from './Pedalboard';
|
||||||
|
|
||||||
|
|
||||||
|
export interface PluginNameDialogProps {
|
||||||
|
open: boolean,
|
||||||
|
pedalboardItem: PedalboardItem | null,
|
||||||
|
allowEmpty?: boolean,
|
||||||
|
label?: string,
|
||||||
|
onApply: (text: string, color: string) => void,
|
||||||
|
onClose: () => void
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface PluginNameDialogState {
|
||||||
|
iconColor: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isTouchUi() {
|
||||||
|
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||||
|
}
|
||||||
|
export default class PluginNameDialog extends ResizeResponsiveComponent<PluginNameDialogProps, PluginNameDialogState> {
|
||||||
|
|
||||||
|
refText: React.RefObject<HTMLInputElement | null>;
|
||||||
|
|
||||||
|
constructor(props: PluginNameDialogProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
iconColor: this.props.pedalboardItem?.iconColor ?? ""
|
||||||
|
};
|
||||||
|
this.refText = React.createRef<HTMLInputElement>();
|
||||||
|
}
|
||||||
|
mounted: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onWindowSizeChanged(width: number, height: number): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
super.componentDidMount();
|
||||||
|
this.mounted = true;
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
super.componentWillUnmount();
|
||||||
|
this.mounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
}
|
||||||
|
checkForIllegalCharacters(filename: string) {
|
||||||
|
if (filename.indexOf('/') !== -1) {
|
||||||
|
throw new Error("Illegal character: '/'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let props = this.props;
|
||||||
|
let { open, onClose, onApply } = props;
|
||||||
|
let model = PiPedalModel.getInstance();
|
||||||
|
|
||||||
|
|
||||||
|
if (!open) return null;
|
||||||
|
let pedalboardItem = props.pedalboardItem;
|
||||||
|
if (!pedalboardItem) return null;
|
||||||
|
|
||||||
|
let uiPlugin = model.getUiPlugin(pedalboardItem.uri);
|
||||||
|
if (uiPlugin === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let defaultName = props.pedalboardItem?.title ?? "";
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
let text = nullCast(this.refText.current).value;
|
||||||
|
text = text.trim();
|
||||||
|
try {
|
||||||
|
this.checkForIllegalCharacters(text);
|
||||||
|
} catch (e: any) {
|
||||||
|
let model: PiPedalModel = PiPedalModel.getInstance();
|
||||||
|
model.showAlert(e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (text.length === 0 && props.allowEmpty !== true) return;
|
||||||
|
onApply(text, this.state.iconColor);
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||||
|
// 'keypress' event misbehaves on mobile so we track 'Enter' key via 'keydown' event
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
handleOk();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DialogEx tag="nameDialog" open={open} maxWidth="md" onClose={handleClose} aria-labelledby="Rename-dialog-title"
|
||||||
|
style={{ userSelect: "none" }}
|
||||||
|
onEnterKey={() => { }}
|
||||||
|
>
|
||||||
|
<DialogTitle style={{paddingBottom: 0, paddingTop: 8}}>
|
||||||
|
<div style={{display: "flex",flexFlow: "row nowrap", alignItems: "center"}} >
|
||||||
|
<IconButtonEx tooltip="Back" edge="start" color="inherit" onClick={() => onClose()} aria-label="back"
|
||||||
|
>
|
||||||
|
<ArrowBackIcon />
|
||||||
|
</IconButtonEx>
|
||||||
|
<Typography noWrap variant="h6" >
|
||||||
|
Plugin Properties
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent >
|
||||||
|
<div style={{ display: "flex", flexFlow: "column nowrap", alignItems: "start", gap: 8, marginBottom: 8 }}>
|
||||||
|
<TextField
|
||||||
|
style={{ flex: "1 1 auto", width: 240 }}
|
||||||
|
autoFocus={!isTouchUi()}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
variant="standard"
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
onChange={(e) => { this.props.onApply(e.target.value.toString(), this.state.iconColor) }}
|
||||||
|
autoCapitalize="off"
|
||||||
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
style: { scrollMargin: 24 }
|
||||||
|
},
|
||||||
|
inputLabel: {
|
||||||
|
shrink: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
id="name"
|
||||||
|
type="text"
|
||||||
|
label="Plugin Display name"
|
||||||
|
placeholder={uiPlugin?.label ?? ""}
|
||||||
|
defaultValue={defaultName}
|
||||||
|
inputRef={this.refText}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<InputLabel style={{ fontSize: "0.75rem", fontWeight: 400, marginTop: 16 }}>Icon color</InputLabel>
|
||||||
|
<IconColorDropdownButton colorKey={this.state.iconColor}
|
||||||
|
onColorChange={(selection) => {
|
||||||
|
if (this.props.pedalboardItem) {
|
||||||
|
this.setState({ iconColor: selection.key });
|
||||||
|
onApply(this.props.pedalboardItem.title, selection.key);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
pluginType={uiPlugin.plugin_type}
|
||||||
|
dropdownAlignment={DropdownAlignment.SE}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</DialogEx>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2022 Robin Davies
|
// Copyright (c) Robin E. R. Davies
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
// this software and associated documentation files (the "Software"), to deal in
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ interface HoverProps extends WithStyles<typeof styles> {
|
|||||||
selected: boolean;
|
selected: boolean;
|
||||||
showHover?: boolean;
|
showHover?: boolean;
|
||||||
borderRadius?: number;
|
borderRadius?: number;
|
||||||
|
clipChildren?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user