griffon: add a splitPane to my app - griffon

I need to add a splitPane textArea to my Griffon app.
I cannot seem to find an example of the proper syntax and way to do this.
Can anyone help me out??
Here is my view so far:
=================================================================================
package test1
import griffon.util.GriffonNameUtils as GNU
import java.beans.PropertyChangeListener
application(title: 'Test1',
//preferredSize: [600, 300],
pack: true,
locationByPlatform: true,
iconImage: imageIcon('/griffon-icon-48x48.png').image,
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
imageIcon('/griffon-icon-32x32.png').image,
imageIcon('/griffon-icon-16x16.png').image]) {
borderLayout()
panel(constraints: WEST,
border: titledBorder(title: 'Platform')) {
migLayout()
buttonGroup(id: 'platform')
def radioButtonConverter = { String title, v -> v ? title : model.deviceType }
for (data in model.deviceTypes) {
radioButton(data.title, buttonGroup: platform, constraints: 'wrap',
selected: bind('deviceType', target: model,
converter: radioButtonConverter.curry(data.title), value: data.selected))
}
}
panel(constraints: EAST,
border: titledBorder(title: 'Path Browser')) {
migLayout()
controller.griffonClass.actionNames.each { name ->
button(getVariable(name + 'Action'),
constraints: 'growx, wrap')
}
}
panel(constraints: CENTER, id: 'devicePanel',
border: titledBorder(id: 'devicePanelBorder', title: 'No Devices')) {
noparent {
model.addPropertyChangeListener('deviceType', { e ->
model.deviceTypes.each{ d-> d.selected = false }
model.deviceTypes.find{ d -> d.title == e.newValue }.selected = true
devicePanelBorder.title = e.newValue
devicePanel.layout.show(devicePanel, e.newValue)
devicePanel.repaint() // force redraw
} as PropertyChangeListener)
}
cardLayout()
for(data in model.deviceTypes) {
// we set the title as the page's constraints -> simplifies bookkeeping
// in the PropertyChangeListener registered above
panel(constraints: data.title) {
gridLayout(cols: 2, rows: (data.devices.size()/2))
data.devices.each { device ->
checkBox(device.name, selected: bind(value: device.selected, target: device, 'selected'))
}
}
}
}
panel(constraints: SOUTH) {
riverLayout()
buttonGroup(id: 'execute', constraints: 'center')
button('Build XML', buttonGroup: execute)
button('Run', buttonGroup: execute)
button('Exit', buttonGroup: execute)
}
panel(constraints: NORTH) {
riverLayout()
label('TWC Companion Device Test Tool', constraints: 'center')
}
}
============================================================================================
Thanks!!
ironmantis7x

As shown by SwingPad (https://github.com/griffon/griffon/blob/master/src/dist/samples/SwingPad/griffon-app/views/griffon/samples/swingpad/SwingPadContent.groovy) using splitPane is as simple as
splitPane(resizeWeight: 0.5f) {
label('Left component')
label('Right component')
}
Have a look at the View section of the Griffon Guide to learn more about nodes
http://griffon.codehaus.org/guide/latest/guide/views.html#specialNodes
The following link has pointers to all nodes that can be used with SwingBuilder
http://groovy.codehaus.org/Swing+Builder
Lastly, you can launch SwingPad ($GRIFFON_HOME/samples/SwingPad) and play with live nodes. This application includes a list of all nodes (Help -> Node List) plus a very basic node name completion feature.

Related

How to select an item from a search file and place in textfield in another file

Using SwiftUI - Xcode 14.2 - iOS 16.0
I have tried different search tutorials to create a search file for my project but am unable to find out how to select the item in the search file and place that selected item in a textfield in another file. I have searched this site for other posts, i tried searching through Google, YouTube, etc...
In File 1, I have a textfield that that has a prompt 'start typing' and when selected, it directs you to the Search file to select the item you want, so it can be placed in place of the prompt.
File 1 (where the textfield is needed to paste the selected item):
VStack {
NavigationLink(destination: NameSearch()) {
TextField("Name", text: .constant(""), prompt: Text(" Start typing ")
.foregroundColor(.blue))
.multilineTextAlignment(.leading)
.padding()
}
}
Once I click on the 'start typing' prompt, it navigates to NameSearch.swift file, as seen below.
NameSearch.swift:
import SwiftUI
struct NameSearch: View {
let name = [
"Jane", "George", "Sam", "Henry", "Sally", "Liz", "John"
]
#State private var searchText = ""
var body: some View {
NavigationStack {
VStack {
// Search view
SearchBarView(searchText: $searchText)
List {
// Filtered list of names
ForEach(name.filter{$0.hasPrefix(searchText) || searchText == ""}, id:\.self) {
searchText in Text(searchText)
}
}
.navigationBarTitle(Text("Search Name"))
.resignKeyboardOnDragGesture()
}
}
}
}
struct NameSearch_Previews: PreviewProvider {
static var previews: some View {
Group {
NameSearch()
.environment(\.colorScheme, .light)
NameSearch()
.environment(\.colorScheme, .dark)
}
}
}
extension UIApplication {
func endEditing(_ force: Bool) {
self.windows
.filter{$0.isKeyWindow}
.first?
.endEditing(force)
}
}
struct ResignKeyboardOnDragGesture: ViewModifier {
var gesture = DragGesture().onChanged{_ in
UIApplication.shared.endEditing(true)
}
func body(content: Content) -> some View {
content.gesture(gesture)
}
}
extension View {
func resignKeyboardOnDragGesture() -> some View {
modifier(ResignKeyboardOnDragGesture())
}
}
struct SearchBarView: View {
#Binding var searchText: String
#State private var showCancelButton: Bool = false
var onCommit: () ->Void = {print("onCommit")}
var body: some View {
HStack {
HStack {
Image(systemName: "magnifyingglass")
// Search text field
ZStack (alignment: .leading) {
if searchText.isEmpty { // Separate text for placeholder to give it the proper color
Text("Search")
}
TextField("", text: $searchText, onEditingChanged: { isEditing in
self.showCancelButton = true
}, onCommit: onCommit).foregroundColor(.primary)
}
// Clear button
Button(action: {
self.searchText = ""
}) {
Image(systemName: "xmark.circle.fill").opacity(searchText == "" ? 0 : 1)
}
}
.padding(EdgeInsets(top: 8, leading: 6, bottom: 8, trailing: 6))
.foregroundColor(.secondary) // For magnifying glass and placeholder test
.background(Color(.tertiarySystemFill))
.cornerRadius(10.0)
if showCancelButton {
// Cancel button
Button("Cancel") {
UIApplication.shared.endEditing(true) // this must be placed before the other commands here
self.searchText = ""
self.showCancelButton = false
}
.foregroundColor(Color(.systemBlue))
}
}
.padding(.horizontal)
.navigationBarHidden(showCancelButton)
}
}
Question 1: How do I hide all the names from showing in the list so that I just see the search bar and the cancel button and an empty list?
Question 2: Once I type the name I am looking for, it should pop up and I want to select name - how can I do this?
once I type the name in search bar, it appears in the empty list
I select that name
it then takes me back to File 1
replaces the 'start typing' prompt with the name i just selected in the Search file.
Question 3: I have noticed in the Search file, I am getting a warning with the following code. How can I resolve it?
extension UIApplication {
func endEditing(_ force: Bool) {
self.windows
.filter{$0.isKeyWindow}
.first?
.endEditing(force)
}
}
The warning that appears is:
'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a
relevant window scene instead
Firstly, thank you for providing a working example of your code.
As you're building for iOS 15+, you should probably be using the .searchable modifier rather than rolling your own.
The 2021 WWDC video introducing this feature is here https://developer.apple.com/wwdc21/10176
Some new features from 2022 here: https://developer.apple.com/wwdc22/10052

Widget unable to display Core Data properties

I am unable to feed and read core data objects/properties into my Widget view. I am only able to feed and present it Doubles/Strings beforehand and it will work.
Prior to this, I have made sure all Core Data related files are shared between both the Widget Extension and the main app. The App Group has been set up and the data can be loaded (I am able to load all transactions in the Provider, calculate the total, and feed that double into the Widget view).
However, when it comes to actually feeding an array of transactions into the Widget View, the data cannot be loaded and all I see are empty rectangles. However, I'm pretty certain the data is accessible as the colours are accurate, and if I display the count of the array, it is accurate too. Below is my attached code.
struct RecentExpenditureWidget: Widget {
let kind: String = "ExpenditureWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: RecentWidgetConfigurationIntent.self, provider: Provider()) { entry in
ExpenditureWidgetEntryView(entry: entry)
}
.configurationDisplayName("Recent Transactions")
.description("View your latest expenses.")
.supportedFamilies([.systemSmall])
}
}
struct Provider: IntentTimelineProvider {
typealias Intent = RecentWidgetConfigurationIntent
public typealias Entry = RecentWidgetEntry
func placeholder(in context: Context) -> RecentWidgetEntry {
RecentWidgetEntry(date: Date(), amount: loadAmount(type: .week), transactions: loadTransactions(type: .week, count: 3))
}
func getSnapshot(for configuration: RecentWidgetConfigurationIntent, in context: Context, completion: #escaping (RecentWidgetEntry) -> ()) {
let entry = RecentWidgetEntry(date: Date(), amount: loadAmount(type: configuration.duration), transactions: loadTransactions(type: configuration.duration, count: 3))
completion(entry)
}
func getTimeline(for configuration: RecentWidgetConfigurationIntent, in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
let entry = RecentWidgetEntry(date: Date(), amount: loadAmount(type: configuration.duration), transactions: loadTransactions(type: configuration.duration, count: 3))
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
func loadAmount(type: TimePeriod) -> Double {
let dataController = DataController()
let itemRequest = dataController.fetchRequestForRecentTransactions(type: type)
let transactions = dataController.results(for: itemRequest)
var holdingTotal = 0.0
transactions.forEach { transaction in
holdingTotal += transaction.wrappedAmount
}
return holdingTotal
}
func loadTransactions(type: TimePeriod, count: Int) -> [Transaction] {
let dataController = DataController()
let itemRequest = dataController.fetchRequestForRecentTransactionsWithCount(type: type, count: count)
return dataController.results(for: itemRequest)
}
}
struct RecentWidgetEntry: TimelineEntry {
let date: Date
let amount: Double
let transactions: [Transaction]
}
struct ExpenditureWidgetEntryView : View {
let entry: Provider.Entry
var body: some View {
VStack(spacing: 5) {
ForEach(entry.transactions) { transaction in
HStack(spacing: 2) {
Capsule()
.fill(Color(transaction.category?.wrappedColour ?? ""))
.frame(width: 4)
Text(transaction.wrappedNote)
.lineLimit(1)
.font(.system(size: 12, weight: .regular, design: .rounded))
.foregroundColor(Color.PrimaryText)
Text(transaction.amount, format: .currency(code: Locale.current.currencyCode ?? "USD"))
.font(.system(size: 12, weight: .regular, design: .rounded))
.foregroundColor(Color.SubtitleText)
}
.frame(height: 15)
}
}
.padding(15)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.PrimaryBackground)
}
}
This is the result I get on my actual devices:
The only (by extremely dumb) workaround I have found is to extract each property of a transaction in the loadTransaction() function, and then feed it into the Widget.
func loadTransactions(type: TimePeriod, count: Int) -> String {
let dataController = DataController()
let itemRequest = dataController.fetchRequestForRecentTransactionsWithCount(type: type, count: count)
return dataController.results(for: itemRequest).first!.wrappedNote
}

Swift UI, removing item from array, while looping in it throws Fatal Error: Index out of range [duplicate]

I am trying to remove rows inside a ForEach. Removing the last row always throws an index out of range exception. Removing any other row does not.
ForEach(Array(player.scores.enumerated()), id: \.element) { index, score in
HStack {
if self.isEditSelected {
Button(action: {
self.player.scores.remove(at: index)
}, label: {
Image("delete")
})
}
TextField("\(score)", value: self.$player.scores[index], formatter: NumberFormatter())
}
}
I have tried using ForEach(player.indices...) & ForEach(player.scores...), but see the same problem.
Looks to me like the crash happens here self.$player.scores[index], as hardcoding the index to any value other that the last row is working.
Does anyone know how to fix this? Or if there is a better approach.
Here is fix
ForEach(Array(player.scores.enumerated()), id: \.element) { index, score in
HStack {
if self.isEditSelected {
Button(action: {
self.player.scores.remove(at: index)
}, label: {
Image("delete")
})
}
TextField("\(score)", value: Binding( // << use proxy binding !!
get: { self.player.scores[index] },
set: { self.player.scores[index] = $0 }),
formatter: NumberFormatter())
}
}
Based on #Asperi answer
public extension Binding where Value: Equatable {
static func proxy(_ source: Binding<Value>) -> Binding<Value> {
self.init(
get: { source.wrappedValue },
set: { source.wrappedValue = $0 }
)
}
}
You can use this as follows:
TextField("Name", text: .proxy($variable))
Xcode 13.0 beta introduced a new way to establish two-way-bindings between the elements of a collection and the views built by ForEach / List.
This method fixes the crash related to deleting the last row.
struct Score: Identifiable {
let id = UUID()
var value: Int
}
struct Player {
var scores: [Score] = (1...10).map {_ in .init(value: Int.random(in: 0...25))}
}
struct BindingTest: View {
#State private var player = Player()
var body: some View {
List {
ForEach($player.scores) { $score in
HStack {
TextField("\(score.value)", value: $score.value,
formatter: NumberFormatter())
}
}
.onDelete { player.scores.remove(atOffsets: $0)}
}
}
}

Tab to next text field when 'next/return' key hit on keyboard in SwiftUI

This is strictly for SwiftUI.
I would like to have the keyboard move to the next available text field when the user hits the 'return' key on the keyboard.
I have the following view:
var body: some View {
VStack {
NavigationView {
Form {
TextField("First name", text: $model.firstname)
.tag(1)
TextField("Last name", text: $model.lastname)
.tag(2)
}
.navigationBarTitle("Add a Person", displayMode: .inline)
}
}
}
And the following code that should allow the tab:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
nextResponder.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return true
}
I am just not sure how to implement it in SwiftUI?
How do I assign it to the delegate of the textfield?!
****UPDATE****
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
print("Current Tag: ", textField.tag) // Works correctly
let nextTag = textField.tag + 1
print("Next Tag: ", nextTag) // Works correctly
let nextResponder = textField.superview?.viewWithTag(nextTag) as UIResponder? // ALWAYS RETURN NIL
....
Not sure why the assignment of nextResponder always returns nil?!
iOS15+
Now it can be easily done with FocusState+.focused(,equals:) specifying named tags and updating focus state on needed action.
Tested with Xcode 13.3 / iOS 15.4
Here is main part:
#FocusState private var infocus: Field?
enum Field {
case first, last
}
// ...
TextField("First name", text: $firstname,
onCommit: { infocus = .last }) // << here !!
.focused($infocus, equals: .first)
Complete test module in project is here

Cannot find namespace 'NodeJS'

If i run this code, i get an error, Cannot find namespace 'NodeJS'.
public exportExcel(jsonData: any[], excelFileName: string): void {
//Excel Title, Header, Data
const header: string[] = Object.keys(jsonData[0]);
const data = jsonData;
//Create workbook and worksheet
let workbook = new Workbook();
let worksheet = workbook.addWorksheet(excelFileName);
//Add Header Row
let headerRow = worksheet.addRow(header);
// Cell Style : Fill and Border
headerRow.eachCell((cell, number) => {
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFFF00' },
bgColor: { argb: 'FF0000FF' }
}
cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } }
})
// Add Data and Conditional Formatting
data.forEach((element) => {
let eachRow = [];
header.forEach((headers) => {
eachRow.push(element[headers])
})
if (element.isDeleted === "Y") {
let deletedRow = worksheet.addRow(eachRow);
deletedRow.eachCell((cell, number) => {
cell.font = { name: 'Calibri', family: 4, size: 11, bold: false, strike: true };
})
} else {
worksheet.addRow(eachRow);
}
})
...
ERROR in node_modules/exceljs/index.d.ts(1648,34): error TS2503: Cannot find namespace 'NodeJS'.
Solution targetting Angular8+ Projects:
This is a known bug, caused due to the incompatibility of exceljs with the version of #types/node. I faced similar issue with Angular 10.
2 possible solutions exists:
Recommended: Update your tsconfig.app.json file with "types": ["node"]
If you are okay without having type support, then you can simply use the below import:
import * as Excel from "exceljs/dist/exceljs.min.js";
Just Open index.d.ts file from yours exceljs node_modules
and replace this line
dictionary: Buffer | NodeJS.TypedArray | DataView | ArrayBuffer; // deflate/inflate only, empty dictionary by default
with this
dictionary: Buffer | DataView | ArrayBuffer; // deflate/inflate only, empty dictionary by default
and then just ng serve

Resources