🎉 first commit

This commit is contained in:
2023-07-13 11:56:17 +02:00
parent 92e03bc310
commit 3ad6ee9134
8 changed files with 328 additions and 32 deletions

90
.gitignore vendored Normal file
View File

@ -0,0 +1,90 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# Accio dependency management
Dependencies/
.accio/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/

View File

@ -7,13 +7,15 @@
objects = {
/* Begin PBXBuildFile section */
680A62122A5F475D004C21A4 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 680A62112A5F475D004C21A4 /* ViewExtensions.swift */; };
682D06A62A5487D200EA4745 /* LutoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 682D06A52A5487D200EA4745 /* LutoApp.swift */; };
682D06A82A5487D200EA4745 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 682D06A72A5487D200EA4745 /* ContentView.swift */; };
682D06A82A5487D200EA4745 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 682D06A72A5487D200EA4745 /* MainView.swift */; };
682D06AA2A5487D500EA4745 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 682D06A92A5487D500EA4745 /* Assets.xcassets */; };
682D06AD2A5487D500EA4745 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 682D06AC2A5487D500EA4745 /* Preview Assets.xcassets */; };
682D06B82A5487D600EA4745 /* LutoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 682D06B72A5487D600EA4745 /* LutoTests.swift */; };
682D06C22A5487D600EA4745 /* LutoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 682D06C12A5487D600EA4745 /* LutoUITests.swift */; };
682D06C42A5487D600EA4745 /* LutoUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 682D06C32A5487D600EA4745 /* LutoUITestsLaunchTests.swift */; };
6838051A2A57356700CEF29C /* TextFieldExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 683805192A57356700CEF29C /* TextFieldExtensions.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -34,9 +36,10 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
680A62112A5F475D004C21A4 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = "<group>"; };
682D06A22A5487D200EA4745 /* Luto.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Luto.app; sourceTree = BUILT_PRODUCTS_DIR; };
682D06A52A5487D200EA4745 /* LutoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LutoApp.swift; sourceTree = "<group>"; };
682D06A72A5487D200EA4745 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
682D06A72A5487D200EA4745 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
682D06A92A5487D500EA4745 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
682D06AC2A5487D500EA4745 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
682D06AE2A5487D500EA4745 /* Luto.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Luto.entitlements; sourceTree = "<group>"; };
@ -45,6 +48,7 @@
682D06BD2A5487D600EA4745 /* LutoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LutoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
682D06C12A5487D600EA4745 /* LutoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LutoUITests.swift; sourceTree = "<group>"; };
682D06C32A5487D600EA4745 /* LutoUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LutoUITestsLaunchTests.swift; sourceTree = "<group>"; };
683805192A57356700CEF29C /* TextFieldExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldExtensions.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -72,6 +76,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
680A620F2A5F4739004C21A4 /* Utils */ = {
isa = PBXGroup;
children = (
683805192A57356700CEF29C /* TextFieldExtensions.swift */,
680A62112A5F475D004C21A4 /* ViewExtensions.swift */,
);
path = Utils;
sourceTree = "<group>";
};
682D06992A5487D200EA4745 = {
isa = PBXGroup;
children = (
@ -95,8 +108,8 @@
682D06A42A5487D200EA4745 /* Luto */ = {
isa = PBXGroup;
children = (
683805182A57354900CEF29C /* UI */,
682D06A52A5487D200EA4745 /* LutoApp.swift */,
682D06A72A5487D200EA4745 /* ContentView.swift */,
682D06A92A5487D500EA4745 /* Assets.xcassets */,
682D06AE2A5487D500EA4745 /* Luto.entitlements */,
682D06AB2A5487D500EA4745 /* Preview Content */,
@ -129,6 +142,15 @@
path = LutoUITests;
sourceTree = "<group>";
};
683805182A57354900CEF29C /* UI */ = {
isa = PBXGroup;
children = (
682D06A72A5487D200EA4745 /* MainView.swift */,
680A620F2A5F4739004C21A4 /* Utils */,
);
path = UI;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -259,7 +281,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
682D06A82A5487D200EA4745 /* ContentView.swift in Sources */,
682D06A82A5487D200EA4745 /* MainView.swift in Sources */,
6838051A2A57356700CEF29C /* TextFieldExtensions.swift in Sources */,
680A62122A5F475D004C21A4 /* ViewExtensions.swift in Sources */,
682D06A62A5487D200EA4745 /* LutoApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -1,6 +1,15 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0",
"green" : "205",
"red" : "255"
}
},
"idiom" : "universal"
}
],

View File

@ -1,26 +0,0 @@
//
// ContentView.swift
// Luto
//
// Created by Pierre Boulc'h on 04/07/2023.
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

View File

@ -9,9 +9,51 @@ import SwiftUI
@main
struct LutoApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
WindowGroup(id: "MainWindow") {
MainView(taskTitle: "", taskDescription: "", listTask: [])
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
private var statusItem: NSStatusItem!
private var popover: NSPopover!
@MainActor func applicationDidFinishLaunching(_ notification: Notification) {
if let window = NSApplication.shared.windows.first {
window.close()
}
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let statusButton = statusItem.button {
statusButton.image = NSImage(systemSymbolName: "brain", accessibilityDescription: "Chart Line")
statusButton.action = #selector(togglePopover)
}
self.popover = NSPopover()
self.popover.contentSize = NSSize(width: 500, height: 500)
self.popover.behavior = .transient
self.popover.contentViewController = NSHostingController(rootView: MainView(taskTitle: "", taskDescription: "", listTask: [])
)
}
@objc func togglePopover() {
if let button = statusItem.button {
if popover.isShown {
self.popover.performClose(nil)
} else {
popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
}
}
}
}

94
Luto/UI/MainView.swift Normal file
View File

@ -0,0 +1,94 @@
//
// ContentView.swift
// Luto
//
// Created by Pierre Boulc'h on 04/07/2023.
//
import SwiftUI
import AppKit
struct MainView: View {
@State var taskTitle = ""
@State var taskDescription = ""
@State var listTask: [String] = []
@State var showAdditionnalFields = false
@FocusState private var titleFieldInFocus: Bool
@FocusState private var descriptionFieldInFocus: Bool
var body: some View {
VStack {
Text("Mes tâches")
LazyVStack(alignment: .leading) {
ForEach(Array(listTask.enumerated()), id: \.offset) { index, task in
HStack {
Text("\(task)")
.padding(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 8))
Spacer()
Button(action: {
removeTask(index: index)
}, label: {
Image(systemName: "trash")
}).buttonStyle(PlainButtonStyle()).padding(12)
.foregroundColor(.accentColor)
}.border(width: 5, edges: [.leading], color: .accentColor)
}
}
Spacer()
HStack {
TextField("Titre de la tâche ...", text: $taskTitle).focused($titleFieldInFocus).onChange(of: titleFieldInFocus) { isFocused in
if !isFocused {
withAnimation {
showAdditionnalFields = true
}
}
}.onSubmit {
addTask()
}
Button(action: {
addTask()
}, label: {
Image(systemName: "plus")
}).buttonStyle(PlainButtonStyle()).padding(12)
.background(Color.accentColor)
.foregroundColor(.black)
.cornerRadius(24)
}.textFieldStyle(OvalTextFieldStyle())
if showAdditionnalFields {
TextField("Description", text: $taskDescription).focused($descriptionFieldInFocus)
}
}
.padding()
.frame(width: 500, height: 500)
}
func addTask() {
if !taskTitle.isEmpty {
withAnimation {
listTask.append(taskTitle)
}
taskTitle = ""
}
}
func removeTask(index: Int) {
_ = withAnimation {
listTask.remove(at: index)
}
}
init(taskTitle: String = "", taskDescription: String = "", listTask: [String]) {
self.taskTitle = taskTitle
self.taskDescription = taskDescription
self.listTask = listTask
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView(taskTitle: "", taskDescription: "", listTask: [])
}
}

View File

@ -0,0 +1,32 @@
//
// TextFieldExtensions.swift
// Luto
//
// Created by Pierre Boulc'h on 06/07/2023.
//
import Foundation
import SwiftUI
struct OvalTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<Self._Label>) -> some View {
configuration
.textFieldStyle(.plain)
.colorMultiply(.gray)
.padding(10)
.tint(.gray)
.foregroundColor(.black)
.background(.white)
.cornerRadius(20)
.shadow(color: .accentColor, radius: 2)
}
}
extension NSTextField {
open override var focusRingType: NSFocusRingType {
get { .none }
set { }
}
}

View File

@ -0,0 +1,31 @@
//
// ViewExtensions.swift
// Luto
//
// Created by Pierre Boulc'h on 12/07/2023.
//
import Foundation
import SwiftUI
extension View {
func border(width: CGFloat, edges: [Edge], color: Color) -> some View {
overlay(EdgeBorder(width: width, edges: edges).foregroundColor(color))
}
}
struct EdgeBorder: Shape {
var width: CGFloat
var edges: [Edge]
func path(in rect: CGRect) -> Path {
edges.map { edge -> Path in
switch edge {
case .top: return Path(.init(x: rect.minX, y: rect.minY, width: rect.width, height: width))
case .bottom: return Path(.init(x: rect.minX, y: rect.maxY - width, width: rect.width, height: width))
case .leading: return Path(.init(x: rect.minX, y: rect.minY, width: width, height: rect.height))
case .trailing: return Path(.init(x: rect.maxX - width, y: rect.minY, width: width, height: rect.height))
}
}.reduce(into: Path()) { $0.addPath($1) }
}
}