mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 19:01:02 +00:00
Change to a pin/bookmark icon.
Added shortcut (Ctrl/Cmd+B) to pin/unpin actions. Clear unresolved pinned actions when switching between modes. Fixed scroll-back issue when scrolling to pin action
This commit is contained in:
@@ -375,7 +375,7 @@ window.HandleStudio = function (payload) {
|
|||||||
// Favourites are at the quick-launch cap - undo the optimistic pin and flash a hint.
|
// Favourites are at the quick-launch cap - undo the optimistic pin and flash a hint.
|
||||||
var fid = payload.id;
|
var fid = payload.id;
|
||||||
if (fid && FAVS.indexOf(fid) !== -1) FAVS.splice(FAVS.indexOf(fid), 1);
|
if (fid && FAVS.indexOf(fid) !== -1) FAVS.splice(FAVS.indexOf(fid), 1);
|
||||||
render({ resize: true });
|
render({ resize: true, keepScroll: true });
|
||||||
flashHint("Favourites are full (" + (payload.limit || K_FAV_LIMIT) + " max)");
|
flashHint("Favourites are full (" + (payload.limit || K_FAV_LIMIT) + " max)");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -434,10 +434,19 @@ function markedText(className, text, match) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function starSvg(on) {
|
// A bookmark glyph: outlined when unpinned, filled when saved to favourites.
|
||||||
|
function pinSvg(on) {
|
||||||
return '<svg width="15" height="15" viewBox="0 0 24 24" fill="' + (on ? "currentColor" : "none") +
|
return '<svg width="15" height="15" viewBox="0 0 24 24" fill="' + (on ? "currentColor" : "none") +
|
||||||
'" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round">' +
|
'" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round">' +
|
||||||
'<path d="M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 17.9 6.8 20.6l1-5.8L3.5 9.7l5.9-.9z"/></svg>';
|
'<path d="M6 3.5A1.5 1.5 0 0 1 7.5 2h9A1.5 1.5 0 0 1 18 3.5V21l-6-4.2L6 21z"/></svg>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync one pin button to its favourite state. Shared by row construction and the in-place
|
||||||
|
// updatePins pass so the two can't drift.
|
||||||
|
function setPinState(pin, on) {
|
||||||
|
pin.classList.toggle("on", on);
|
||||||
|
pin.innerHTML = pinSvg(on);
|
||||||
|
pin.title = on ? "Unpin from favourites (Ctrl+B)" : "Pin to favourites (Ctrl+B)";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- render ------------------------------------------------------------------
|
// ---- render ------------------------------------------------------------------
|
||||||
@@ -551,7 +560,7 @@ function updateFavEyebrow(favs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A command/action row - used for search results, recents, and (because settings are actions now)
|
// A command/action row - used for search results, recents, and (because settings are actions now)
|
||||||
// the setting options too. All rows are pinnable, so every row carries a star.
|
// the setting options too. All rows are pinnable, so every row carries a bookmark.
|
||||||
function renderActionRow(a, i) {
|
function renderActionRow(a, i) {
|
||||||
var on = FAVS.indexOf(a.id) !== -1;
|
var on = FAVS.indexOf(a.id) !== -1;
|
||||||
var row = document.createElement("div");
|
var row = document.createElement("div");
|
||||||
@@ -591,14 +600,13 @@ function renderActionRow(a, i) {
|
|||||||
row.appendChild(tile);
|
row.appendChild(tile);
|
||||||
row.appendChild(left);
|
row.appendChild(left);
|
||||||
|
|
||||||
var star = document.createElement("button");
|
var pin = document.createElement("button");
|
||||||
star.className = "star" + (on ? " on" : "");
|
pin.className = "pin";
|
||||||
star.innerHTML = starSvg(on);
|
setPinState(pin, on);
|
||||||
star.title = on ? "Unpin from favourites" : "Pin to favourites";
|
pin.onclick = function (ev) { ev.stopPropagation(); toggleFav(a.id); };
|
||||||
star.onclick = function (ev) { ev.stopPropagation(); toggleFav(a.id); };
|
|
||||||
// why: two quick fav/unfav clicks must not dblclick-run the row
|
// why: two quick fav/unfav clicks must not dblclick-run the row
|
||||||
star.ondblclick = function (ev) { ev.stopPropagation(); };
|
pin.ondblclick = function (ev) { ev.stopPropagation(); };
|
||||||
row.appendChild(star);
|
row.appendChild(pin);
|
||||||
|
|
||||||
row.onclick = function () { sel = { zone: "list", i: i }; render({ resize: true }); };
|
row.onclick = function () { sel = { zone: "list", i: i }; render({ resize: true }); };
|
||||||
row.ondblclick = function () { sel = { zone: "list", i: i }; activateEntry(a); };
|
row.ondblclick = function () { sel = { zone: "list", i: i }; activateEntry(a); };
|
||||||
@@ -663,6 +671,21 @@ function updateSelection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync the pin buttons in place when FAVS changes but the row set doesn't (fav toggle), so a
|
||||||
|
// bookmark fills/empties without a rebuild that would reset scroll. Rows carry data-idx into the
|
||||||
|
// active list.
|
||||||
|
function updatePins(list) {
|
||||||
|
var rows = listEl ? listEl.querySelectorAll(".row") : [];
|
||||||
|
for (var i = 0; i < rows.length; i++) {
|
||||||
|
var a = list[parseInt(rows[i].getAttribute("data-idx"), 10)];
|
||||||
|
var pin = rows[i].querySelector(".pin");
|
||||||
|
if (!a || !pin) continue;
|
||||||
|
var on = FAVS.indexOf(a.id) !== -1;
|
||||||
|
if (pin.classList.contains("on") !== on)
|
||||||
|
setPinState(pin, on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderCommandsList() {
|
function renderCommandsList() {
|
||||||
var list = currentList();
|
var list = currentList();
|
||||||
var total = list.length;
|
var total = list.length;
|
||||||
@@ -702,9 +725,10 @@ function renderCommandsList() {
|
|||||||
countEl.textContent = showList ? resultCountText(ACTIONS.length, total, query) : total + " recent";
|
countEl.textContent = showList ? resultCountText(ACTIONS.length, total, query) : total + " recent";
|
||||||
}
|
}
|
||||||
updateSelection();
|
updateSelection();
|
||||||
|
updatePins(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A tab row: no star/unpin (tabs aren't pinnable), placeholder tile (tabs have no pictogram). Uses
|
// A tab row: no pin/unpin (tabs aren't pinnable), placeholder tile (tabs have no pictogram). Uses
|
||||||
// tabTitle so pages added with an empty text (e.g. Home) still show a label.
|
// tabTitle so pages added with an empty text (e.g. Home) still show a label.
|
||||||
function renderTabRow(t, i) {
|
function renderTabRow(t, i) {
|
||||||
var label = tabTitle(t);
|
var label = tabTitle(t);
|
||||||
@@ -781,7 +805,10 @@ function renderList() {
|
|||||||
function render(opts) {
|
function render(opts) {
|
||||||
renderFav();
|
renderFav();
|
||||||
renderList();
|
renderList();
|
||||||
scrollSelectedIntoView();
|
// Pin toggles don't move the selection, so they pass keepScroll to avoid snapping the list
|
||||||
|
// back to a row that is currently off-screen.
|
||||||
|
if (!(opts && opts.keepScroll))
|
||||||
|
scrollSelectedIntoView();
|
||||||
if (opts && opts.resetScroll)
|
if (opts && opts.resetScroll)
|
||||||
resetScrollPositions(listEl, document);
|
resetScrollPositions(listEl, document);
|
||||||
if (opts && opts.resize)
|
if (opts && opts.resize)
|
||||||
@@ -835,7 +862,7 @@ function toggleFav(id) {
|
|||||||
var newState = k === -1;
|
var newState = k === -1;
|
||||||
if (newState) FAVS.push(id); else FAVS.splice(k, 1);
|
if (newState) FAVS.push(id); else FAVS.splice(k, 1);
|
||||||
SendMessage({ command: "toggle_favourite", id: id, fav: newState });
|
SendMessage({ command: "toggle_favourite", id: id, fav: newState });
|
||||||
render({ resize: true });
|
render({ resize: true, keepScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire a command/plugin action; C++ owns the run-confirm (native dialog) + suppression, then
|
// Fire a command/plugin action; C++ owns the run-confirm (native dialog) + suppression, then
|
||||||
@@ -951,6 +978,14 @@ function OnInit() {
|
|||||||
|
|
||||||
document.addEventListener("keydown", function (e) {
|
document.addEventListener("keydown", function (e) {
|
||||||
if (favMenuEl && !favMenuEl.hidden && e.key === "Escape") { e.preventDefault(); hideFavMenu(); return; }
|
if (favMenuEl && !favMenuEl.hidden && e.key === "Escape") { e.preventDefault(); hideFavMenu(); return; }
|
||||||
|
// Pin/unpin the highlighted action: Ctrl/Cmd+B. Commands phase only (tabs/percent aren't pinnable).
|
||||||
|
if (phase === "commands" && (e.ctrlKey || e.metaKey) && !e.altKey && !e.shiftKey &&
|
||||||
|
e.key.toLowerCase() === "b") {
|
||||||
|
e.preventDefault();
|
||||||
|
var id = selectedActionId(sel, currentList(), currentVisibleFavs(), query);
|
||||||
|
if (id) toggleFav(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Quick-launch a numbered favourite: Alt/Option + digit (0 = the 10th). Only in the
|
// Quick-launch a numbered favourite: Alt/Option + digit (0 = the 10th). Only in the
|
||||||
// commands phase, where the pinned bar is shown.
|
// commands phase, where the pinned bar is shown.
|
||||||
if (phase === "commands" && e.altKey && !e.ctrlKey && !e.metaKey) {
|
if (phase === "commands" && e.altKey && !e.ctrlKey && !e.metaKey) {
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ kbd {
|
|||||||
border: 1px solid var(--border, var(--orca-border, #ddd));
|
border: 1px solid var(--border, var(--orca-border, #ddd));
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.star {
|
.pin {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
@@ -269,11 +269,11 @@ kbd {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.star svg { display: block; }
|
.pin svg { display: block; }
|
||||||
.row:hover .star:not(.on),
|
.row:hover .pin:not(.on),
|
||||||
.row.sel .star:not(.on) { opacity: .5; }
|
.row.sel .pin:not(.on) { opacity: .5; }
|
||||||
.star.on { opacity: 1; color: var(--main-color, var(--orca-accent, #009688)); }
|
.pin.on { opacity: 1; color: var(--main-color, var(--orca-accent, #009688)); }
|
||||||
.star:hover { background: rgba(127,127,127,.18); }
|
.pin:hover { background: rgba(127,127,127,.18); }
|
||||||
.dial-empty {
|
.dial-empty {
|
||||||
min-height: 64px;
|
min-height: 64px;
|
||||||
padding: 18px 10px;
|
padding: 18px 10px;
|
||||||
|
|||||||
@@ -728,9 +728,18 @@ nlohmann::json ActionRegistry::snapshot()
|
|||||||
|
|
||||||
// why: favourites is the ORDERED pin list - it must come from favourite_actions
|
// why: favourites is the ORDERED pin list - it must come from favourite_actions
|
||||||
// as stored, not be re-derived from the frecency-sorted actions (that would
|
// as stored, not be re-derived from the frecency-sorted actions (that would
|
||||||
// reorder the favourites bar). The page (js) filters out ids with no live action itself.
|
// reorder the favourites bar). Drop pins with no live action (an option hidden by the
|
||||||
// Cap on read so the bar cannot exceed the quick-launch slots (kFavLimit).
|
// current mode, an unloaded plugin, a gone plate/project) and persist the pruned list, so
|
||||||
nlohmann::json favourites(favourite_ids());
|
// invisible pins can't silently fill the quick-launch cap. Order is preserved.
|
||||||
|
std::vector<std::string> favs = favourite_ids();
|
||||||
|
std::vector<std::string> live_favs;
|
||||||
|
live_favs.reserve(favs.size());
|
||||||
|
for (const auto& id : favs)
|
||||||
|
if (m_actions.count(id))
|
||||||
|
live_favs.push_back(id);
|
||||||
|
if (live_favs.size() != favs.size())
|
||||||
|
write_section("favourite_actions", nlohmann::json(live_favs));
|
||||||
|
nlohmann::json favourites(live_favs);
|
||||||
|
|
||||||
// Recent = the last-N launched actions by recency (only actions with a run history).
|
// Recent = the last-N launched actions by recency (only actions with a run history).
|
||||||
constexpr size_t kRecentLimit = 5;
|
constexpr size_t kRecentLimit = 5;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ void SpeedDialWebDialog::handle_web_command(const nlohmann::json& payload)
|
|||||||
send_actions();
|
send_actions();
|
||||||
} else if (command == "toggle_favourite") {
|
} else if (command == "toggle_favourite") {
|
||||||
// set_favourite() refuses once the bar hits kFavLimit; tell the page so it can undo the
|
// set_favourite() refuses once the bar hits kFavLimit; tell the page so it can undo the
|
||||||
// star and show a "favourites are full" hint instead of silently losing the pin.
|
// pin and show a "favourites are full" hint instead of silently losing the favourite.
|
||||||
const std::string fav_id = payload.value("id", "");
|
const std::string fav_id = payload.value("id", "");
|
||||||
const bool ok = wxGetApp().action_registry().set_favourite(fav_id, payload.value("fav", false));
|
const bool ok = wxGetApp().action_registry().set_favourite(fav_id, payload.value("fav", false));
|
||||||
if (!ok)
|
if (!ok)
|
||||||
|
|||||||
Reference in New Issue
Block a user