mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-18 00:12:09 +00:00
Add right-click menu to reorder or unpin favs
Right-clicking a fav tile now opens a small context menu with Move left, Move right, and Unpin, instead of relying on drag-only reorder. - speeddial.js: oncontextmenu handler, the menu build/position/dismiss logic, and an Escape-key close. - style.css: .ctx-menu/.ctx-item styling. - ActionRegistry: reorder_favourites persists the new bar order to AppConfig. - SpeedDialDialog: routes the new reorder_favourites command from the page.
This commit is contained in:
@@ -273,10 +273,65 @@ function renderFav() {
|
||||
tile.title = a.title;
|
||||
tile.setAttribute("aria-label", actionLabel(a, ACTIONS));
|
||||
tile.onclick = function () { sel = { zone: "fav", i: i }; run(a); };
|
||||
tile.oncontextmenu = function (ev) {
|
||||
ev.preventDefault();
|
||||
// why: selecting shows the eyebrow, which grows the launcher - resize so the popup
|
||||
// isn't clipped (mirrors arrow-nav). requestResize no-ops when height is unchanged.
|
||||
sel = { zone: "fav", i: i }; render({ resize: true });
|
||||
showFavMenu(ev.clientX, ev.clientY, id);
|
||||
};
|
||||
favEl.appendChild(tile);
|
||||
});
|
||||
}
|
||||
|
||||
// ---- favourite context menu (right-click a tile) -----------------------------
|
||||
var favMenuEl = null;
|
||||
|
||||
function hideFavMenu() { if (favMenuEl) favMenuEl.hidden = true; }
|
||||
|
||||
function addFavMenuItem(label, enabled, fn) {
|
||||
var item = document.createElement("button");
|
||||
item.className = "ctx-item";
|
||||
item.textContent = label;
|
||||
item.disabled = !enabled;
|
||||
item.onclick = function () { hideFavMenu(); fn(); };
|
||||
favMenuEl.appendChild(item);
|
||||
}
|
||||
|
||||
// One reused menu node (Move left/right + Unpin), positioned at the cursor and clamped
|
||||
// to the viewport. Native browser context menus can't add items, so we roll our own tiny one.
|
||||
function showFavMenu(x, y, id) {
|
||||
if (!favMenuEl) {
|
||||
favMenuEl = document.createElement("div");
|
||||
favMenuEl.className = "ctx-menu";
|
||||
document.body.appendChild(favMenuEl);
|
||||
}
|
||||
favMenuEl.innerHTML = "";
|
||||
var favs = currentVisibleFavs();
|
||||
var vi = favs.indexOf(id);
|
||||
addFavMenuItem("Move left", vi > 0, function () { moveFav(id, -1); });
|
||||
addFavMenuItem("Move right", vi >= 0 && vi < favs.length - 1, function () { moveFav(id, 1); });
|
||||
addFavMenuItem("Unpin", true, function () { toggleFav(id); });
|
||||
favMenuEl.hidden = false;
|
||||
favMenuEl.style.left = Math.max(0, Math.min(x, window.innerWidth - favMenuEl.offsetWidth - 4)) + "px";
|
||||
favMenuEl.style.top = Math.max(0, Math.min(y, window.innerHeight - favMenuEl.offsetHeight - 4)) + "px";
|
||||
}
|
||||
|
||||
// Swap a favourite with its visible neighbour (dir -1/+1) and persist the new order. Swapping
|
||||
// by id inside FAVS (not the visible slice) keeps any hidden pins (no live action) in place.
|
||||
function moveFav(id, dir) {
|
||||
var favs = currentVisibleFavs();
|
||||
var vi = favs.indexOf(id);
|
||||
var ni = vi + dir;
|
||||
if (vi === -1 || ni < 0 || ni >= favs.length) return;
|
||||
var a = FAVS.indexOf(id), b = FAVS.indexOf(favs[ni]);
|
||||
if (a === -1 || b === -1) return;
|
||||
FAVS[a] = favs[ni]; FAVS[b] = id;
|
||||
SendMessage({ command: "reorder_favourites", ids: FAVS.slice() });
|
||||
sel = { zone: "fav", i: ni };
|
||||
render({ resize: true });
|
||||
}
|
||||
|
||||
// Name of the selected favourite, shown above the bar; hidden unless a fav is selected.
|
||||
function updateFavEyebrow(favs) {
|
||||
if (!eyeEl) return;
|
||||
@@ -432,7 +487,12 @@ function OnInit() {
|
||||
query = qEl.value; sel = { zone: "list", i: 0 }; syncClearButton(); render({ resize: true, resetScroll: true });
|
||||
});
|
||||
|
||||
// why: dismiss the fav context menu on any click/scroll away from it (capture scroll to catch nested scrollers).
|
||||
document.addEventListener("click", hideFavMenu);
|
||||
document.addEventListener("scroll", hideFavMenu, true);
|
||||
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (favMenuEl && !favMenuEl.hidden && e.key === "Escape") { e.preventDefault(); hideFavMenu(); return; }
|
||||
var arr = filterActions(ACTIONS, query);
|
||||
var favs = currentVisibleFavs();
|
||||
// why: Up/Down always navigate; Left/Right only navigate the fav bar. In the list zone,
|
||||
|
||||
@@ -55,6 +55,32 @@ body {
|
||||
border: 1px solid var(--speed-tile-border, #d8d8d8);
|
||||
}
|
||||
.fav-tile.sel { outline: 2px solid var(--main-color, var(--orca-accent, #009688)); outline-offset: 2px; }
|
||||
.ctx-menu {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
min-width: 120px;
|
||||
padding: 4px;
|
||||
background: var(--panel, var(--orca-bg, #fff));
|
||||
border: 1px solid var(--border, var(--orca-border, #ddd));
|
||||
border-radius: 7px;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,.18);
|
||||
}
|
||||
.ctx-menu[hidden] { display: none; }
|
||||
.ctx-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
background: transparent;
|
||||
color: var(--text, var(--orca-fg, #1b1c1e));
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ctx-item:hover { background: var(--row-hover, rgba(127,127,127,.16)); }
|
||||
.ctx-item:disabled { opacity: .4; cursor: default; }
|
||||
.ctx-item:disabled:hover { background: transparent; }
|
||||
.dial-head { flex: 0 0 auto; padding: 8px; }
|
||||
.plugin-search {
|
||||
display: flex;
|
||||
|
||||
@@ -254,6 +254,23 @@ void ActionRegistry::set_favourite(const std::string& id, bool on)
|
||||
live->favourite = on;
|
||||
}
|
||||
|
||||
void ActionRegistry::reorder_favourites(const std::vector<std::string>& ids)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
auto cur = read_string_array("favourite_actions");
|
||||
std::vector<std::string> next;
|
||||
// keep the requested order, but only ids that are actually favourites (guard a bad payload)
|
||||
for (const auto& id : ids)
|
||||
if (std::find(cur.begin(), cur.end(), id) != cur.end() &&
|
||||
std::find(next.begin(), next.end(), id) == next.end())
|
||||
next.push_back(id);
|
||||
// why: don't drop favourites the page omitted (e.g. pins with no live action hidden from the bar)
|
||||
for (const auto& id : cur)
|
||||
if (std::find(next.begin(), next.end(), id) == next.end())
|
||||
next.push_back(id);
|
||||
write_section("favourite_actions", nlohmann::json(next));
|
||||
}
|
||||
|
||||
bool ActionRegistry::should_ask(const std::string& plugin_key) const
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
// Dispatch + write-through (registry is the only thing that touches AppConfig).
|
||||
ScriptRunOutcome run(const std::string& id); // runs + bumps stats
|
||||
void set_favourite(const std::string& id, bool on);
|
||||
void reorder_favourites(const std::vector<std::string>& ids); // persist a new bar order
|
||||
|
||||
bool should_ask(const std::string& plugin_key) const; // run-confirm gate
|
||||
void suppress_ask(const std::string& plugin_key);
|
||||
|
||||
@@ -189,6 +189,12 @@ private:
|
||||
send_actions();
|
||||
}
|
||||
else if (cmd == "toggle_favourite") wxGetApp().action_registry().set_favourite(p.value("id", ""), p.value("fav", false));
|
||||
else if (cmd == "reorder_favourites") {
|
||||
std::vector<std::string> ids;
|
||||
if (p.contains("ids") && p["ids"].is_array())
|
||||
for (auto& e : p["ids"]) if (e.is_string()) ids.push_back(e.get<std::string>());
|
||||
wxGetApp().action_registry().reorder_favourites(ids);
|
||||
}
|
||||
else if (cmd == "run_action") run_action(p.value("id", ""), p.value("title", ""));
|
||||
else if (cmd == "resize") resize_to_content(json_int_or(p, "height", 0));
|
||||
else if (cmd == "close_page") Dismiss();
|
||||
|
||||
Reference in New Issue
Block a user