Skip to content

Commit 744a13a

Browse files
committed
Fix positioning and width of material selection menu
Fixes #32
1 parent 5ddd2e9 commit 744a13a

5 files changed

Lines changed: 585 additions & 8 deletions

File tree

Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
// Copyright (c) 2022 Aldo Hoeben / fieldOfView
2+
// SidebarGUIPlugin is released under the terms of the AGPLv3 or higher.
3+
// Copyright (c) 2022 Ultimaker B.V.
4+
// Cura is released under the terms of the LGPLv3 or higher.
5+
6+
import QtQuick 2.7
7+
import QtQuick.Controls 2.4
8+
import QtQuick.Layouts 2.7
9+
10+
import UM 1.5 as UM
11+
import Cura 1.7 as Cura
12+
13+
/* This element is a workaround for MacOS, where it can crash in Qt6 when nested menus are closed.
14+
Instead we'll use a pop-up which doesn't seem to have that problem. */
15+
16+
Cura.MenuItem
17+
{
18+
id: materialBrandMenu
19+
overrideShowArrow: true
20+
21+
property var materialTypesModel
22+
text: materialTypesModel.name
23+
24+
contentItem: MouseArea
25+
{
26+
hoverEnabled: true
27+
28+
RowLayout
29+
{
30+
spacing: 0
31+
opacity: materialBrandMenu.enabled ? 1 : 0.5
32+
33+
Item
34+
{
35+
// Spacer
36+
width: UM.Theme.getSize("default_margin").width
37+
}
38+
39+
UM.Label
40+
{
41+
text: replaceText(materialBrandMenu.text)
42+
Layout.fillWidth: true
43+
Layout.fillHeight:true
44+
elide: Label.ElideRight
45+
wrapMode: Text.NoWrap
46+
}
47+
48+
Item
49+
{
50+
Layout.fillWidth: true
51+
}
52+
53+
Item
54+
{
55+
// Right side margin
56+
width: UM.Theme.getSize("default_margin").width
57+
}
58+
}
59+
60+
onEntered: showTimer.restartTimer()
61+
onExited: hideTimer.restartTimer()
62+
}
63+
64+
Timer
65+
{
66+
id: showTimer
67+
interval: 250
68+
function restartTimer()
69+
{
70+
restart();
71+
running = Qt.binding(function() { return materialBrandMenu.enabled && materialBrandMenu.contentItem.containsMouse; });
72+
hideTimer.running = false;
73+
}
74+
onTriggered: menuPopup.open()
75+
}
76+
Timer
77+
{
78+
id: hideTimer
79+
interval: 250
80+
function restartTimer() //Restart but re-evaluate the running property then.
81+
{
82+
restart();
83+
running = Qt.binding(function() { return materialBrandMenu.enabled && !materialBrandMenu.contentItem.containsMouse && !menuPopup.itemHovered > 0; });
84+
showTimer.running = false;
85+
}
86+
onTriggered: menuPopup.close()
87+
}
88+
89+
Popup
90+
{
91+
id: menuPopup
92+
width: materialTypesList.width + padding * 2
93+
height: materialTypesList.height + padding * 2
94+
95+
property var flipped: false
96+
97+
x: - width + UM.Theme.getSize("thin_margin").width
98+
y: {
99+
// Checks if popup is more than halfway down the screen AND further than 400 down (this avoids popup going off the top of screen)
100+
// If it is then the popup will push up instead of down
101+
// This fixes the popups appearing bellow the bottom of the screen.
102+
103+
if (materialBrandMenu.parent.height / 2 < parent.y && parent.y > 400) {
104+
flipped = true
105+
return -UM.Theme.getSize("default_lining").width - height + UM.Theme.getSize("menu").height
106+
}
107+
flipped = false
108+
return -UM.Theme.getSize("default_lining").width
109+
}
110+
111+
padding: background.border.width
112+
// Nasty hack to ensure that we can keep track if the popup contains the mouse.
113+
// Since we also want a hover for the sub items (and these events are sent async)
114+
// We have to keep a count of itemHovered (instead of just a bool)
115+
property int itemHovered: 0
116+
MouseArea
117+
{
118+
id: submenuArea
119+
anchors.fill: parent
120+
121+
hoverEnabled: true
122+
onEntered: hideTimer.restartTimer()
123+
}
124+
125+
background: Rectangle
126+
{
127+
color: UM.Theme.getColor("main_background")
128+
border.color: UM.Theme.getColor("lining")
129+
border.width: UM.Theme.getSize("default_lining").width
130+
}
131+
132+
Column
133+
{
134+
id: materialTypesList
135+
spacing: 0
136+
width: brandMaterialsRepeater.width
137+
138+
property var brandMaterials: materialTypesModel.material_types
139+
140+
Repeater
141+
{
142+
id: brandMaterialsRepeater
143+
model: parent.brandMaterials
144+
145+
property var maxWidth: 0
146+
onItemAdded: updateWidth()
147+
onItemRemoved: updateWidth()
148+
149+
function updateWidth()
150+
{
151+
var result = 0;
152+
for (var i = 0; i < count; ++i) {
153+
var item = itemAt(i);
154+
if(item != null)
155+
{
156+
result = Math.max(item.contentWidth, result);
157+
}
158+
}
159+
width = result + 2 * UM.Theme.getSize("default_margin").width;
160+
}
161+
162+
//Use a MouseArea and Rectangle, not a button, because the button grabs mouse events which makes the parent pop-up think it's no longer being hovered.
163+
//With a custom MouseArea, we can prevent the events from being accepted.
164+
delegate: Rectangle
165+
{
166+
id: brandMaterialBase
167+
height: UM.Theme.getSize("menu").height
168+
width: parent.width
169+
170+
color: materialTypeButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1")
171+
172+
property int contentWidth: brandLabel.width + chevron.width + UM.Theme.getSize("default_margin").width
173+
property bool isFlipped: menuPopup.flipped
174+
175+
RowLayout
176+
{
177+
spacing: 0
178+
opacity: materialBrandMenu.enabled ? 1 : 0.5
179+
height: parent.height
180+
width: parent.width
181+
182+
Item
183+
{
184+
// Spacer
185+
width: UM.Theme.getSize("default_margin").width
186+
}
187+
188+
UM.Label
189+
{
190+
id: brandLabel
191+
text: model.name
192+
Layout.fillHeight: true
193+
elide: Label.ElideRight
194+
wrapMode: Text.NoWrap
195+
}
196+
197+
Item
198+
{
199+
Layout.fillWidth: true
200+
}
201+
202+
UM.ColorImage
203+
{
204+
id: chevron
205+
height: UM.Theme.getSize("default_arrow").height
206+
width: UM.Theme.getSize("default_arrow").width
207+
color: UM.Theme.getColor("setting_control_text")
208+
source: UM.Theme.getIcon("ChevronSingleRight")
209+
}
210+
211+
Item
212+
{
213+
// Right side margin
214+
width: UM.Theme.getSize("default_margin").width
215+
}
216+
}
217+
218+
MouseArea
219+
{
220+
id: materialTypeButton
221+
anchors.fill: parent
222+
223+
hoverEnabled: true
224+
acceptedButtons: Qt.NoButton
225+
226+
onEntered:
227+
{
228+
menuPopup.itemHovered += 1;
229+
showSubTimer.restartTimer();
230+
}
231+
onExited:
232+
{
233+
menuPopup.itemHovered -= 1;
234+
hideSubTimer.restartTimer();
235+
}
236+
}
237+
Timer
238+
{
239+
id: showSubTimer
240+
interval: 250
241+
function restartTimer()
242+
{
243+
restart();
244+
running = Qt.binding(function() { return materialTypeButton.containsMouse; });
245+
hideSubTimer.running = false;
246+
}
247+
onTriggered: colorPopup.open()
248+
}
249+
Timer
250+
{
251+
id: hideSubTimer
252+
interval: 250
253+
function restartTimer() //Restart but re-evaluate the running property then.
254+
{
255+
restart();
256+
running = Qt.binding(function() { return !materialTypeButton.containsMouse && !colorPopup.itemHovered > 0; });
257+
showSubTimer.running = false;
258+
}
259+
onTriggered: colorPopup.close()
260+
}
261+
262+
Popup
263+
{
264+
id: colorPopup
265+
width: materialColorsList.width + padding * 2
266+
height: materialColorsList.height + padding * 2
267+
x: parent.width
268+
y: {
269+
// If flipped the popup should push up rather than down from the parent
270+
if (brandMaterialBase.isFlipped) {
271+
return -height + UM.Theme.getSize("menu").height + UM.Theme.getSize("default_lining").width
272+
}
273+
return -UM.Theme.getSize("default_lining").width
274+
}
275+
276+
property int itemHovered: 0
277+
padding: background.border.width
278+
279+
background: Rectangle
280+
{
281+
color: UM.Theme.getColor("main_background")
282+
border.color: UM.Theme.getColor("lining")
283+
border.width: UM.Theme.getSize("default_lining").width
284+
}
285+
286+
Column
287+
{
288+
id: materialColorsList
289+
spacing: 0
290+
width: brandColorsRepeater.width
291+
292+
property var brandColors: model.colors
293+
294+
Repeater
295+
{
296+
id: brandColorsRepeater
297+
model: parent.brandColors
298+
299+
property var maxWidth: 0
300+
onItemAdded: updateColorWidth()
301+
onItemRemoved: updateColorWidth()
302+
303+
function updateColorWidth()
304+
{
305+
var result = 0;
306+
for (var i = 0; i < count; ++i) {
307+
var item = itemAt(i);
308+
if(item != null)
309+
{
310+
result = Math.max(item.contentWidth, result);
311+
}
312+
}
313+
width = result + 2 * UM.Theme.getSize("default_margin").width;
314+
}
315+
316+
delegate: Rectangle
317+
{
318+
height: UM.Theme.getSize("menu").height
319+
width: parent.width
320+
321+
color: materialColorButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1")
322+
323+
property int contentWidth: checkmark.width + colorLabel.width + UM.Theme.getSize("default_margin").width
324+
325+
Item
326+
{
327+
opacity: materialBrandMenu.enabled ? 1 : 0.5
328+
anchors.fill: parent
329+
330+
//Checkmark, if the material is selected.
331+
UM.ColorImage
332+
{
333+
id: checkmark
334+
visible: model.id === materialMenu.activeMaterialId
335+
height: UM.Theme.getSize("default_arrow").height
336+
width: height
337+
anchors.left: parent.left
338+
anchors.leftMargin: UM.Theme.getSize("default_margin").width
339+
anchors.verticalCenter: parent.verticalCenter
340+
source: UM.Theme.getIcon("Check", "low")
341+
color: UM.Theme.getColor("setting_control_text")
342+
}
343+
344+
UM.Label
345+
{
346+
id: colorLabel
347+
text: model.name
348+
anchors.left: parent.left
349+
anchors.leftMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("default_arrow").height
350+
anchors.verticalCenter: parent.verticalCenter
351+
352+
elide: Label.ElideRight
353+
wrapMode: Text.NoWrap
354+
}
355+
}
356+
357+
MouseArea
358+
{
359+
id: materialColorButton
360+
anchors.fill: parent
361+
362+
hoverEnabled: true
363+
onClicked:
364+
{
365+
Cura.MachineManager.setMaterial(extruderIndex, model.container_node);
366+
menuPopup.close();
367+
colorPopup.close();
368+
materialMenu.close();
369+
}
370+
onEntered:
371+
{
372+
menuPopup.itemHovered += 1;
373+
colorPopup.itemHovered += 1;
374+
}
375+
onExited:
376+
{
377+
menuPopup.itemHovered -= 1;
378+
colorPopup.itemHovered -= 1;
379+
}
380+
}
381+
}
382+
}
383+
}
384+
}
385+
}
386+
}
387+
}
388+
}
389+
}

0 commit comments

Comments
 (0)