Skip to content

48 make sidebar more intuitive #52

Merged
merged 15 commits into from
Nov 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 57 additions & 30 deletions src/AnnotationControl/annotationPanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ img.button-icon[src$='/annotation.svg'] {
padding: 8px;
border-radius: 4px;
border: 1px solid #404a50;
background: #2f383d;
color: #cfd5d8;
background: #636262;
color: #636262;
font-family: inherit;
font-size: 12px;
line-height: 1.3;
Expand All @@ -44,10 +44,6 @@ img.button-icon[src$='/annotation.svg'] {
margin-top: 6px;
}

.annotation-add-button {
margin: 10px 0;
}

.annotation-empty {
opacity: 0.6;
padding: 10px;
Expand Down Expand Up @@ -350,37 +346,68 @@ img.button-icon[src$='/annotation.svg'] {
flex: 0 0 18px;
}

.pv-menu-list_annotations-panel {
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
}

/* Add button */
.annotation-add-button {
background: linear-gradient(180deg, #f6f6f6 0%, #e9e9e9 100%);
color: #222;
padding: 8px 16px;
min-width: 140px;
height: 38px;
display: block;
margin: 12px auto;
border-radius: 6px;
width: 80%;
margin: 20px 0 10px;
padding: 10px 10px;
font-size: 13px;
font-weight: 700;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.6) inset;
border: 1px solid #cfcfcf;
font-weight: 500;
background-color: #636262;
color: #ffffff;
border: 1px solid #555;
border-radius: 4px;
cursor: pointer;
text-align: center;
}
.annotation-add-button .add-label {
color: #222;
font-weight: 700;
transition:
background-color 0.2s ease,
transform 0.1s ease;
}

.annotation-add-button:hover {
background: linear-gradient(180deg, #f3f3f3 0%, #e2e2e2 100%);
border-color: #bfbfbf;
background-color: #8f8f8f;
}
.annotation-add-button:active {
transform: translateY(1px);
background: linear-gradient(180deg, #e9e9e9 0%, #dbdbdb 100%);
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.06);

#labelToggleContainer {
margin: 8px 0 6px;
padding-left: 4px;
}
.annotation-add-button:focus {
outline: 2px solid rgba(100, 100, 100, 0.12);
outline-offset: 2px;
#labelToggleContainer .labels-legend {
font-size: 13px;
color: #ddd;
margin-bottom: 4px;
}

.toggle-group {
display: flex;
width: 265px;
border: 1px solid black;
border-radius: 4px;
overflow: hidden;
}
.toggle-group button {
flex: 1;
padding: 6px 15px;
background: #a7a9aa;
color: #3d3c3c;
border: 0;
cursor: pointer;
font-weight: 300;
transition: background 0.2s;
}

.toggle-group button:not(:last-child) {
border-right: 1px solid #555;
}

.toggle-group button.active {
background: #c7c9ca;
color: #000;
}
55 changes: 54 additions & 1 deletion src/AnnotationControl/annotationPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function initAnnotationsPanel(viewer) {
header.appendChild(headerSpan)

const panel = document.createElement('div')
panel.className = 'pv-menu-list annotations-panel'
panel.className = 'pv-menu-list_annotations-panel'

const listContainerDiv = document.createElement('div')
listContainerDiv.id = 'annotations_list'
Expand Down Expand Up @@ -68,6 +68,47 @@ export function initAnnotationsPanel(viewer) {
targetContainer = panel.querySelector('#annotations_list')
}
}

// --- Add Show/Hide labels toggle group ---
const panelEl =
targetContainer.closest('.pv-menu-list_annotations-panel') ||
targetContainer.parentElement

if (!panelEl.querySelector('#labelToggleContainer')) {
const controls = document.createElement('div')
controls.id = 'labelToggleContainer'
controls.innerHTML = `
<p class="labels-legend">Show/Hide saved locations</p>
<div class="toggle-group">
<button id="showLabelsBtn" class="active">Show</button>
<button id="hideLabelsBtn">Hide</button>
</div>
`
// Insert before list of annotations
panelEl.insertBefore(controls, targetContainer)

// show/hide all annotations
const setLabelsVisible = (visible) => {
const cont = document.getElementById('potree_annotation_container')
if (cont) cont.style.display = visible ? '' : 'none'
}

const showBtn = controls.querySelector('#showLabelsBtn')
const hideBtn = controls.querySelector('#hideLabelsBtn')

showBtn.addEventListener('click', () => {
setLabelsVisible(true)
showBtn.classList.add('active')
hideBtn.classList.remove('active')
})

hideBtn.addEventListener('click', () => {
setLabelsVisible(false)
hideBtn.classList.add('active')
showBtn.classList.remove('active')
})
}

if (!targetContainer) {
console.warn(
'Annotations list container not found and dynamic injection failed'
Expand Down Expand Up @@ -947,6 +988,18 @@ export function initAnnotationsPanel(viewer) {
annotationHeader.nextElementSibling.style.display = ''
}
}
// Ensure annotation labels are shown when starting to add a location.
// The "Show" button will be shown as active and reveals Potree's annotation container.
try {
const showBtn = document.getElementById('showLabelsBtn')
const hideBtn = document.getElementById('hideLabelsBtn')
if (showBtn) showBtn.classList.add('active')
if (hideBtn) hideBtn.classList.remove('active')
const cont = document.getElementById('potree_annotation_container')
if (cont) cont.style.display = ''
} catch (e) {
console.warn('Could not enable annotation labels on add', e)
}
// Capture current camera view (position) at the moment the user clicks Add
let camPos = null
try {
Expand Down
25 changes: 18 additions & 7 deletions src/Filter/filter.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#doAcceptedFiltering {
display: flex;
width: 100%;
justify-content: center;
margin: 6px 0 10px;
padding: 10px 10px;
font-size: 13px;
Expand Down Expand Up @@ -102,22 +103,32 @@
align-items: center;
margin: 3px 0;
font-size: 13px;
color: #ddd; /* visible text */
color: #ddd;
}

/* Color boxes */
/* Color boxes → now circles */
#accepted_legend .legend-color {
width: 16px;
height: 16px;
border: 1px solid #777;
margin-right: 8px;
border-radius: 2px;
width: 20px;
height: 8px;
border-radius: 4px; /* pill shape */
margin-left: 6px;
}

#accepted_legend .legend-color.accepted {
background-color: #fff;
border: #0008;
box-shadow: 0 0 4px 1px #fff8;
}

#accepted_legend .legend-color.not-accepted {
background-color: #000;
box-shadow: 0 0 4px 1px #fff8;
}

#gradient_repeat_option fieldset {
margin: 15px 0px 12px 0px !important;
}

#gradient_repeat_option fieldset legend {
margin: 0px 0px 5px 0px !important;
}
12 changes: 10 additions & 2 deletions src/Filter/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ function ensureElevationButton(hooks) {
btn.type = 'button'
btn.textContent = 'Activate elevation control'
btn.addEventListener('click', () => {
const elevBtn = byId('btnDoElevationControl')
const accBtn = byId('doAcceptedFiltering')
if (elevBtn) elevBtn.style.display = 'none'
if (accBtn) accBtn.style.display = ''
switchMode('elevation', hooks?.onActivateElevation, hooks)
})
btns.appendChild(btn)
Expand Down Expand Up @@ -283,6 +287,10 @@ function ensureAcceptedButton(hooks) {
btn.type = 'button'
btn.textContent = 'Activate accepted filter'
btn.addEventListener('click', () => {
const accBtn = byId('doAcceptedFiltering')
const elevBtn = byId('btnDoElevationControl')
if (accBtn) accBtn.style.display = 'none'
if (elevBtn) elevBtn.style.display = ''
switchMode('accepted', hooks?.onActivateAccepted, hooks)
})
btns.appendChild(btn)
Expand All @@ -303,12 +311,12 @@ function ensureAcceptedLegend() {
legend.style.display = 'none'
legend.innerHTML = `
<div class="legend-row">
<span>Accepted points displayed as: </span>
<div class="legend-color accepted"></div>
<span>Accepted points</span>
</div>
<div class="legend-row">
<span>Not accepted points displayed as: </span>
<div class="legend-color not-accepted"></div>
<span>Not accepted points</span>
</div>
`
list.appendChild(legend)
Expand Down
29 changes: 29 additions & 0 deletions src/MeasurementControl/measurementsPanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,32 @@
border-top: 1px solid #303a3f;
padding-top: 10px;
}

.tool-with-label {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 10px;
cursor: pointer;
border-radius: 4px;
padding: 4px;
}
.tool-with-label:hover {
box-shadow: 0 0 5px #fff8;
}

.tool-with-label:hover img {
filter: brightness(1.7);
}

.tool-with-label:hover .tool-label {
color: #fff;
}

.tool-label {
font-size: 14px;
margin-top: 2px;
margin-left: 4px;
color: #aaa;
pointer-events: none;
}
41 changes: 40 additions & 1 deletion src/MeasurementControl/measurementsPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function initMeasurementsPanel(viewer) {
panel.appendChild(toolsHostDiv)
panel.appendChild(listContainerDiv)
// Insert before filters/tools if possible, else append at end
const tools = document.getElementById('menu_tools')
const tools = document.getElementById('menu_appearance')
if (tools) {
menu.insertBefore(panel, tools)
menu.insertBefore(header, panel)
Expand Down Expand Up @@ -650,6 +650,45 @@ export function initMeasurementsPanel(viewer) {
toolsHost.appendChild(existingTools)
}

// After tools are moved into `toolsHost`
const toolDescriptions = {
'angle.png': 'Measure angle',
'point.svg': 'Inspect point',
'distance.svg': 'Measure distance',
'height.svg': 'Measure height',
'circle.svg': 'Circle',
'azimuth.svg': 'Azimuth',
'area.svg': 'Area',
'volume.svg': 'Volume',
'sphere_distances.svg': 'Sphere volume',
'profile.svg': '2D height profile',
'reset_tools.svg': 'Remove all'
}

const toolIcons = existingTools.querySelectorAll('img')
toolIcons.forEach((img) => {
const src = img.getAttribute('src')
const file = src.split('/').pop() // extract icon name
const baseName = file.replace(/\.[^/.]+$/, '')

if (toolDescriptions[file]) {
const wrapper = document.createElement('div')
wrapper.className = 'tool-with-label'
wrapper.id = `tool-wrapper-${baseName}`

wrapper.addEventListener('click', () => img.click())

img.parentNode.insertBefore(wrapper, img)
wrapper.appendChild(img)

const label = document.createElement('span')
label.className = 'tool-label'
label.textContent = toolDescriptions[file]
label.id = `label-${file.replace(/\.[^/.]+$/, '')}`
wrapper.appendChild(label)
}
})

// Move measurement options UI into our tools host
if (toolsHost) {
const measOptions = document.getElementById('measurement_options_show')
Expand Down
5 changes: 1 addition & 4 deletions src/potreeViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ export async function createPotreeViewer(

viewer.loadGUI(() => {
viewer.setLanguage('en')
$('#menu_appearance').next().show()
$('#menu_tools').next().show()
$('#menu_scene').next().show()
$('#menu_filters').next().show()
$('#menu_filters').remove()
viewer.toggleSidebar()

// Store the last used elevation gradient
Expand Down