ContextMenu in macOS App SwiftUI on Button Tap - macos

I want to tap a button and show a context menu in SwiftUI for a macOS app. I can see the button but when I tap the button nothing happens.
let menuItems = ContextMenu {
Button("Today") {}
Button("Tomorrow") {}
}
Button {
// action
} label: {
Label("Add Date", systemImage: "calendar")
.contextMenu(menuItems)
}
Any ideas?

It seems to me that you just look for menu with button style, like
Menu {
Button("Today") {}
Button("Tomorrow") {}
} label: {
Label("Add Date", systemImage: "calendar")
}
.menuStyle(.borderedButton)

Placing a Button around the context menu means that the button is managing all the click events, and none of them get through to the contextMenu modifier.
You could move the modifier to attach onto the button itself, and you get something that executes the button action on left click, but displays the context menu when right-clicked:
Button {
print("Clicked")
} label: {
Label("Add Date", systemImage: "calendar")
}
.contextMenu(menuItems)

Related

QML on Windows: use platform menu in dark mode, or get qt menu to jump to underlines?

Using both Qt 6.3.0 and 6.4.1, I'm trying to use a menu bar on Windows that (a) renders in dark mode and (b) lets the user jump to a menu item by typing its underlined character, just like a normal Windows app does.
If I use qt.labs platform menus, I can't find a way to make (a) happen, but (b) happens by default.
If I use qt's own menus, (a) happens naturally, but (b) does not.
I set up dark mode in main.cpp as follows (based on this post, among others):
#ifdef Q_OS_WIN
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",QSettings::NativeFormat);
if(settings.value("AppsUseLightTheme")==0){
app.setStyle(QStyleFactory::create("Fusion"));
QPalette darkPalette;
QColor darkColor = QColor(64,64,64); // QColor(45,45,45);
QColor disabledColor = QColor(127,127,127);
darkPalette.setColor(QPalette::Window, darkColor);
darkPalette.setColor(QPalette::WindowText, Qt::white);
...
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, disabledColor);
app.setPalette(darkPalette);
app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
#endif
My QML code is this, for platform menus:
import Qt.labs.platform as Platform
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Platform.MenuBar {
id: menuBar
Platform.Menu {
id: fileMenu
title: qsTr("&File")
Platform.MenuItem {
id: openMenuItem;
text: qsTr("&Open...")
onTriggered: fileOpenAction.trigger();
} // open
}
}
...
Or this, for native menus:
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
menuBar: MenuBar {
id: menuBar
Menu {
id: fileMenu
title: qsTr("&File")
MenuItem {
id: openMenuItem;
text: qsTr("&Open...")
onTriggered: fileOpenAction.trigger();
} // open
}
}
...
In both cases, the "Open..." item displays with an underlined O.
With the platform menus, the menu bar and menus are resolutely black on white, even as the rest of the window goes dark (explicit changes to the parent's palette aren't seen either, and the platform menu elements don't have a palette member that can be modified directly). I can type Alt+F to display the menu, and O to select the Open item. Is there a way to make the Windows-native platform menu respect dark mode?
With qt menus, the menu turns dark in dark mode, and I can type Alt+F to display the menu, but typing O does nothing. Is there a way to make the qt menus select a menu item when its underlined character is typed?
You can change the color of your controls by modifying the palette. The modification of the palette is hierarchical. i.e. you can define the palette by setting it in the parent control and the children will inherit, or, you can define the palette in the child control.
Here's a working example:
import QtQuick
import QtQuick.Controls
Page {
background: Rectangle { color: "#444" }
palette.buttonText: "lightgreen" // menu text
palette.button: "black" // menu background
palette.mid: "grey" // menu highlighted background
palette.windowText: "lightgreen" // popup menu text
palette.window: "black" // popup menu background
palette.light: "grey" // popup menu highlighted background
palette.dark: "grey" // popup menu border
MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&Open...")
}
MenuItem {
text: qsTr("&Quit")
}
}
Menu {
title: qsTr("&Edit")
palette.window: "#400"
MenuItem {
text: qsTr("&Copy")
}
MenuItem {
text: qsTr("&Paste")
}
}
}
}
You can Try it Online!
References:
https://doc.qt.io/qt-6/qml-qtquick-systempalette.html
https://doc.qt.io/qt-6/qml-qtquick-palette.html
"Solved" by talamaki in the comments: Pressing the letter alone is no longer that way it's done; the proper way is to press Alt+letter, and that works with qt menus — which obey dark mode. Thanks, talamaki!

How to keep newly opened macOS window in front & prevent being hidden with SwiftUI?

I am using SwiftUI to create a macOS app and need to create a new window that opens with an image inside of it, which I am successfully accomplishing currently.
However, if I click back on the main app window, the newly opened window goes to the background and is hidden (normal behavior), however, I want the newly opened window to always be on top of the main app window AFTER if I click back on the main application window.
The reason is that the new window (WindowGroup) opened contains an image with the information I need to enter in the main app so if it goes behind the main app window, I can't see the image anymore.
Is there a WindowGroup modifier I can implement so that after the WindowGroup("imageView") window opens, it is always on top & how can I integrate into my existing code?
Thank you!
#main
struct customApp: App {
#StateObject var session = SessionStore()
var body: some Scene {
WindowGroup("mainView") {
ContentView().environmentObject(session)
}.handlesExternalEvents(matching: ["mainView"])
WindowGroup("imageView") {
ImageView(url: SessionStore.imageUrl)
}.handlesExternalEvents(matching: ["imageView"])
}
}
View that opens new window
struct ImageViews: View {
#Environment(\.openURL) var openURL
var body: some View {
HStack {
WebImage(string: idUrl)
.onTapGesture {
guard let url = URL(string: "app://imageView") else { return }
openURL(url)
}
}
}
}
Set the window.level to always on top .floating. You can access it via NSApplication.shared.windows.
Button("Window level") {
for window in NSApplication.shared.windows {
window.level = .floating
}
}

Baseline alignment of buttons in SwiftUI on macOS

I’m trying to vertically align a Text and a regular Button along their baselines in SwiftUI on macOS. I basically want to replicate this layout from the WiFi preference pane:
I expected that putting the said controls into an HStack with the alignment set to .firstTextBaseline would produce this layout. While this is the case for many other controls, it doesn’t seem to work with non-plain buttons. See the following example:
HStack(alignment: .firstTextBaseline) {
Text("Text")
Text("Large Text")
.font(.title)
Button {} label: {
Text("Button")
}
Button {} label: {
Text("Borderless")
}
.buttonStyle(.borderless)
Button {} label: {
Text("Plain")
}
.buttonStyle(.plain)
Toggle("Toggle", isOn: .constant(true))
Picker("Picker", selection: .constant(0)) {
Text("One").tag(0)
Text("Two").tag(1)
Text("Three").tag(2)
}
.pickerStyle(.radioGroup)
}
.padding()
Am I holding it wrong? Is there a way to levere the baselines just like in AppKit, or do I have to position the button manually?

Removing Button Highlight SwiftUI

Is there anyway we could remove the default button highlight in SwiftUI?
I currently have a view that acts as a navigationlink but when I tap on it, I would like it to not have that default highlight (the fade out ).
The main view looks like:
NavigationLink( ... ) {
VStack{
{ ... }
Button(action: ... ){ ... }
}
}
.buttonStyle(PlainButtonStyle())
It's gotten rid of the blue foreground text color, but has not removed the default highlight.
I would like to do this as I have another button inside that view that does a seperate action, but when I click on that button, it highlights the entire view (but doesn't trigger the main view navigationlink!! it only triggers the inner button action)
I am currently using swiftui 2.0
edit:
I couldn't a find a way to remove that button highlight, but I found a different approach. Instead, I would just navigate programmatically by using the isActive version of NavigationLink. So instead it would be :
#State private var showOneLevelIn = false
//this navigationLink is hidden
NavigationLink(destination: OneLevelInView(), isActive: $showOneLevelIn, label: { EmptyView() })
//original view without navigationlink wrapped around
VStack{
{ ... }
Button(action: ... ){ ... }
}
.onTapGesture(count: 1) {
showOneLevelIn = true
}
found from: Use NavigationLink programmatically in SwiftUI
List detects any default button at any subview level. So try to change button style not only for link but for buttons as well
NavigationLink( ... ) {
VStack{
{ ... }
Button(action: ... ){ ... }
.buttonStyle(PlainButtonStyle()) // << here !!
}
}
.buttonStyle(PlainButtonStyle())

JavaFX: Right click on Mac (Control + Click) not being detected. Any fixes?

I have a list view with items that I am allowing to be double clicked and right clicked (to delete the item). Why does control clicking not work on a mac? Thanks in advance.
Edit: My code is
listview.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
if (event.getButton().equals(MouseButton.PRIMARY))
{
if (event.getClickCount() == 2)
{
System.out.println("Double clicked");
System.out.println("clicked on " + listview.getSelectionModel().getSelectedItem());
}
}
if(event.getButton().equals(MouseButton.SECONDARY))
{
System.out.println("Right click");
}
}
});
My trackpad is set up as secondary button with two finger tap.
For anyone else looking this up. It is correct that two finger clicking will register as a MouseButton.SECONDARY event, but I think you should also check for Ctrl + MouseButton.PRIMARY since holding the control key is a common method for emulating a right click. So the if statement should be :
if ( event.getButton() == MouseButton.SECONDARY || e.isControlDown() ) {
// DO RIGHT CLICK ACTION
}

Resources