├── Package.swift ├── README.md └── src └── Element ├── css └── utils │ ├── CSS.swift │ ├── CSSFileParser.swift │ ├── CSSLinkResolver.swift │ ├── CSSParser.swift │ └── CSSPropertyParser.swift ├── skin ├── Skin.swift ├── Skinable.swift ├── graphic │ ├── GraphicSkin.swift │ └── utils │ │ ├── GraphicModifier.swift │ │ ├── GraphicSkin+Extensions.swift │ │ ├── GraphicSkinModifier.swift │ │ ├── GraphicSkinParser.swift │ │ └── GraphicSkinUtils.swift ├── text │ ├── TextSkin+Extensions.swift │ ├── TextSkin.swift │ └── TextSkinable.swift └── utils │ ├── SkinFactory.swift │ ├── SkinManager.swift │ ├── SkinModifier.swift │ ├── SkinParser.swift │ ├── SkinResolver.swift │ └── SkinStates.swift ├── style ├── Stylable.swift ├── Style.swift ├── collection │ ├── StyleCollection.swift │ ├── StyleCollectionKind.swift │ └── utils │ │ ├── StyleCollectionKindExtensions.swift │ │ ├── StyleCollectionModifier.swift │ │ └── StyleCollectionParser.swift ├── manager │ ├── StyleManager.swift │ └── utils │ │ ├── StyleManagerModifier.swift │ │ ├── StyleManagerParser.swift │ │ └── StyleManagerUtils.swift ├── property │ ├── StyleProperty.swift │ ├── StylePropertyKind.swift │ └── utils │ │ ├── StyleMetricParser.swift │ │ ├── StylePropertyAsserter.swift │ │ ├── StylePropertyExtensions.swift │ │ └── StylePropertyParser.swift ├── selector │ ├── Selector.swift │ ├── SelectorKind.swift │ ├── SelectorWeight.swift │ ├── StyleWeight.swift │ ├── WeightedStyle.swift │ ├── WeightedStyleAsserter.swift │ └── utils │ │ ├── SelectorAsserter.swift │ │ ├── SelectorExtensions.swift │ │ ├── SelectorParser.swift │ │ └── StyleAsserter.swift └── utils │ ├── LiveEdit.swift │ ├── StylableExtension.swift │ ├── StyleCache.swift │ ├── StyleExtensions.swift │ ├── StyleModifier.swift │ ├── StyleParser.swift │ ├── StyleResolver.swift │ ├── StyleResolverUtils.swift │ └── StyleWatcher.swift ├── ui ├── Element.swift ├── ElementKind.swift ├── button │ ├── Button.swift │ ├── TextButton.swift │ ├── check │ │ ├── CheckButton.swift │ │ ├── CheckButton2.swift │ │ ├── Checkable.swift │ │ └── event │ │ │ ├── CheckEvent.swift │ │ │ └── CheckEvent2.swift │ ├── checkboxbutton │ │ ├── CheckBoxButton.swift │ │ ├── CheckBoxButton2.swift │ │ └── SelectCheckBoxButton.swift │ ├── events │ │ ├── ButtonEvent.swift │ │ └── SelectEvent.swift │ ├── radio │ │ └── RadioButton.swift │ ├── select │ │ ├── SelectButton.swift │ │ └── Selectable.swift │ └── text │ │ ├── CheckTextButton.swift │ │ └── SelectTextButton.swift ├── combobox │ ├── ComboBox.swift │ ├── event │ │ └── ComboBoxEvent.swift │ └── utils │ │ ├── ComboBoxModifier.swift │ │ ├── ComboBoxParser.swift │ │ └── ComboBoxWin.swift ├── list │ ├── DEPRECATED │ │ ├── abstract │ │ │ ├── FastListable3.swift │ │ │ ├── Listable3.swift │ │ │ ├── Listable3Extensions.swift │ │ │ └── utils │ │ │ │ ├── List3Modifier.swift │ │ │ │ └── List3Parser.swift │ │ ├── concrete │ │ │ └── list │ │ │ │ ├── ElasticScrollList3.swift │ │ │ │ ├── ElasticSlideScrollList3.swift │ │ │ │ ├── FastList3.swift │ │ │ │ ├── List3.swift │ │ │ │ ├── ScrollFastList3.swift │ │ │ │ ├── ScrollList3.swift │ │ │ │ ├── SlideList3.swift │ │ │ │ ├── SlideScrollFastList3.swift │ │ │ │ ├── SlideScrollList3.swift │ │ │ │ ├── events │ │ │ │ └── ListEvent.swift │ │ │ │ ├── fast │ │ │ │ ├── ElasticScrollFastList3.swift │ │ │ │ ├── ElasticSlideScrollFastList3.swift │ │ │ │ ├── SlideFastList3.swift │ │ │ │ └── abstract │ │ │ │ │ ├── ElasticScrollableFastListable3.swift │ │ │ │ │ ├── ElasticSlidableScrollableFastListable3.swift │ │ │ │ │ ├── ScrollableFastListable3.swift │ │ │ │ │ └── SlideableScrollableFastListable3.swift │ │ │ │ └── utils │ │ │ │ ├── ElasticUtils.swift │ │ │ │ ├── FastList3Modifier.swift │ │ │ │ ├── FastList3Parser.swift │ │ │ │ └── SliderListUtils.swift │ │ └── extensions │ │ │ └── FastListable3Extensions.swift │ ├── utils │ │ ├── MoverGroup.swift │ │ └── ScrollableUtils.swift │ └── v5 │ │ └── list │ │ ├── composition │ │ ├── ElasticScrollList5.swift │ │ ├── ElasticScrollerFastList5.swift │ │ ├── ElasticSliderScrollerFastList5.swift │ │ ├── ElasticSliderScrollerList5.swift │ │ ├── ScrollerFastList5.swift │ │ ├── SliderScrollerFastList5.swift │ │ ├── SliderScrollerList5.swift │ │ ├── handlers │ │ │ ├── ScrollerFastListHandler.swift │ │ │ ├── SliderScrollerFastListHandler.swift │ │ │ ├── SliderScrollerHandler.swift │ │ │ ├── decorator │ │ │ │ ├── ElasticDecorator.swift │ │ │ │ ├── FastListableDecorator.swift │ │ │ │ ├── ProgressableDecorator.swift │ │ │ │ └── SlidableDecorater.swift │ │ │ └── elastic │ │ │ │ ├── ElasticScrollerFastListHandler.swift │ │ │ │ ├── ElasticScrollerHandler.swift │ │ │ │ ├── ElasticSliderScrollerFastListHandler.swift │ │ │ │ └── ElasticSliderScrollerHandler.swift │ │ └── protocol │ │ │ ├── Elastic5+Fast+Extensions.swift │ │ │ ├── Elastic5+Slidable5+FastListable5+Extensions.swift │ │ │ └── Elastic5.swift │ │ ├── fast │ │ ├── FastList5+Extensions.swift │ │ ├── FastList5.swift │ │ ├── protocol │ │ │ ├── FastListable5+Extensions.swift │ │ │ └── FastListable5.swift │ │ └── utils │ │ │ └── FastListHandler.swift │ │ ├── list │ │ ├── List5.swift │ │ └── protocol │ │ │ └── Listable5.swift │ │ ├── progress │ │ ├── ProgressableList5.swift │ │ ├── protocol │ │ │ └── Progressable5.swift │ │ └── utils │ │ │ └── ProgressHandler.swift │ │ ├── scroll │ │ ├── ScrollerList5.swift │ │ ├── protocol │ │ │ └── Scrollable5.swift │ │ └── utils │ │ │ └── ScrollHandler.swift │ │ └── slider │ │ ├── Slidable5+Extensions.swift │ │ ├── SliderList5.swift │ │ └── protocol │ │ └── Slidable5.swift ├── other │ ├── ProgressIndicator.swift │ ├── graph │ │ ├── Graph.swift │ │ ├── GraphLine.swift │ │ ├── GraphUtils.swift │ │ └── bar │ │ │ └── BarGraph.swift │ ├── misc │ │ ├── FilePicker.swift │ │ └── abstract │ │ │ ├── Disablable.swift │ │ │ ├── Focusable.swift │ │ │ ├── Lable.swift │ │ │ └── SkinStateChangeable.swift │ └── view │ │ ├── DEPRECATED │ │ ├── abstract │ │ │ ├── Containable3.swift │ │ │ ├── Elastic3.swift │ │ │ ├── Progressable3.swift │ │ │ ├── Scrollable3.swift │ │ │ └── Slidable3.swift │ │ ├── composition │ │ │ ├── ElasticScrollable3.swift │ │ │ ├── ElasticSlidableScrollable3.swift │ │ │ └── SlidableScrollable3.swift │ │ └── concrete │ │ │ ├── ContainerView3.swift │ │ │ ├── ElasticScrollView3.swift │ │ │ ├── ElasticSlideScrollView3.swift │ │ │ ├── ScrollView3.swift │ │ │ ├── SlideScrollView3.swift │ │ │ └── SlideView3.swift │ │ └── v5 │ │ ├── ContainerView5.swift │ │ ├── ElasticScrollerView5.swift │ │ ├── ElasticSliderScrollerView5.swift │ │ ├── ProgressableView5.swift │ │ ├── ScrollerView5.swift │ │ ├── SliderScrollerView5.swift │ │ ├── SliderView5.swift │ │ └── protocol │ │ └── Containable5.swift ├── slider │ ├── Slider.swift │ ├── VolumeSlider.swift │ ├── event │ │ └── SliderEvent.swift │ ├── rb │ │ └── Thumb.swift │ └── utils │ │ └── SliderParser.swift ├── spinner │ ├── LeverSpinner.swift │ └── event │ │ └── SpinnerEvent.swift ├── stepper │ ├── LeverStepper.swift │ ├── Stepper.swift │ └── event │ │ └── StepperEvent.swift ├── text │ ├── SliderTextArea.swift │ ├── Text.swift │ ├── TextArea.swift │ ├── TextInput.swift │ └── utils │ │ └── TextKind.swift ├── treelist │ ├── DEPRECATED │ │ └── TreeList3.swift │ ├── TreeList5.swift │ ├── events │ │ └── TreeListEvent.swift │ └── utils │ │ ├── TreeList3AdvanceModifier.swift │ │ ├── TreeList3Asserter.swift │ │ ├── TreeList3Item.swift │ │ ├── TreeList3Modifier.swift │ │ ├── TreeList3Parser.swift │ │ ├── TreeList3Utils.swift │ │ ├── TreeListable3+Extensions.swift │ │ ├── TreeListable3.swift │ │ ├── dp │ │ ├── TreeDP+Extensions.swift │ │ ├── TreeDP.swift │ │ ├── TreeDPAsserter.swift │ │ ├── TreeDPModifier.swift │ │ └── TreeDPParser.swift │ │ ├── handler │ │ └── TreeListHandler.swift │ │ └── tree │ │ ├── Tree.swift │ │ └── utils │ │ ├── HashList2Modifier.swift │ │ ├── Tree+Extensions.swift │ │ ├── TreeAsserter.swift │ │ ├── TreeConverter.swift │ │ ├── TreeKind.swift │ │ ├── TreeModifier.swift │ │ ├── TreeParser.swift │ │ └── TreeUtils.swift └── window │ ├── TranslucentWin.swift │ ├── Window.swift │ └── view │ ├── CustomView.swift │ ├── PopupView.swift │ ├── TitleView.swift │ └── WindowView.swift └── utils ├── Container.swift ├── ElementExtenions.swift ├── ElementModifier.swift ├── ElementParser.swift ├── Section.swift ├── check ├── CheckModifier.swift └── CheckParser.swift ├── group ├── CheckGroup.swift ├── SelectGroup.swift ├── event │ ├── CheckGroupEvent.swift │ └── SelectGroupEvent.swift └── utils │ ├── SelectGroupExtensions.swift │ ├── SelectGroupModifier.swift │ └── SelectGroupParser.swift └── select ├── SelectAsserter.swift ├── SelectModifier.swift └── SelectParser.swift /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "Element", 5 | dependencies: [ 6 | .Package(url: "https://github.com/eonist/swift-utils.git", Version(0, 0, 0, prereleaseIdentifiers: ["alpha", "6"])) 7 | ], 8 | exclude: ["README.md"] 9 | ) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![MIT Status](https://img.shields.io/badge/License-MIT-lightgrey.svg?maxAge=2592000) ![platform](https://img.shields.io/badge/OS-macOS-blue.svg?maxAge=2592000) ![Lang](https://img.shields.io/badge/Swift-4-orange.svg) [![SPM compatible](https://img.shields.io/badge/SPM-compatible-orange.svg)](https://github.com/apple/swift-package-manager) [![codebeat badge](https://codebeat.co/badges/2de7a2a5-91d5-401e-8913-8f1993affd55)](https://codebeat.co/projects/github-com-eonist-element) 2 | 3 | StyleKit - UI framework for OSX | Product Hunt 4 | 5 | ### Description: 6 | Programmatic UI Framework for macOS. Swift handles app logic, CSS/SVG handles design and JSON handles struture. 7 | 8 | img 9 | 10 | ### Installation: 11 | **Step 1:** Add this to your Package.swift [Tutorial](http://stylekit.org/blog/2017/02/05/Xcode-and-spm/) 12 | 13 | ```swift 14 | import PackageDescription 15 | let package = Package( 16 | name: "AwesomeApp", 17 | dependencies: [ 18 | .Package(url: "https://github.com/eonist/Element.git", Version(0, 0, 0, prereleaseIdentifiers: ["alpha", "5"])) 19 | ] 20 | ) 21 | ``` 22 | 23 | **Step 2:** In AppDelegate.swift add this to the top ``@testable import Element`` and ``@testable import Utils``and this inside the ``applicationDidFinishLaunching`` method: 24 | 25 | ```swift 26 | StyleManager.addStyle("Button{fill:blue;}") 27 | let btn = Button(100,20) 28 | let window = NSApp.windows[0] 29 | window.contentView = InteractiveView()/*TopLeft orientation*/ 30 | window.contentView?.addSubview(btn) 31 | btn.addHandler(.upInside) = { (event:ButtonEvent) in 32 | Swift.print("hello world") 33 | } 34 | ``` 35 | 36 | You can also compile Element as a regular .framework instructions [here](https://github.com/eonist/Element/wiki/framework-instructions) 37 | 38 | ### Resources: 39 | - Simple example app made with Element: [Stash](https://github.com/stylekit/stash) 40 | - Library of example code for each component in Element: [Explorer](https://github.com/stylekit/explorer) 41 | - Default macOS styles to get you started: [ElCapitan](https://github.com/stylekit/ElCapitan) 42 | 43 | ### iOS: 44 | Element for 📱 is in the works [here](https://github.com/stylekit/Element-iOS) 45 | img 46 | 47 | ## More... 48 | More about Element 👉 [wiki](https://github.com/eonist/Element/wiki) 49 | 50 | ## Sponsors: 51 | [img 52 | ](https://www.jetbrains.com/objc/) 53 | -------------------------------------------------------------------------------- /src/Element/skin/Skinable.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Rename to Skinable or SkinKind 5 | */ 6 | protocol Skinable:class{ 7 | typealias HasChanged = (style:Bool,state:Bool,size:Bool) 8 | /*Implicit getters / setters*/ 9 | func setStyle(_ style:Stylable) 10 | func setSkinState(_ state:String) 11 | func setSize(_ width:CGFloat, _ height:CGFloat) 12 | // func getWidth()->CGFloat 13 | // func getHeight()->CGFloat 14 | /*Getters / Setters*/ 15 | var decoratables:[GraphicDecoratableKind]{get} 16 | var style:Stylable?{get} 17 | var state:String{get} 18 | var parent:ElementKind?{get}/*We use ElementKind here instead of Element because sometimes we need to use Window which is not an Element but impliments IElement*/ 19 | // var width:CGFloat?{get} 20 | // var height:CGFloat?{get} 21 | var hasChanged:HasChanged {get} 22 | // var skinSize:CGSize?{get set} 23 | // var hasStyleChanged:Bool{get} 24 | // var hasStateChanged:Bool{get} 25 | // var hasSizeChanged:Bool{get} 26 | } 27 | extension Skinable{ 28 | var parent:ElementKind? {//rename to element, get rid of parent 29 | guard let element = (self as? NSView)?.superview as? ElementKind else {fatalError("Element is not elementKind: ")}//superview 30 | return element 31 | }//parent element sort of.//TODO: ⚠️️ rename to parent? 32 | } 33 | -------------------------------------------------------------------------------- /src/Element/skin/graphic/utils/GraphicModifier.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class GraphicModifier { 5 | /** 6 | * TODO: Fill and linestyle should be graphic spessific see original code 7 | */ 8 | static func applyProperties(_ decoratable:GraphicDecoratableKind,_ fillStyle:FillStyleKind,_ lineStyle:LineStylable?,_ offsetType:OffsetType)->GraphicDecoratableKind { 9 | decoratable.graphic.fillStyle = fillStyle 10 | decoratable.graphic.lineStyle = lineStyle 11 | decoratable.graphic.lineOffsetType = offsetType 12 | return decoratable 13 | } 14 | /** 15 | * New 16 | * PARAM: roation is in deg -90 90 180 0 etc 17 | * NOTE: possible future normalization: Trig.normalize2(rotation * ㎭)/*between -π and π*/ 18 | */ 19 | static func applyRotation(_ decoratable:GraphicDecoratableKind,_ rotation:CGFloat, _ pivot:CGPoint){ 20 | decoratable.graphic.layer?.rotate(rotation, pivot) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Element/skin/graphic/utils/GraphicSkin+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | extension GraphicSkin{ 5 | /** 6 | * Draws decoratable 7 | */ 8 | func drawDecoratable(_ depth:Int){ 9 | if hasChanged.size { 10 | GraphicSkinModifier.setSize(decoratables[depth], GraphicSkinParser.size(self,depth)) 11 | }/*Do sizing of the sizable here*/ 12 | if hasChanged.state || hasChanged.style { 13 | updateAppearance(decoratables[depth], depth) 14 | } 15 | if hasChanged.size || hasChanged.state || hasChanged.style { 16 | decoratables[depth].draw()/*<--Init the actual draw call, you only want to draw once bc performance*/ 17 | } 18 | } 19 | /** 20 | * Refreshes the look of the "decoratable" 21 | */ 22 | func updateAppearance(_ decoratable:GraphicDecoratableKind,_ depth:Int){ 23 | GraphicSkinModifier.applyStyle(decoratable,self,depth)/*derives and applies style to the decoratable*/ 24 | decoratable.get(RectGraphic.self)?.setSizeValue(GraphicSkinParser.size(self,depth)) 25 | decoratable.get(RoundRectGraphic.self)?.fillet = StyleMetricParser.fillet(self,depth)/*fillet*/ 26 | decoratable.get(AssetDecorator.self)?.assetURL = StylePropertyParser.asset(self,depth)/*Svg*/ 27 | decoratable.get(DropShadowDecorator.self)?.dropShadow = StylePropertyParser.dropShadow(self,depth)/*dropshadow*/ 28 | GraphicSkinModifier.setRotation(decoratable, self, depth) 29 | _ = SkinModifier.align(self,decoratables[depth] as! Positional,depth) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Element/skin/graphic/utils/GraphicSkinModifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class GraphicSkinModifier{ 5 | /** 6 | * beta 7 | */ 8 | static func setSize(_ sizableDecorator:GraphicDecoratableKind,_ size:CGSize){ 9 | (sizableDecorator as! Sizable).setSizeValue(size) 10 | //sizableDecorator.draw() 11 | } 12 | static func setRotation(_ decoratable:GraphicDecoratableKind,_ skin:Skinable,_ depth:Int){ 13 | if let rotation:CGFloat = StyleMetricParser.rotation(skin,depth){ 14 | let size:CGSize = (decoratable as! Sizable).size 15 | let pos:CGPoint = (decoratable as! Positional).pos 16 | let rect:CGRect = CGRect(pos, size) 17 | disableAnim{ 18 | GraphicModifier.applyRotation(decoratable, rotation, rect.center) 19 | } 20 | } 21 | } 22 | /** 23 | * Applies style and lineOffset 24 | */ 25 | static func applyStyle(_ decoratable:GraphicDecoratableKind, _ graphicSkin:GraphicSkin,_ depth:Int){ 26 | let fillStyle:FillStyleKind = StylePropertyParser.fillStyle(graphicSkin,depth) 27 | let lineStyle:LineStylable? = StylePropertyParser.lineStyle(graphicSkin,depth) 28 | let lineOffsetType = StylePropertyParser.lineOffsetType(graphicSkin,depth) 29 | _ = GraphicModifier.applyProperties(decoratable,fillStyle ,lineStyle ,lineOffsetType)/*color or gradient*/ 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Element/skin/graphic/utils/GraphicSkinParser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | /** 5 | * Parser for "decoratable" 6 | */ 7 | class GraphicSkinParser{ 8 | /** 9 | * TODO: ⚠️️ Should just use the instance setSize function 10 | * TODO: ⚠️️ Should only be called if the size has actually changed 11 | */ 12 | static func size(_ skin:Skinable,_ depth:Int)->CGSize{ 13 | let padding:Padding = Padding()//StylePropertyParser.padding(self,depth) //StylePropertyParser.padding(self,depth);// :TODO: what about margin?<----not sure this is needed, the padding 14 | let width:CGFloat = GraphicSkinParser.width(skin,depth,padding) 15 | let height:CGFloat = GraphicSkinParser.height(skin,depth,padding) 16 | return CGSize(width,height) 17 | } 18 | /** 19 | * Derives the width from CSS 20 | */ 21 | static func width(_ skin:Skinable,_ depth:Int, _ padding:Padding) -> CGFloat { 22 | return (StyleMetricParser.width(skin,depth) ?? skin.parent?.frame.w ?? {fatalError("err")}()/*skin.skinSize!.width*/) + padding.hor// :TODO: only querry this if the size has changed? 23 | } 24 | /** 25 | * Derives the height from CSS 26 | */ 27 | static func height(_ skin:Skinable,_ depth:Int, _ padding:Padding) -> CGFloat { 28 | return (StyleMetricParser.height(skin,depth) ?? skin.parent?.frame.h ?? {fatalError("err")}() /*skin.skinSize!.height*/) + padding.ver// :TODO: only querry this if the size has changed? 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/Element/skin/text/TextSkin+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | extension TextSkin{ 5 | /** 6 | * Set the text and updates the skin 7 | * TODO: ⚠️️ Add more advance setText features like start and end etc 8 | */ 9 | func setText(_ text:String){ 10 | textField.stringValue = text 11 | hasTextChanged = true 12 | draw() 13 | } 14 | /** 15 | * Applies properties to textfield 16 | */ 17 | func applyProperties(_ textField:NSTextField){ 18 | let padding:Padding = StyleMetricParser.padding(self) 19 | let width:CGFloat = getWidth()//(StyleMetricParser.width(self) ?? {fatalError("err")}()) + padding.left + padding.right// :TODO: only querry this if the size has changed? 20 | let height:CGFloat = getHeight() + padding.top + padding.bottom //(StyleMetricParser.height(self) ?? {fatalError("err")}()) + padding.top + padding.bottom// :TODO: only querry this if the size has changed? 21 | let size = CGSize(width,height) 22 | // Swift.print("applyProperties.size: " + "\(size)") 23 | textField.frame.size = size 24 | 25 | super.frame.size = size//quick fix, this is a strange bug 26 | 27 | 28 | TextFieldModifier.applyTextFormat(textField,textFormat)/*applies the textFormat*/ 29 | let temp = TextFormatUtils.attributedStringValue(stringValue:textField.stringValue,textFormat:textFormat) 30 | textField.attributedStringValue = temp 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Element/skin/text/TextSkinable.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | typealias ITextSkin = TextSkinable 5 | protocol TextSkinable:class,Skinable { 6 | var textField:NSTextField{get} 7 | func setText(_ text:String) 8 | } 9 | -------------------------------------------------------------------------------- /src/Element/skin/utils/SkinFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /* 3 | * This is the default skinfactory for Element make your own or mix and match with decorator classes. 4 | * TODO: ⚠️️ Remove ...skin in the skin cosntant names 5 | * TODO: ⚠️️ Make SkinFactory work with Decorator pattern. mixing skins with each other etc (Extending is simpler and it works now) could be usefull when it comes to bevel dropshadow etc 6 | * TODO: ⚠️️ Create TextFormat skins multiline input pixel etc 7 | * TODO: ⚠️️ Create an EmptySkin that also 8 | */ 9 | class SkinFactory{ 10 | static var graphicsSkin:String = "graphicSkin"//TODO: ⚠️️ we could use Skin(GraphicSkin) instead of this variable 11 | static var textSkin:String = "textSkin" 12 | } 13 | -------------------------------------------------------------------------------- /src/Element/skin/utils/SkinManager.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | class SkinManager{ 3 | /** 4 | * Returns a new SkinClass instance 5 | */ 6 | static func skin(skinName:String, element:ElementKind, style:Stylable)->Skin?{ 7 | switch skinName{ 8 | case SkinFactory.graphicsSkin : 9 | return GraphicSkin(style, element.skinState/*, element*/) 10 | case SkinFactory.textSkin : 11 | return TextSkin(style, (element as! TextKind).initText, element.skinState/*, element*/) 12 | default: 13 | fatalError("NOT IMPLEMENTED YET") 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Element/skin/utils/SkinResolver.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /* 3 | * TODO: should be able to return an empty skin, sometimes an empty skin is what you want 4 | * TODO: use Vector for speed etc? 5 | */ 6 | class SkinResolver{ 7 | /** 8 | * Returns a Skin instance 9 | * TODO: ⚠️️ future additions? //resolveSkinFromStylabaleParents(stylable) || resolveSkinByClass(stylable) || resolveSkinBySuperClass(stylable) || resolveSkinByDeafultStyling(stylable) 10 | * TODO: ⚠️️ enable these additions when you have more controll over the Element FrameWork for now you need to throw error to debug 11 | */ 12 | static func skin(_ element:ElementKind)->Skin{ 13 | let style:Stylable = StyleResolver.style(element) 14 | let skinName:String = style.getValue("skin") as? String ?? Utils.skinName(element) 15 | return SkinManager.skin(skinName:skinName,element:element,style:style) ?? {fatalError("SKINRESOLVER: NO SKIN COULD BE RESOLVED FOR ELEMENT BY THE ID: ")}()/*Throws an error message if a skin cant be resolved (with usefull information for debugging)*/ 16 | } 17 | } 18 | private class Utils{ 19 | /** 20 | * Returns a skin name based on what class type the element parent is 21 | */ 22 | static func skinName(_ element:ElementKind)->String { 23 | var skinName:String; 24 | switch element.getClassType(){ 25 | case "Text":skinName = SkinFactory.textSkin 26 | default:skinName = SkinFactory.graphicsSkin 27 | } 28 | return skinName 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Element/skin/utils/SkinStates.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * TODO: ⚠️️ These will probably be upgraded to enums soon, since swift has awesome syntactic support for enums 4 | * TODO: ⚠️️ Or you could extens String and set receiving params to take SkinStates and then do: .over etc use class var if you need to override 5 | */ 6 | enum SkinStates {//rename to SkinStateTypes maybe? 7 | /*Core*/ 8 | static let none:String = ""/*aka: idle*/ 9 | static let over:String = "over" 10 | static let down:String = "down" 11 | /*Other*/ 12 | static let selected:String = "selected" 13 | static let checked:String = "checked" 14 | static let focused:String = "focused" 15 | static let disabled:String = "disabled" 16 | } 17 | /* 18 | enum SkinStates:String 19 | case None = "" 20 | case Over = "over" 21 | //etc 22 | */ 23 | -------------------------------------------------------------------------------- /src/Element/style/Stylable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol Stylable { 4 | var name:String {get} 5 | var selectors:[SelectorKind] {get} 6 | var styleProperties:[StylePropertyKind] {get set} 7 | } 8 | -------------------------------------------------------------------------------- /src/Element/style/Style.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * NOTE: The StyleProperty instances are stored in _styleProperties, and then you querry them by name and depth 4 | */ 5 | struct Style:Stylable{ 6 | var name:String/*Name of the class*/ 7 | var selectors:[SelectorKind]/*[Type#id:state.class]*/ 8 | var styleProperties:[StylePropertyKind]/*The styleProperties of this style*/ 9 | init(_ name:String = "",_ selectors:[SelectorKind] = [], _ styleProperties:[StylePropertyKind] = []){ 10 | self.name = name 11 | self.selectors = selectors 12 | self.styleProperties = styleProperties 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Element/style/collection/StyleCollection.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * NOTE: a datastorage class ("bean") parsing should be done elsewhere! 5 | * TODO: get rid of name?!? 6 | */ 7 | struct StyleCollection:IStyleCollection{ 8 | var styles:[Stylable] = []//use dict instead? 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/style/collection/StyleCollectionKind.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | typealias IStyleCollection = StyleCollectionKind 4 | protocol StyleCollectionKind { 5 | var styles:[Stylable] {get set} 6 | } 7 | /*func addStyle(_ style:IStyle) 8 | func addStyles(_ styles:[IStyle]) 9 | func removeStyle(_ name:String)->IStyle? 10 | func getStyle(_ name:String)->IStyle? 11 | func getStyleAt(_ index:Int)->IStyle?*/ 12 | -------------------------------------------------------------------------------- /src/Element/style/collection/utils/StyleCollectionKindExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | extension StyleCollectionKind{ 5 | /** 6 | * Adds every style in an array to the_styles array (uses the addStyle method to do it so that it checks for duplicates) 7 | * NOTE: the reason we dont move the following core methods into StyleCollectionModifier is because they are used alot and are not that complex 8 | */ 9 | mutating func addStyles(_ styles:[Stylable]){ 10 | styles.forEach{style in addStyle(style)} 11 | } 12 | /** 13 | * Adds a style to the StyleCollection instance 14 | * PARAM: style: IStyle 15 | */ 16 | mutating func addStyle(_ style:Stylable){ 17 | if let matchIdx = styles.index(where: {$0.name == style.name}){ 18 | StyleModifier.combine(&styles[matchIdx], style)/*<--was merge, but styles that comes later in the array with the same name should hard-override properties, not soft-override like it was previously*/ 19 | } 20 | styles.append(style) 21 | } 22 | /** 23 | * TODO: One Could change this to return nothing 24 | * RETURNS: the removed Style 25 | */ 26 | mutating func removeStyle(_ name:String)->Stylable?{ 27 | if let idx = styles.index(where: {$0.name == name}){ 28 | return ArrayModifier.splice2(&styles,idx,1).first 29 | } 30 | return nil/*could also return the index i guess -1 instead of nil, do we need to return anything ? its nice to be able to assert if something was removed with nil coalesing as it is now*/ 31 | } 32 | /** 33 | * NOTE: We can't use a for each loop here since it returns inside the loop clause, and forEach doesn't allow for that 34 | */ 35 | func getStyle(_ name:String)->Stylable?{ 36 | return styles.first(where: {$0.name == name}) 37 | } 38 | /** 39 | * Convenience 40 | */ 41 | func getStyleAt(_ index:Int)->Stylable?{ 42 | return styles[index] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Element/style/collection/utils/StyleCollectionModifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class StyleCollectionModifier { 4 | /** 5 | * Merges PARAM: a with PARAM: b (Adds styles that are not present in PARAM: a, and merges but does not override styles that have the same name) 6 | */ 7 | static func merge(_ a:inout IStyleCollection,_ b:IStyleCollection) { 8 | b.styles.forEach { styleB in 9 | if var match = a.styles.first(where:{styleA in styleA.name == styleB.name}){ 10 | StyleModifier.merge(&match, styleB) 11 | }else{ 12 | a.addStyle(styleB) 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Element/style/collection/utils/StyleCollectionParser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /*@testable import Utils*/ 3 | 4 | class StyleCollectionParser { 5 | /** 6 | * Returns an array of style names 7 | */ 8 | static func styleNames(_ styleCollection:IStyleCollection) -> [String]{ 9 | return styleCollection.styles.map{ style in 10 | return style.name 11 | } 12 | } 13 | /** 14 | * Describes the stylecollection content 15 | * Note can you use the ObjectDescriber in place of this class? 16 | */ 17 | static func describe(_ styleCollection:IStyleCollection) { 18 | func printStyleProperties(_ style:Stylable) { 19 | Swift.print(":"+style.name) 20 | var propertyNames:[String] = StyleParser.stylePropertyNames(style) 21 | (0..Stylable?{ 10 | return self.styles.first(where:{$0.name == name}) 11 | } 12 | static func getStyle(_ index:Int)->Stylable?{ 13 | return styles[safe:index]//<-⚠️️ just added safe 14 | } 15 | /** 16 | * New 17 | */ 18 | static func getStylePropVal(_ styleName:String, _ stylePropertyName:String, _ depth:Int = 0) -> Any?{ 19 | return StyleManager.getStyle(styleName)?.getStyleProperty(stylePropertyName)?.value 20 | } 21 | /** 22 | * New 23 | */ 24 | static func index(_ name:String) -> Int?{ 25 | return styles.index(where: {$0.name == name}) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Element/style/property/StyleProperty.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * Depth should really be UInt, but since apple doesnt use/like UInt and the support isn't that great we use Int 4 | */ 5 | struct StyleProperty:StylePropertyKind { 6 | var name:String 7 | var value:Any 8 | var depth:Int 9 | init(_ name:String,_ value:Any,_ depth:Int = 0){ 10 | self.name = name 11 | self.value = value 12 | self.depth = depth 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Element/style/property/StylePropertyKind.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol StylePropertyKind { 4 | var value:Any {get set} 5 | var name:String {get set} 6 | var depth:Int {get} 7 | } 8 | -------------------------------------------------------------------------------- /src/Element/style/property/utils/StylePropertyAsserter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class StylePropertyAsserter { 5 | /** 6 | * Test if fillet is nil instead, 0 should return true actually. Think over state vs idle state etc 7 | */ 8 | static func hasFillet(_ skin:Skinable,_ depth:Int = 0)->Bool { 9 | let fillet:Fillet = StyleMetricParser.fillet(skin,depth) 10 | return !(fillet.topLeft == 0 && fillet.topRight == 0 && fillet.bottomLeft == 0 && fillet.bottomRight == 0) 11 | } 12 | static func hasGradient(_ skin:Skinable,_ depth:Int = 0)->Bool { 13 | let value = StylePropertyParser.value(skin, CSS.Other.fill,depth) 14 | return value is Gradient/*|| StylePropertyParser.value(skin, "line", depth) is Gradient*/ 15 | } 16 | static func hasAsset(_ skin:Skinable,_ depth:Int = 0)->Bool { 17 | return StylePropertyParser.value(skin, CSS.Other.fill,depth) is [Any]//TODO: ⚠️️ I think you can do is Array here 18 | } 19 | static func hasDropShadow(_ skin:Skinable,_ depth:Int = 0)->Bool { 20 | let value = StylePropertyParser.value(skin, CSS.Other.drop_shadow,depth) 21 | /*You may need to do something like this: getGraphic().fillStyle.dynamicType is GradientFillStyle.Type*/ 22 | return value != nil/*this differes slightly from the original code, but was needed to support "none" as a dropshadow param in css*/ 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Element/style/property/utils/StylePropertyExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * NOTE: Variables that are Of type Any are a bit tricky because swift is a type safe language 5 | * NOTE: The commonly used types could be reused and then only have the custom "one of" classes in this method 6 | * IMPORTANT: ⚠️️ This must be located here because it belongs in the Element lib but uses the swift-utils lib 7 | */ 8 | extension StyleProperty:UnWrappable{ 9 | static func unWrap(_ xml:XML) -> T? { 10 | let name:String = unWrap(xml, "name")! 11 | let value:Any = UnWrap.any(xml,"value") 12 | let depth:Int = unWrap(xml, "depth")! 13 | return StyleProperty(name,value,depth) as? T 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Element/style/selector/Selector.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * NOTE: ObjectC already has a class named Selector that may conflict with this one, use: this syntax instead if you need to use the ObjC Selector: ObjectiveC.Selector 4 | */ 5 | struct Selector:SelectorKind{ 6 | let element:String/*Button*///should be nil 7 | let classIds:[String]/*.customButton*///should be nil 8 | let id:String/*#customButton*///should be nil 9 | let states:[String]/*:down*///should be nil 10 | init(_ element:String = "",_ classIds:[String] = [],_ id:String = "",_ states:[String] = []/*TODO: ""<-init with idle state?*/) { 11 | self.element = element 12 | self.classIds = classIds 13 | self.id = id 14 | self.states = states 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Element/style/selector/SelectorKind.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * TODO: ⚠️️ Remember to check out NSHipster CSS example 3 | */ 4 | protocol SelectorKind{ 5 | var element:String{get} 6 | var classIds:[String]{get} 7 | var id:String{get} 8 | var states:[String]{get} 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/style/selector/SelectorWeight.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * NOTE: "Selectorparser","WeightdStyleAsserter" and "StyleWeight" all make use of this class 4 | */ 5 | struct SelectorWeight { 6 | let weight:Int 7 | let hasId:Bool 8 | let hasElement:Bool 9 | let hasClassId:Bool 10 | let hasState:Bool 11 | let numOfSimilarClassIds:Int 12 | let stateWeight:Int 13 | init(_ weight:Int,_ hasId:Bool,_ hasElement:Bool,_ hasClassId:Bool,_ hasState:Bool,_ numOfSimilarClassIds:Int,_ stateWeight:Int) {// :TODO: shouldnt this be in this order: hasElement,hasClassId,classmatchcount,hasId,hasState has stateweight?? 14 | self.weight = weight 15 | self.hasId = hasId 16 | self.hasElement = hasElement 17 | self.hasClassId = hasClassId 18 | self.hasState = hasState 19 | self.numOfSimilarClassIds = numOfSimilarClassIds 20 | self.stateWeight = stateWeight 21 | } 22 | } -------------------------------------------------------------------------------- /src/Element/style/selector/StyleWeight.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * // :TODO: move into its own class? if other classses uses this code yes if not then no! 4 | */ 5 | struct StyleWeight { 6 | var selectorWeights:[SelectorWeight] 7 | var idWeight:Int = 0 8 | var elementWeight:Int = 0 9 | var classWeight:Int = 0 10 | var stateWeight:Int = 0 11 | init(_ selectorWeights:[SelectorWeight]) { 12 | self.selectorWeights = selectorWeights 13 | for selectorItemWeight:SelectorWeight in selectorWeights { 14 | self.elementWeight += selectorItemWeight.hasElement ? selectorItemWeight.weight : 0 15 | self.classWeight += selectorItemWeight.hasClassId ? selectorItemWeight.weight : 0 16 | self.idWeight += selectorItemWeight.hasId ? selectorItemWeight.weight : 0 17 | self.stateWeight += selectorItemWeight.hasState ? selectorItemWeight.weight : 0 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/style/selector/WeightedStyle.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | struct WeightedStyle:Stylable/*,Comparable*/{ 3 | /*private(set) internal */var styleWeight:StyleWeight 4 | var style:Stylable 5 | init(_ style:Stylable, _ styleWeight:StyleWeight) { 6 | self.styleWeight = styleWeight; 7 | self.style = style//super.init(style.name,style.selectors,style.styleProperties); 8 | } 9 | } 10 | extension WeightedStyle{ 11 | var name:String {return style.name} 12 | var selectors:[SelectorKind] {get {return style.selectors}} 13 | var styleProperties:[StylePropertyKind] {get{return style.styleProperties}set{style.styleProperties = newValue}} 14 | } 15 | /* 16 | func < (lhs: WeightedStyle, rhs: WeightedStyle) -> Bool { 17 | return lhs.someNumber < rhs.someNumber 18 | } 19 | func == (lhs: WeightedStyle, rhs: WeightedStyle) -> Bool { 20 | return lhs.someNumber == rhs.someNumber 21 | } 22 | */ 23 | -------------------------------------------------------------------------------- /src/Element/style/selector/utils/SelectorExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | extension Selector:UnWrappable{ 5 | /** 6 | * Converts xml to a Selector instance 7 | */ 8 | static func unWrap(_ xml:XML) -> T? { 9 | let element:String = unWrap(xml, "element") ?? "" 10 | let id:String = unWrap(xml, "id") ?? "" 11 | let classIds:[String?] = unWrap(xml, "classIds") 12 | let states:[String?] = unWrap(xml, "states") 13 | return Selector(element,classIds.flatMap{$0},id,states.flatMap{$0}) as? T 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Element/style/selector/utils/StyleAsserter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | class StyleAsserter { 3 | /** 4 | * Asserts if PARAM: style is a direct style of the PARAM: stack (the stack is derived from an Element instance) 5 | * NOTE: the opposite would be an indirect style 6 | */ 7 | static func direct(_ stack:[SelectorKind],_ style:Stylable)->Bool {//TODO: rename to isDirect, is it Necessary? StyleAsserter.direct is pretty self explanatory 8 | if let lastStackItem = stack.last, let lastSelector = style.selectors.last{ 9 | return SelectorAsserter.hasSimilarity(lastStackItem, lastSelector) 10 | } 11 | return false 12 | } 13 | static func alreadyExists(_ style:Stylable,_ styleProperty:StylePropertyKind) -> Bool{ 14 | return style.styleProperties.first(where: {$0.name == styleProperty.name && $0.depth == styleProperty.depth}) != nil 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Element/style/utils/LiveEdit.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class LiveEdit { 4 | /** 5 | * Removes styles that have been refreshed 6 | */ 7 | static func styles(_ stylesURL:String) -> [Stylable]{ 8 | let cssString:String = CSSFileParser.cssString(stylesURL) 9 | let styles = StyleManagerUtils.styles(cssString) 10 | if let cssStr = StyleManager.cssFiles[stylesURL]{/*check if the url already exists in the dictionary*/ 11 | // cssStr = CSSLinkResolver.resolveLinks(cssFile) 12 | let styles:[Stylable] = StyleManagerUtils.styles(cssStr) 13 | StyleManager.removeStyle(styles)/*if url exists then remove the styles that it represents*/ 14 | }else{/*If the url wasn't in the dictionary, then add it*/ 15 | StyleManager.cssFiles[stylesURL] = cssString//<--I'm not sure how this works, but it works 16 | } 17 | return styles 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Element/style/utils/StylableExtension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * TODO: ⚠️️ Add subScripts to get prop 4 | */ 5 | extension Stylable{ 6 | /** 7 | * Returns a style property by the name given 8 | * NOTE: returning nil is fine, no need to make a EmptyStyleProperty class, or is there? 9 | */ 10 | func getStyleProperty(_ stylePropName:String,_ depth:Int = 0)->StylePropertyKind?{ 11 | return styleProperties.first(where: {$0.name == stylePropName && $0.depth == depth}) 12 | } 13 | /** 14 | * NOTE: this function is not redundant, it's usefull for qucik access in some methods 15 | */ 16 | func getValue(_ stylePropName:String,_ depth:Int = 0) -> Any?{ 17 | return getStyleProperty(stylePropName,depth)?.value 18 | } 19 | /** 20 | * Add styleProperty 21 | */ 22 | mutating func addStyleProperty(_ styleProperty:StylePropertyKind) { 23 | styleProperties.append(styleProperty)//TODO: ⚠️️ this method was more elaborate, it checks if the item is original, if its not throw error, implement this when its time 24 | } 25 | /** 26 | * Adds styleProperties 27 | */ 28 | mutating func addStyleProperty(_ styleProperties:[StylePropertyKind]){ 29 | self.styleProperties = styleProperties.map{$0} 30 | } 31 | /** 32 | * NOTE: StyleParser.depthCount() uses this method 33 | */ 34 | func getStyleProperties(_ name:String)->[StylePropertyKind]{ 35 | return self.styleProperties.filter(){ styleProperty in 36 | styleProperty.name == name 37 | } 38 | } 39 | /** 40 | * NOTE: a benefit of having this method is that when used you can use the interface of the return type instantly 41 | */ 42 | func getStylePropertyAt(_ index:Int)->StylePropertyKind{ 43 | return styleProperties[index] 44 | } 45 | /** 46 | * NOTE: this method is here for convenience (methods that should be contained in an Utils class are thous that are seldom used) 47 | */ 48 | func getValueAt(_ index:Int)->Any{ 49 | return getStylePropertyAt(index).value 50 | } 51 | func describe(){ 52 | StyleParser.describe(self) 53 | } 54 | var clone:Stylable { 55 | return StyleModifier.clone(self) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Element/style/utils/StyleExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | extension Style:UnWrappable{ 5 | static var clear:Stylable = Style("clear",[],[StyleProperty("idleColor",0x000000),StyleProperty("idleOpacity",0)])//this won't work since it doesnt have any selectors 6 | init(_ id:String,_ styleProperty:StylePropertyKind){/*Convenience*/ 7 | self.init(id,[],[styleProperty]) 8 | } 9 | } 10 | extension Style{ 11 | /** 12 | * Converts xml to a Style instance 13 | * IMPORTANT: ⚠️️ this must be located here because it belongs in the Element lib but uses the swift-utils lib 14 | */ 15 | static func unWrap(_ xml:XML) -> T? { 16 | let name:String = unWrap(xml, "name")! 17 | let styleProperties:[StyleProperty?] = unWrap(xml, "styleProperties") 18 | let selectors:[Selector?] = unWrap(xml, "selectors") 19 | return Style(name,selectors.flatMap{$0},styleProperties.flatMap{$0}) as? T 20 | } 21 | /** 22 | * New 23 | * IMPORTANT: ⚠️️ You aperantly have to use Style, protocols are not mutable apperantly 24 | */ 25 | mutating func setStyleProperty(_ styleProp:StylePropertyKind){ 26 | StyleModifier.overrideStyleProperty(&self, styleProp) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Element/style/utils/StyleParser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * NOTE: One could use Vector for speed etc, but that would make the framework less readable for now 5 | * NOTE: If you ever need to find the absolute herarchy path to an instance use this: StyleParser.hierarchicalStyleName(someInstance);//SomeClass someInstance AnotherClass anotherInstance etc 6 | * TODO: ⚠️️ Make support for int and uint values for styles that are not numbers 7 | */ 8 | class StyleParser {// ⚠️️ TODO: rename to StyleResolver, it doesnt feel like a normal parser class not all the functions here!?!? 9 | /** 10 | * TODO: depthCount should probably be set when you are creating the Style instance, depthcount may change depending on the usage, think love preview or animation 11 | */ 12 | static func depthCount(_ style:Stylable)->Int{ 13 | let propertyNames:[String] = stylePropertyNames(style) 14 | let fillCount:Int = ArrayAsserter.has(propertyNames, "fill") ? style.getStyleProperties("fill").count : 0 15 | let lineCount:Int = ArrayAsserter.has(propertyNames, "line") ? style.getStyleProperties("line").count : 0 16 | return max(fillCount,lineCount) 17 | } 18 | /** 19 | * TODO: write documentation 20 | */ 21 | static func describe(_ style:Stylable){ 22 | Swift.print("StyleParser.describe()") 23 | Swift.print("style.name: " + style.name) 24 | style.styleProperties.forEach { styleProperty in 25 | var value:String = "" 26 | if(styleProperty.value is String || styleProperty.value is Double || styleProperty.value is Bool || styleProperty.value is UInt || styleProperty.value is Int){ 27 | value = "\(styleProperty.value)" 28 | }else { 29 | value = "\(styleProperty.value)" 30 | } 31 | Swift.print(" " + styleProperty.name + ":" + value + " depth:" + "\(styleProperty.depth)") 32 | } 33 | } 34 | /** 35 | * Returns an array populated with style property names 36 | */ 37 | static func stylePropertyNames(_ style:Stylable) -> [String]{ 38 | return style.styleProperties.map{$0.name} 39 | } 40 | /** 41 | * Returns the index of the styleProperty with PARAM: name, and PARAM: depth 42 | * PARAM: name the propertyname 43 | * IMPORTANT: ⚠️️ Returns -1 if item is not found, this method will be deleted in the future, and optional will be priotizied 44 | */ 45 | static func index(_ style:Stylable, _ name:String, _ depth:Int = 0) -> Int { 46 | return style.styleProperties.index(where: {$0.name == name && $0.depth == depth}) ?? -1 47 | } 48 | /** 49 | * New 50 | */ 51 | static func idx(_ style:Stylable, _ name:String, _ depth:Int = 0) -> Int? { 52 | return style.styleProperties.index(where: {$0.name == name && $0.depth == depth}) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Element/style/utils/StyleResolverUtils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class StyleResolverUtils { 4 | /** 5 | * Return an array of WeightedStyle instances 6 | * NOTE: while loading the Basic.css flat structure queries 97656 vs treestructure queries 13324 (this is why we use a treestructure querey technique) 7 | */ 8 | static func query(_ querySelectors:[SelectorKind],_ searchTree:[String:Any],_ cursor:Int = 0) -> [WeightedStyle]{ 9 | var weightedStyles:[WeightedStyle] = [] 10 | let querySelectorsCount = querySelectors.count/*optimization*/ 11 | //var styles:[IStyle] = [] 12 | for key in searchTree.keys { 13 | //print("key: " + key + " object: "+searchTree[key] + " at cursor: "+cursor); 14 | if(key == "style") {weightedStyles.append(WeightedStyle(searchTree[key] as! Stylable, StyleWeight([])))} 15 | else{ 16 | let keySelector:SelectorKind = SelectorParser.selector(key)/*expand the selectorString to a selector*/ 17 | for i in cursor.. for (var i : Int = cursor; i < querySelectorsCount; i++) { 18 | StyleResolver.styleLookUpCount += 1 19 | let querySelector:SelectorKind = querySelectors[i] 20 | if(SelectorAsserter.hasCommonality(keySelector, querySelector)){ 21 | //print("matching element found, keep digging deeper"); 22 | let result:[WeightedStyle] = query(querySelectors, searchTree[key] as! [String:Any],i+1) 23 | if(result.count > 0) {weightedStyles += result} 24 | } 25 | } 26 | } 27 | } 28 | return weightedStyles 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Element/style/utils/StyleWatcher.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class StyleWatcher { 5 | /** 6 | * IMPORTANT: ⚠️️ StyleManger.addStylesByURL("",true)<--liveEdit flag must be true to use this watch feature 7 | */ 8 | static func watch(_ folderURL:String,_ fileURL:String, _ view:NSView) { 9 | let fileWatcher = FileWatcher([folderURL.tildePath]) 10 | fileWatcher.callback = { event in 11 | if(event.fileChange && FilePathParser.fileExtension(event.path) == "css") {//assert for .css file changes, so that .ds etc doesnt trigger events etc 12 | Swift.print(event.description) 13 | Swift.print("update to the file happened: " + "\(event.path)") 14 | StyleManager.addStyle(url:fileURL,liveEdit:true) 15 | ElementModifier.refreshSkin(view as! ElementKind) 16 | ElementModifier.floatChildren(view) 17 | } 18 | } 19 | fileWatcher.start() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Element/ui/button/TextButton.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ get rid of LableKind 5 | */ 6 | class TextButton:Button,LableKind { 7 | lazy var text:Text = self.createText() 8 | var textString:String/*Interim value*/ 9 | init(text:String = "defaultText" ,size:CGSize = CGSize(0,0),id:String? = nil){ 10 | textString = text 11 | super.init(size: size, id: id) 12 | } 13 | override func resolveSkin() { 14 | // Swift.print("TextButton.resolveSkin before") 15 | super.resolveSkin() 16 | // Swift.print("TextButton.resolveSkin before text") 17 | _ = text 18 | // Swift.print("TextButton.resolveSkin after") 19 | } 20 | override func mouseDown(_ event:MouseEvent) { 21 | super.mouseDown(event) 22 | Swift.print(ElementParser.stackString(text)) 23 | } 24 | override var skinState:String { 25 | get {return super.skinState} 26 | set { 27 | super.skinState = newValue 28 | text.skinState = (newValue)/*Why is this set directly to the skin and not to the element?, Text doesnt have a setSkin method so i guess thats why?, well it does actually, through it super class Element, so fix this*/ 29 | } 30 | } 31 | override func setSize(_ width:CGFloat, _ height:CGFloat) { 32 | super.setSize(width, height) 33 | text.setSize(width, height) 34 | } 35 | /** 36 | * Returns the text in the _text.textField.text 37 | */ 38 | func getText()->String{ 39 | return text.getText() 40 | } 41 | /** 42 | * Sets the text in the _text instance 43 | * NOTE: cant be named setText as its blocked by objc 44 | */ 45 | func setTextValue(_ text:String){ 46 | self.text.setText(text) 47 | } 48 | required init(coder:NSCoder) {fatalError("init(coder:) has not been implemented")} 49 | //DEPRECATED 50 | init(_ width:CGFloat, _ height:CGFloat, _ text:String = "defaultText", _ parent:ElementKind?, _ id:String? = nil) { 51 | textString = text 52 | super.init(size:CGSize(width,height),id:id) 53 | } 54 | } 55 | extension TextButton { 56 | /** 57 | * Makes lazy var more organized 58 | */ 59 | func createText()->Text{ 60 | // Swift.print("createText") 61 | // Swift.print("self.skinSize: " + "\(self.skinSize)") 62 | let text = self.addSubView(Text.init(text: self.textString, size: self.skinSize, id: nil))//(text;,size:) 63 | text.isInteractive = false 64 | // Swift.print("before return createText") 65 | return text 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/Element/ui/button/check/CheckButton.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * NOTE: to force the CheckButton to apply its Checked or unchecked skin, use the setChecked after the instance is created 5 | * NOTE: isChecked is not priv because setting it manually and then setting style is cheaper than using setSkinState. TreeList uses this scheme 6 | */ 7 | class CheckButton:Button,Checkable{ 8 | var isChecked:Bool 9 | init(isChecked:Bool = false,size:CGSize = CGSize(0,0),id:String? = nil){ 10 | self.isChecked = isChecked 11 | super.init(size: size, id: id) 12 | } 13 | override func mouseUpInside(_ event:MouseEvent) { 14 | isChecked = !isChecked 15 | super.mouseUpInside(event) 16 | super.onEvent(CheckEvent(CheckEvent.check, isChecked, self)) 17 | } 18 | /** 19 | * Sets the self.isChecked variable (Toggles between two states) 20 | */ 21 | func setChecked(_ isChecked:Bool) { 22 | self.isChecked = isChecked 23 | skinState = {self.skinState}() 24 | } 25 | func getChecked() -> Bool { 26 | return isChecked 27 | } 28 | override var skinState:String { 29 | get {return isChecked ? SkinStates.checked + " " + super.skinState : super.skinState} 30 | set {super.skinState = newValue} 31 | } 32 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 33 | //dep 34 | init(_ width:CGFloat, _ height:CGFloat, _ isChecked:Bool = false, _ parent:ElementKind? = nil, _ id:String? = nil){ 35 | self.isChecked = isChecked 36 | super.init(size:CGSize(width,height),id:id) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Element/ui/button/check/CheckButton2.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | /** 5 | * NOTE: to force the CheckButton to apply its Checked or unchecked skin, use the setChecked after the instance is created 6 | * NOTE: isChecked is not priv because setting it manually and then setting style is cheaper than using setSkinState. TreeList uses this scheme 7 | */ 8 | class CheckButton2:Button { 9 | var checkedState:CheckedState = .none{ 10 | didSet{/*Sets the self.isChecked variable (Toggles between two states)*/ 11 | skinState = {self.skinState}()//refresh skinState 12 | } 13 | } 14 | init(state:CheckedState, size: CGSize, id: String? = nil) { 15 | self.checkedState = state 16 | super.init(size: size, id: id) 17 | } 18 | override func mouseUpInside(_ event:MouseEvent) { 19 | checkedState = checkedState == .none ? .checked : .none 20 | super.mouseUpInside(event) 21 | super.onEvent(CheckEvent2(state:checkedState, origin:self)) 22 | } 23 | override var skinState:String { 24 | get {return checkedState == .none ? super.skinState : checkedState.rawValue + " " + super.skinState } 25 | set {super.skinState = newValue} 26 | } 27 | override func getClassType() -> String { 28 | return "\(CheckButton.self)" 29 | } 30 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 31 | } 32 | 33 | protocol Checkable2{ 34 | var checkedState:CheckedState {get set}//rename to checkedState or checkState 35 | } 36 | /** 37 | * Mix state can only be set from outside 38 | */ 39 | enum CheckedState:String{ 40 | case checked = "checked", mix = "mix", none = "none" 41 | } 42 | -------------------------------------------------------------------------------- /src/Element/ui/button/check/Checkable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * IMPORTANT: The reason why getChecked and setChecked is used rather than variables is because we want to support class composition, methods overriding methods in sub classes. THis is not possible with variables. getChecked and setChecked also implies implicit gettters and setters which a word like "checked" does not 4 | * CAUTION: ⚠️️ you probably have to revert to the checked variable as you need to be able to use only one way to assert checkedness. think checkgroup etc 5 | */ 6 | //typealias Checkable = Checkable//Legacy support 7 | //TODO: ⚠️️ do selected and _selected 8 | protocol Checkable:class/*<--derive only classes for the protocol, not structs, this enables === operator of protocol*/{ 9 | func setChecked(_ isChecked:Bool) 10 | func getChecked()->Bool/*<--shouldn't this be isChecked? :TODO: this should be getChecked since composite classes can impliment ICHeckable and they will need to access a sub instance via a implimcit getter method, same for IDisableable, ISelectable, IFocusable etc*/ 11 | } 12 | -------------------------------------------------------------------------------- /src/Element/ui/button/check/event/CheckEvent.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Include isCheckable? what use would it have?, you could add that via an extension 5 | */ 6 | class CheckEvent:Event{ 7 | var isChecked:Bool 8 | static var check:String = CheckEventType.check.rawValue 9 | init(_ type:String, _ isChecked:Bool, _ origin:NSView){ 10 | self.isChecked = isChecked 11 | super.init(type, origin) 12 | } 13 | } 14 | enum CheckEventType:String{ 15 | case check = "checkEventCheck" 16 | } 17 | extension Event{ 18 | func assert(_ type:CheckEventType) -> Bool{ 19 | return self.type == type.rawValue 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/Element/ui/button/check/event/CheckEvent2.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Include isCheckable? what use would it have?, you could add that via an extension 5 | */ 6 | class CheckEvent2:Event{ 7 | 8 | let state:CheckedState 9 | init(state:CheckedState, origin:NSView){ 10 | self.state = state 11 | super.init(EventType.check2.rawValue, origin) 12 | } 13 | } 14 | extension CheckEvent2{ 15 | enum EventType:String{//We use enum so we can use dot syntax 16 | case check2 = "checkEvent2Check" 17 | } 18 | var checked:Bool {return state == .checked}//simple bool getter 19 | } 20 | extension Event{ 21 | func assert(_ type:CheckEvent2.EventType) -> Bool{ 22 | return self.type == type.rawValue 23 | } 24 | } 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Element/ui/button/checkboxbutton/CheckBoxButton.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Implement a way to also include the text in being in a checked status 5 | */ 6 | class CheckBoxButton:Button,Checkable,LableKind{//TODO: ⚠️️⚠️️ extend TextButton likeRadioButton does it. it means less code 7 | private var isChecked:Bool//TODO: ⚠️️ This should be initChecked, and then we should use only checkBox as a state holder 8 | var textString:String 9 | lazy var checkBox:CheckBox = createCheckBox() 10 | lazy var text:Text = createText() 11 | init(text:String = "defaultText",isChecked:Bool = false,size:CGSize = CGSize(0,0),id:String? = nil){ 12 | self.textString = text 13 | self.isChecked = isChecked 14 | super.init(size: size, id: id) 15 | } 16 | override func resolveSkin() { 17 | super.resolveSkin() 18 | _ = checkBox/*Init the UI*/ 19 | _ = text/*Init the UI*/ 20 | } 21 | func setChecked(_ isChecked:Bool) { 22 | checkBox.setChecked(isChecked) 23 | } 24 | func getChecked() -> Bool { 25 | return checkBox.getChecked() 26 | } 27 | override var skinState:String { 28 | get {return isChecked ? SkinStates.checked + " " + super.skinState : super.skinState} 29 | set { 30 | super.skinState = newValue 31 | checkBox.skinState = {checkBox.skinState}()//New, more like refresh, worked 🎉 32 | text.skinState = {text.skinState}()//New, same as above /*Why is this set directly to the skin and not to the element?, Text doesnt have a setSkin method so i guess thats why?, well it does actually, through it super class Element, so fix this*/ 33 | } 34 | } 35 | func setSize(width:CGFloat, height:CGFloat) { 36 | super.setSize(width, height) 37 | checkBox.skinState = {checkBox.skinState}() 38 | text.skinState = {text.skinState}() 39 | } 40 | required init(coder:NSCoder) {fatalError("init(coder:) has not been implemented")} 41 | //DEP 42 | init(_ width:CGFloat, _ height:CGFloat, _ text:String = "defaultText", _ isChecked:Bool = false, _ parent:ElementKind? = nil, _ id:String? = nil) { 43 | self.textString = text 44 | self.isChecked = isChecked 45 | super.init(size:CGSize(width,height),id:id) 46 | } 47 | } 48 | class CheckBox:CheckButton{}/*CheckBox purly exists to differentiate between types in CSS*/ 49 | extension CheckBoxButton{ 50 | func getText()->String{return text.getText()} 51 | func setTextValue(_ text:String){self.text.setText(text)} 52 | func createCheckBox()->CheckBox{ 53 | return self.addSubView(CheckBox(13,13,self.isChecked,self)) 54 | } 55 | func createText() -> Text { 56 | let text = self.addSubView(Text(self.getWidth(),self.getHeight(),self.textString,self)) 57 | text.isInteractive = false 58 | return text 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Element/ui/button/checkboxbutton/CheckBoxButton2.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Impliment IDisableable also and extend DisableTextButton 5 | * make the old init method to support legacy stuff. 6 | */ 7 | class CheckBoxButton2:TextButton,Checkable2{ 8 | lazy var checkBox:CheckBox2 = createCheckBox() 9 | private let initCheckedState:CheckedState 10 | var checkedState:CheckedState {/*we only checkBox as a state holder*/ 11 | get{return checkBox.checkedState} 12 | set{/*Sets the self.isChecked variable (Toggles between two states)*/ 13 | checkBox.checkedState = newValue 14 | } 15 | } 16 | init(text:String = "defaultText", checkedState:CheckedState = .none,size:CGSize = CGSize(0,0),id:String? = nil){ 17 | self.initCheckedState = checkedState 18 | super.init(text:text, size: size, id: id) 19 | //text.isInteractive = false 20 | } 21 | /** 22 | * NOTE: When added to stage and if RadioBullet dispatches selct event it will bubble up and through this class (so no need for extra eventlistners and dispatchers in this class) 23 | * NOTE: The radioBullet has an id of "radioBullet" (this may be usefull if you extend CheckBoxButton and that subclass has children that are of type Button and you want to identify this button and noth the others) 24 | */ 25 | override func resolveSkin() { 26 | super.resolveSkin() 27 | _ = checkBox/*Init the UI*/ 28 | } 29 | func setSize(width:CGFloat, height:CGFloat) { 30 | super.setSize(width, height) 31 | checkBox.skinState = {checkBox.skinState}() 32 | text.skinState = {text.skinState}() 33 | } 34 | override func getClassType() -> String { 35 | return "\(CheckBoxButton.self)" 36 | } 37 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 38 | } 39 | 40 | class CheckBox2:CheckButton2{ 41 | override func getClassType() -> String { 42 | return "\(CheckBox.self)" 43 | } 44 | }/*CheckBox purly exists to differentiate between types in CSS*/ 45 | extension CheckBoxButton2{ 46 | // func getText()->String{return text.getText()} 47 | // func setTextValue(_ text:String){self.text.setText(text)} 48 | func createCheckBox()->CheckBox2{ 49 | return self.addSubView(CheckBox2.init(state: self.initCheckedState, size: CGSize(13,13))) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Element/ui/button/checkboxbutton/SelectCheckBoxButton.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * EXAMPLE: see TestCheckBoxButton 5 | * NOTE: Remember to use the setChecked(true) if you want to change the state and skin after initiating the instance, since it wont do this itself on initiate 6 | */ 7 | class SelectCheckBoxButton:CheckBoxButton,Selectable { 8 | private var isSelected:Bool 9 | init(text:String = "defaultText",isSelected:Bool = false,isChecked:Bool = false,size:CGSize = CGSize(NaN,NaN),id:String? = nil){ 10 | self.isSelected = isSelected 11 | super.init(text:text, isChecked:isChecked, size: size, id: id) 12 | } 13 | override func mouseDown(_ event:MouseEvent) { 14 | self.isSelected = !self.isSelected 15 | super.mouseDown(event) 16 | super.onEvent(SelectEvent(SelectEvent.select,self)) 17 | } 18 | /** 19 | * NOTE: Do not add a dispatch event here, that is the responsibily of the caller 20 | */ 21 | func setSelected(_ isSelected:Bool) { 22 | self.isSelected = isSelected 23 | skinState = {skinState}() 24 | } 25 | func getSelected()->Bool{return isSelected} 26 | override var skinState:String { 27 | get {return isSelected ? SkinStates.selected + " " + super.skinState : super.skinState} 28 | set {super.skinState = newValue} 29 | } 30 | required init(coder:NSCoder) {fatalError("init(coder:) has not been implemented")} 31 | //dep 32 | init(_ width:CGFloat, _ height:CGFloat, _ text:String = "defaultText", _ isSelected : Bool = false, _ isChecked:Bool = false, _ parent:ElementKind? = nil, _ id:String = ""){ 33 | self.isSelected = isSelected 34 | super.init(width, height, text, isChecked, parent, id) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Element/ui/button/events/ButtonEvent.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Add for touch as well: touchUp,Down,upOutSide,upInside,out,over etc 5 | * NOTE: Reading the event.location can be done by reading the localPos from the button it self 6 | */ 7 | class ButtonEvent:Event{ 8 | static let down:String = ButtonEventType.down.rawValue 9 | static let up:String = ButtonEventType.up.rawValue 10 | static let upInside:String = ButtonEventType.upInside.rawValue 11 | static let upOutside:String = ButtonEventType.upOutside.rawValue 12 | static let out:String = ButtonEventType.out.rawValue 13 | static let over:String = ButtonEventType.over.rawValue 14 | /*Right*/ 15 | static var rightMouseDown:String = ButtonEventType.rightMouseDown.rawValue 16 | weak var event:NSEvent? 17 | init(_ type:String = "", _ origin:AnyObject,_ event:NSEvent? = nil){ 18 | self.event = event 19 | super.init(type, origin) 20 | } 21 | } 22 | enum ButtonEventType:String{ 23 | case down = "buttonEventDown" 24 | case up = "buttonEventUp" 25 | case upInside = "buttonEventUpInside" 26 | case upOutside = "buttonEventUpOutside" 27 | case out = "buttonEventOut" 28 | case over = "buttonEventOver" 29 | case rightMouseDown = "buttonEventRightMouseDown" 30 | } 31 | extension Event{ 32 | /** 33 | * NOTE: this works even if the Event isnt cast as ButtonEvent! Its infered via the enum 34 | */ 35 | func assert(_ type:ButtonEventType, id:String? = nil) -> Bool{ 36 | return self.type == type.rawValue && (self.origin as? ElementKind)?.id == id 37 | } 38 | func assert(_ type:ButtonEventType, _ origin:AnyObject?) -> Bool{ 39 | return self.type == type.rawValue && self.origin === origin 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Element/ui/button/events/SelectEvent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * "Unselect" vs "Deselect": Unselected is used to qualify something that has not been selected, not something that was selected and isn't anymore. 5 | * NOTE: To get the selected state: ((sender as! NSNotification).object as ISelectable).isSelected 6 | */ 7 | class SelectEvent:Event { 8 | static var select:String = "select" 9 | //static var deSelect:String = "deSelect"//unSelect is also an option? 10 | } 11 | extension SelectEvent{ 12 | var isSelected:Bool{return (origin as! Selectable).getSelected()} 13 | } 14 | -------------------------------------------------------------------------------- /src/Element/ui/button/radio/RadioButton.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Impliment IDisableable also and extend DisableTextButton 5 | * TODO: 6 | */ 7 | class RadioButton:TextButton,Selectable{ 8 | lazy var radioBullet:RadioBullet = self.createRadioBullet() 9 | fileprivate var isSelected:Bool 10 | init(text:String = "defaultText",isSelected:Bool = false,size:CGSize = CGSize(0,0),id:String? = nil){ 11 | self.isSelected = isSelected 12 | super.init(text:text, size: size, id: id) 13 | } 14 | /** 15 | * NOTE: When added to stage and if RadioBullet dispatches selct event it will bubble up and through this class (so no need for extra eventlistners and dispatchers in this class) 16 | * NOTE: The radioBullet has an id of "radioBullet" (this may be usefull if you extend CheckBoxButton and that subclass has children that are of type Button and you want to identify this button and noth the others) 17 | */ 18 | override func resolveSkin() { 19 | super.resolveSkin() 20 | _ = radioBullet/*Init the UI*/ 21 | } 22 | func setSelected(_ isSelected:Bool) { 23 | radioBullet.setSelected(isSelected) 24 | } 25 | /** 26 | * NOTE: This method represents something that should be handled by a method named getSelected, but since this class impliments ISelectable it has to implment selected and selectable 27 | */ 28 | func getSelected()->Bool { 29 | return radioBullet.getSelected() 30 | } 31 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 32 | //DEPRECATED 33 | init(_ width:CGFloat, _ height:CGFloat, _ text:String = "defaultText", _ isSelected:Bool = false, _ parent:ElementKind? = nil, _ id:String? = nil) { 34 | self.isSelected = isSelected 35 | super.init(width,height,text,parent,id) 36 | } 37 | } 38 | class RadioBullet:SelectButton{}/*RadioBullet is targeted in CSS, but is essentially the same as a SelectButton*/ 39 | extension RadioButton{ 40 | /** 41 | * Makes lazy var more organized 42 | */ 43 | func createRadioBullet()->RadioBullet{ 44 | return self.addSubView(RadioBullet.init(isSelected: self.isSelected)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Element/ui/button/select/SelectButton.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | //TODO: ⚠️️ do selected and _selected 4 | class SelectButton:Button,Selectable { 5 | private var isSelected:Bool 6 | init(isSelected : Bool = false ,size:CGSize = CGSize(0,0),id:String? = nil){ 7 | self.isSelected = isSelected 8 | super.init(size: size, id: id) 9 | } 10 | /** 11 | * Select state should only take place when there is a mouseUpInside event 12 | */ 13 | override func mouseUpInside(_ event:MouseEvent) { 14 | isSelected = true 15 | super.mouseUpInside(event)/*Forward the event*/ 16 | super.onEvent(SelectEvent(SelectEvent.select,self)) 17 | } 18 | /** 19 | * NOTE: do not add a dispatch event here, that is the responsibilyy of the caller 20 | */ 21 | func setSelected(_ isSelected:Bool){ 22 | self.isSelected = isSelected 23 | self.skinState = {self.skinState}() 24 | } 25 | func getSelected()->Bool{return isSelected} 26 | override var skinState:String { 27 | get {return self.isSelected ? SkinStates.selected + " " + super.skinState : super.skinState} 28 | set {super.skinState = newValue} 29 | } 30 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 31 | //dep 32 | init(_ width: CGFloat, _ height: CGFloat, _ isSelected : Bool = false, _ parent: ElementKind? = nil, _ id: String? = nil) { 33 | self.isSelected = isSelected 34 | super.init(size:CGSize(width,height),id:id) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Element/ui/button/select/Selectable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * We could do selected instead of getSelected, but var getters confuse people. functions are easier to grasp. Its also easier to override functions conceptually than variables. Functions can also do method overloading. 4 | * NOTE: Using isSelceted could be the wrong way to go about this -> getSelected would work better->*//*var isSelected:Bool{get}*//*This is named isSelected because selected is ocupied by obc and using selected() as a method seems inconsistent 5 | */ 6 | //TODO: ⚠️️ do selected and _selected, call it Selectable2 and replace all Selectables 7 | protocol Selectable:class/*Derive only classes for the protocol, not structs, this enables === operator of protocol and also enables inout support for protocols*/{ 8 | func setSelected(_ isSelected:Bool) 9 | func getSelected()->Bool/*<--Shouldn't this be isSelected? :TODO: this should be getSelected since composite classes can impliment ICHeckable and they will need to access a sub instance via a implimcit getter method, same for IDisableable, ISelectable, IFocusable etc*/ 10 | } 11 | extension Selectable{/*<-- New, simplifes access, while maininting easy overriding abilities with the functions in the Iselectable protocol*/ 12 | var selected:Bool {get{return getSelected()}set{setSelected(newValue)}} 13 | } 14 | -------------------------------------------------------------------------------- /src/Element/ui/button/text/CheckTextButton.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class CheckTextButton:TextButton,Checkable { 5 | var isChecked:Bool 6 | init(_ width : CGFloat, _ height : CGFloat, _ text : String = "defaultText", _ isChecked : Bool = false, _ parent : ElementKind? = nil, _ id : String? = nil){ 7 | self.isChecked = isChecked; 8 | super.init(width, height, text, parent, id) 9 | } 10 | override func mouseUpInside(_ event:MouseEvent) { 11 | isChecked = !isChecked 12 | super.mouseUpInside(event) 13 | self.event(CheckEvent(CheckEvent.check,isChecked,self))//TODO:Remove the bool from the event. Similar to SelectEvent 14 | } 15 | /** 16 | * Sets the isChecked variable (Toggles between two states) 17 | * NOTE: do not add a dispatch event here, that is the responsibilyy of the caller 18 | */ 19 | func setChecked(_ isChecked:Bool) { 20 | self.isChecked = isChecked 21 | skinState = {self.skinState}() 22 | } 23 | func getChecked()->Bool{return isChecked} 24 | override var skinState:String { 25 | get {return isChecked ? SkinStates.checked + " " + super.skinState : super.skinState} 26 | set {super.skinState = newValue} 27 | } 28 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 29 | } 30 | -------------------------------------------------------------------------------- /src/Element/ui/button/text/SelectTextButton.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * NOTE: Maybe the methods relating to ISelectable could be moved to an extension (Maybe not, since you need access to super, test this idea in playground) 5 | */ 6 | class SelectTextButton:TextButton,Selectable { 7 | var isSelected:Bool 8 | init(_ width:CGFloat, _ height:CGFloat, _ text:String = "defaultText", _ isSelected:Bool = false, _ parent:ElementKind? = nil, _ id:String? = nil){ 9 | self.isSelected = isSelected 10 | super.init(width, height, text, parent, id) 11 | } 12 | override func mouseUpInside(_ event: MouseEvent) { 13 | isSelected = true 14 | super.mouseUpInside(event) 15 | self.event(SelectEvent(SelectEvent.select,self)) 16 | } 17 | /** 18 | * NOTE: Do not add a dispatch event here, that is the responsibility of the caller 19 | */ 20 | func setSelected(_ isSelected:Bool){ 21 | self.isSelected = isSelected 22 | skinState = {self.skinState}() 23 | } 24 | func getSelected()->Bool{return isSelected} 25 | override var skinState:String { 26 | get {return isSelected ? SkinStates.selected + " " + super.skinState : super.skinState} 27 | set {super.skinState = newValue} 28 | } 29 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 30 | } 31 | -------------------------------------------------------------------------------- /src/Element/ui/combobox/event/ComboBoxEvent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * NOTE: You can derive the slected via the combobox item it self. So no need to pass the selected along in the event 5 | */ 6 | class ComboBoxEvent:Event{ 7 | static var click:String = "comboBoxEventClick" 8 | static var headerClick:String = "comboBoxEventHeaderClick" 9 | static var listSelect:String = "comboBoxEventListSelect" 10 | /*private*/ var index:Int//🔶 swift 3 update 11 | init(_ type: String, _ index:Int, _ origin: AnyObject) { 12 | self.index = index 13 | super.init(type, origin) 14 | } 15 | } 16 | extension ComboBoxEvent{ 17 | /** 18 | * Convenience 19 | * NOTE: Keeps the event light-weight by not referencing the item directly 20 | * NOTE: you may have to reconsider this as the selected item may have de-selected before the event arrives (think cpu threads etc) 21 | */ 22 | var selected:Selectable{ 23 | //continue here: index from where? See similar event class in list 24 | //you could even just grab the index from the list it self 25 | fatalError("not supported yet") 26 | //return (origin as! ComboBox).list!.lableContainer!.subviews[index] as! ISelectable 27 | } 28 | var selectedProperty:String{ 29 | let list: Listable3 = ((origin as! ComboBox).popupWindow!.contentView as! ComboBoxView).list! 30 | let property:String = List3Parser.propertyAt(list , index) 31 | return property 32 | } 33 | var selectedTitle:String{ 34 | let list: Listable3 = ((origin as! ComboBox).popupWindow!.contentView as! ComboBoxView).list! 35 | let title:String = List3Parser.titleAt(list, index) 36 | return title 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Element/ui/combobox/utils/ComboBoxModifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | class ComboBoxModifier{ 4 | static func selectByProperty(_ comboBox:ComboBox,_ property:String) { 5 | let index:Int = comboBox.dataProvider!.index("property", property)! 6 | let title:String = comboBox.dataProvider!.getItemAt(index)!["title"]! 7 | comboBox.selectedIndex = index 8 | comboBox.headerButton.setTextValue(title) 9 | 10 | } 11 | /** 12 | * TODO: ⚠️️ Implement asserting that the title exists 13 | * IMPORTANT: be sure that the title exists in the dataprovider 14 | */ 15 | static func select(_ comboBox:ComboBox,_ title:String) { 16 | let index:Int = comboBox.dataProvider!.getIndex(title)! 17 | comboBox.selectedIndex = index 18 | comboBox.headerButton.setTextValue(title) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/ui/combobox/utils/ComboBoxParser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ComboBoxParser { 5 | /** 6 | * Returns the property at index 7 | */ 8 | static func selectedProperty(_ comboBox:ComboBox)->String{ 9 | return comboBox.dataProvider!.getItemAt(comboBox.selectedIndex)!["property"]! 10 | } 11 | /** 12 | * Returns the title at index 13 | */ 14 | static func selectedTitle(_ comboBox:ComboBox)->String{ 15 | return comboBox.dataProvider!.getItemAt(comboBox.selectedIndex)!["title"]! 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Element/ui/combobox/utils/ComboBoxWin.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ComboBoxWin:Window{ 5 | var dataProvider:DataProvider 6 | let initSelectedIndex:Int 7 | var itemHeight:CGFloat 8 | init(_ width: CGFloat, _ height: CGFloat, _ dataProvider:DataProvider, _ initSelectedIndex:Int, _ itemHeight:CGFloat) { 9 | self.initSelectedIndex = initSelectedIndex 10 | self.itemHeight = itemHeight 11 | self.dataProvider = dataProvider 12 | super.init(width,height) 13 | self.animationBehavior = NSWindow.AnimationBehavior.utilityWindow/*Fades the window in and out slightly*/ 14 | } 15 | override func resolveSkin() { 16 | super.resolveSkin() 17 | self.contentView = ComboBoxView(frame.size.width,frame.size.height, dataProvider, initSelectedIndex,itemHeight,nil,"comboBox")/*Sets the mainview of the window*/ 18 | } 19 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 20 | required init(_ width: CGFloat, _ height: CGFloat) {fatalError("init has not been implemented")} 21 | } 22 | class ComboBoxView:PopupView{ 23 | var dataProvider:DataProvider 24 | var list:List3? 25 | var initSelectedIndex:Int 26 | var itemHeight:CGFloat//TODO:this should be set in the css? 27 | init(_ width:CGFloat, _ height:CGFloat, _ dataProvider:DataProvider,_ initSelectedIndex:Int, _ itemHeight:CGFloat, _ parent: ElementKind? = nil, _ id: String? = nil) { 28 | self.dataProvider = dataProvider 29 | self.initSelectedIndex = initSelectedIndex 30 | self.itemHeight = itemHeight 31 | super.init(size:CGSize(width,height),id:id) 32 | } 33 | override func resolveSkin() { 34 | Swift.print("ComboBoxView.resolveSkin()") 35 | Swift.print("width: " + "\(skinSize.w)") 36 | Swift.print("height: " + "\(skinSize.h)") 37 | super.resolveSkin() 38 | list = addSubView(List3(skinSize.w, skinSize.h, CGSize(itemHeight,itemHeight), dataProvider,.ver, self)) 39 | List3Modifier.selectAt(list!, initSelectedIndex) 40 | } 41 | /** 42 | * override func onEvent(event: Event) {} 43 | */ 44 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 45 | } 46 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/abstract/Listable3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | protocol Listable3:Containable3 {//extending Containable3 is new 5 | var dp:DataProvider {get} 6 | var dir:Dir {get}//the "primary" direction the list scroll in. the secondary direction is the opposite 7 | var itemSize:CGSize{get} 8 | } 9 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/abstract/Listable3Extensions.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: Continue adding methods here 5 | */ 6 | extension Listable3 { 7 | /*Parsers*/ 8 | var selected:Selectable?{fatalError("not implemented yet")}/*Convenience*/ 9 | var selectedIndex:Int{return 0}/*Convenience*/ 10 | //var itemsHeight:CGFloat {fatalError("not implemented yet")}/*Convenience*/ 11 | /*Modifiers*/ 12 | func selectAt(_ index:Int){/*convenience*/ 13 | print("IList.selectAt(\(index)) not implemented yet") 14 | } 15 | var dp:DataProvider {return self.dp}/*convenience*/ 16 | //var itemsHeight: CGFloat {return dp.count * itemHeight}//👈 new 17 | 18 | /*new/temp TODO: maybe use where Self:IList && Self:Scrollable*/ 19 | var interval:CGFloat{return floor(contentSize[dir] - maskSize[dir])/itemSize[dir]} 20 | var progress:CGFloat{return SliderParser.progress(contentContainer.frame.origin[dir], maskSize[dir], contentSize[dir])} 21 | } 22 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/abstract/utils/List3Modifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | 5 | class List3Modifier { 6 | /** 7 | * Selects the first item that has PARAM title as its title 8 | */ 9 | static func select(_ list:Listable3, _ title:String) { 10 | let index:Int = list.dp.getItemIndex(list.dp.getItem(title)!) 11 | selectAt(list,index) 12 | } 13 | /** 14 | * Selects an item in the itemContainer 15 | */ 16 | static func selectAt(_ list:Listable3, _ index:Int) { 17 | let selectable:Selectable = list.contentContainer.subviews[index] as! Selectable 18 | if(!selectable.getSelected()) {selectable.setSelected(true)} 19 | SelectModifier.unSelectAllExcept(selectable, list.contentContainer) 20 | } 21 | } 22 | 23 | class List5Modifier { 24 | /** 25 | * Selects the first item that has PARAM title as its title 26 | */ 27 | static func select(_ list:Listable5, _ title:String) { 28 | let index:Int = list.dp.getItemIndex(list.dp.getItem(title)!) 29 | selectAt(list,index) 30 | } 31 | /** 32 | * Selects an item in the itemContainer 33 | */ 34 | static func selectAt(_ list:Listable5, _ index:Int) { 35 | let selectable:Selectable = list.contentContainer.subviews[index] as! Selectable 36 | if(!selectable.getSelected()) {selectable.setSelected(true)} 37 | SelectModifier.unSelectAllExcept(selectable, list.contentContainer) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/abstract/utils/List3Parser.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import Utils 3 | 4 | class List3Parser { 5 | /** 6 | * Returns the index of a "label" 7 | * PARAM: view is the Label 8 | */ 9 | static func index(_ list: Listable3, _ view:NSView)->Int { 10 | fatalError("out of order atm") 11 | //return list.contentContainer.indexOf(view) 12 | } 13 | /** 14 | * Returns the property for index 15 | */ 16 | static func propertyAt(_ list:Listable3, _ index:Int)->String{ 17 | fatalError("out of order atm") 18 | //return list.dp.getItemAt(index)!["property"]! 19 | } 20 | /** 21 | * Returns the title for index 22 | */ 23 | static func titleAt(_ list: Listable3, _ index:Int)->String{ 24 | fatalError("out of order atm") 25 | //return list.dataProvider.getItemAt(index)!["title"]! 26 | } 27 | /** 28 | * Returns the index of the currentSelected 29 | */ 30 | static func selectedIndex(_ list:Listable3) -> Int { 31 | fatalError("out of order atm") 32 | /* let selected:ISelectable? = self.selected(list) 33 | return selected != nil ? list.lableContainer!.indexOf(selected as! NSView) : -1*/ 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/ElasticScrollList3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class ElasticScrollList3:List3,ElasticScrollable3 { 4 | lazy var moverGroup:MoverGroup? = MoverGroup(self.setProgress,self.maskSize,self.contentSize) 5 | } 6 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/ElasticSlideScrollList3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ElasticSlideScrollList3:SlideList3,ElasticSlidableScrollable3 { 5 | lazy var moverGroup:MoverGroup? = self.moverGrp 6 | override func onEvent(_ event:Event) { 7 | if(event.type == AnimEvent.stopped){ 8 | Swift.print("ElasticSlideScrollList3.onEvent: " + "\(event.type)") 9 | let dir:Dir = event.origin === moverGroup!.yMover ? .ver : .hor 10 | Swift.print("bounce back anim stopp dir: \(dir)") 11 | hideSlider(dir)/*hides the slider when bounce back anim stopps*/ 12 | } 13 | super.onEvent(event) 14 | } 15 | } 16 | private extension ElasticSlideScrollList3{ 17 | var moverGrp:MoverGroup {//⚠️️ TODO: rename to createMoverGroup 18 | var group = MoverGroup(self.setProgress,self.maskSize,self.contentSize); 19 | group.event = self.onEvent/*Add an eventHandler for the mover object, , this has no functionality in this class, but may have in classes that extends this class, like hide progress-indicator when all animation has stopped*/ 20 | return group 21 | } 22 | } 23 | 24 | /* 25 | override open func scrollWheel(with event: NSEvent) { 26 | Swift.print("SlideView3.scrollWheel() \(event.type)") 27 | super.scrollWheel(with: event) 28 | if(event.phase == NSEventPhase.mayBegin || event.phase == NSEventPhase.began){ 29 | showSlider() 30 | } 31 | /*else if(event.phase == NSEventPhase.ended || event.phase == NSEventPhase.cancelled){ 32 | //hideSlider() 33 | }*/ 34 | } 35 | */ 36 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/List3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | class List4:Element{ 4 | override func getClassType() -> String { 5 | return "List" 6 | } 7 | } 8 | class List3:ContainerView3,Listable3{ 9 | var dp:DataProvider 10 | var dir:Dir 11 | var itemSize:CGSize 12 | override var contentSize:CGSize { get{return dir == .hor ? CGSize(dp.count * itemSize.width ,skinSize.h) : CGSize(skinSize.w ,dp.count * itemSize.height) } set{_ = newValue}} 13 | 14 | init(_ width: CGFloat, _ height: CGFloat, _ itemSize:CGSize = CGSize(NaN,NaN), _ dataProvider:DataProvider? = nil, _ dir:Dir = .ver, _ parent: ElementKind? = nil, _ id: String? = "") { 15 | self.itemSize = itemSize 16 | self.dp = dataProvider ?? DataProvider()/*<--if it's nil then a DB is created*/ 17 | self.dir = dir 18 | super.init(size:CGSize(width,height), id:id) 19 | self.dp.event = onEvent/*Add event handler for the dataProvider*/ 20 | layer!.masksToBounds = true/*masks the children to the frame, I don't think this works, seem to work now*/ 21 | } 22 | /** 23 | * Creates the components in the List Component 24 | */ 25 | override func resolveSkin() { 26 | super.resolveSkin() 27 | Swift.print("before merge") 28 | // Swift.print("dp.items: " + "\(dp.items)") 29 | mergeAt(dp.items, 0) 30 | Swift.print("after merge") 31 | } 32 | /** 33 | * Creates and adds items to the _lableContainer 34 | * TODO: possibly move into ListModifier, TreeList has its mergeAt in an Utils class see how it does it 35 | */ 36 | func mergeAt(_ dictionaries:[[String:String]], _ index:Int){//TODO: possible rename to something better, placeAt? insertAt? 37 | var i:Int = index 38 | for dict:[String:String] in dictionaries { 39 | _ = createItem(dict,i) 40 | i += 1 41 | } 42 | } 43 | func createItem(_ dict:[String:String], _ i:Int) -> Element{ 44 | let dictItem:String = dict["title"] ?? {fatalError("err")}() 45 | Swift.print("dictItem: " + "\(dictItem)") 46 | Swift.print("itemSize: " + "\(itemSize)") 47 | let item:SelectTextButton = SelectTextButton(itemSize.width, itemSize.height ,dictItem, false, contentContainer) 48 | Swift.print("item: " + "\(item)") 49 | Swift.print("contentContainer.numSubViews: " + "\(contentContainer.numSubViews)") 50 | Swift.print("contentContainer.subviews: " + "\(contentContainer.subviews)") 51 | contentContainer.addSubviewAt(item, i+1)/*the first index is reserved for the List skin, what?*/ 52 | 53 | Swift.print("after add") 54 | return item 55 | } 56 | override func getClassType() -> String { 57 | return dir == .ver ? "List" : "VList"//<--this is actually wrong, use HList instead. and correct the css name 58 | } 59 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/ScrollFastList3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ScrollFastList3:FastList3,ScrollableFastListable3{ 5 | static var startTime:NSDate? 6 | static var numOfEvents:Int? 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/ScrollList3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class ScrollList3:List3,Scrollable3 {} 4 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/SlideList3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SlideList3:List3,Slidable3 { 5 | lazy var horSlider:Slider? = self.hSlider 6 | lazy var verSlider:Slider? = self.vSlider 7 | override func onEvent(_ event:Event) { 8 | if(event == SliderEvent.change){ 9 | let dir:Dir = event.origin === horSlider ? .hor : .ver 10 | setProgress((event as! SliderEvent).progress,dir) 11 | } 12 | super.onEvent(event) 13 | } 14 | } 15 | extension Slidable3{ 16 | var hSlider:Slider { 17 | let horSlider:Slider = (self as! NSView).addSubView(Slider(self.width,self.itemSize.height,.hor,self.itemSize,0,(self as! ElementKind))) 18 | let thumbWidth:CGFloat = SliderParser.thumbSize(width/itemSize.width, horSlider.skinSize.w) 19 | horSlider.setThumbSide(thumbWidth) 20 | horSlider.thumb.fadeOut()//inits fade out anim on init 21 | return horSlider 22 | } 23 | var vSlider:Slider{ 24 | let verSlider:Slider = (self as! NSView).addSubView(Slider(self.itemSize.width,self.height,.ver,self.itemSize,0,(self as! ElementKind))) 25 | let thumbHeight:CGFloat = SliderParser.thumbSize(height/contentSize.height, verSlider.skinSize.h) 26 | verSlider.setThumbSide(thumbHeight) 27 | verSlider.thumb.fadeOut()//inits fade out anim on init 28 | return verSlider 29 | } 30 | } 31 | /*extension SlideList3{ 32 | override open func scrollWheel(with event: NSEvent) { 33 | Swift.print("SlideView3.scrollWheel() \(event.type)") 34 | super.scrollWheel(with: event) 35 | if(event.phase == NSEventPhase.mayBegin || event.phase == NSEventPhase.began){ 36 | showSlider() 37 | } 38 | /*else if(event.phase == NSEventPhase.ended || event.phase == NSEventPhase.cancelled){ 39 | //hideSlider() 40 | }*/ 41 | } 42 | }*/ 43 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/SlideScrollFastList3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SlideScrollFastList3:SlideFastList3,SlideableScrollableFastListable3{} 5 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/SlideScrollList3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class SlideScrollList3:SlideList3,SlidableScrollable3{} 5 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/events/ListEvent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ListEvent:Event{ 5 | static var select:String = "listEventSelect" 6 | var index:Int 7 | init(_ type:String, _ index:Int, _ origin:AnyObject) { 8 | self.index = index 9 | super.init(type, origin) 10 | } 11 | } 12 | extension ListEvent{ 13 | /** 14 | * Convenience 15 | * NOTE: Keeps the event light-weight by not referencing the item directly 16 | * NOTE: You may have to reconsider this as the selected item may have de-selected before the event arrives (think cpu threads etc) 17 | */ 18 | var selected:Selectable{ 19 | return (origin as! Listable3).contentContainer.subviews[index] as! Selectable 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/fast/ElasticScrollFastList3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ElasticScrollFastList3:FastList3,ElasticScrollableFastListable3 { 5 | lazy var moverGroup:MoverGroup? = MoverGroup(self.setProgressVal,self.maskSize,self.contentSize) 6 | lazy var rbContainer:Container? = {return self.rubberBandContainer}()/*needed for the overshot animation*/ 7 | } 8 | extension Elastic3 where Self:FastList3{ 9 | var rubberBandContainer:Container { 10 | /*Swift.print("create rbContainer")*/ 11 | let rbContainer = addSubView(Container(self.skinSize.w,self.skinSize.h,self,"rb"))//⚠️️TODO: move to lazy var later 12 | rbContainer.addSubview(contentContainer)/*Adds content Container inside rbContainer*/ 13 | // fatalError("the bellow must be fixed") 14 | Swift.print("ElasticScrollFastList3. the bellow must be fixed") 15 | //contentContainer.parent = rbContainer/*Set the correct parent*/ 16 | return rbContainer 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/fast/ElasticSlideScrollFastList3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ElasticSlideScrollFastList3:SlideFastList3,ElasticSlidableScrollableFastListable3 { 5 | lazy var moverGroup:MoverGroup? = self.moverGrp 6 | lazy var rbContainer:Container? = self.rubberBandContainer/*Needed for the overshot animation*/ 7 | override func onEvent(_ event:Event) { 8 | if(event.type == AnimEvent.stopped){ 9 | let dir:Dir = event.origin === moverGroup!.yMover ? .ver : .hor 10 | hideSlider(dir)/*Hides the slider when bounce back anim stopps*/ 11 | } 12 | super.onEvent(event) 13 | } 14 | var moverGrp:MoverGroup { 15 | var group = MoverGroup(self.setProgressValue,self.maskSize,self.contentSize) 16 | group.event = self.onEvent/*Add an eventHandler for the mover object, , this has no functionality in this class, but may have in classes that extends this class, like hide progress-indicator when all animation has stopped*/ 17 | return group 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/fast/SlideFastList3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SlideFastList3:FastList3,Slidable3 { 5 | lazy var horSlider:Slider? = self.hSlider 6 | lazy var verSlider:Slider? = self.vSlider 7 | } 8 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/fast/abstract/ElasticSlidableScrollableFastListable3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol ElasticSlidableScrollableFastListable3:Slidable3,ElasticScrollableFastListable3 {} 5 | extension ElasticSlidableScrollableFastListable3{ 6 | func setProgressValue(_ value:CGFloat, _ dir:Dir) {/*Gets calls from MoverGroup*/ 7 | //Swift.print("ElasticSlidableScrollableFastListable3.setProgressValue(val,dir)") 8 | setProgressVal(value, dir)//forward 9 | let sliderProgress = ElasticUtils.progress(value,contentSize[dir],maskSize[dir])//doing some double calculations here 10 | slider(dir).setProgressValue(sliderProgress)//temp fix 11 | } 12 | func scroll(_ event: NSEvent) { 13 | //Swift.print("ElasticSlidableScrollableFastListable3.scroll") 14 | (self as Scrollable3).scroll(event)//forward the event 15 | switch event.phase{ 16 | case NSEvent.Phase.changed://Direct scroll, ⚠️️That you need a hock here is not that great 17 | let sliderProgress:CGPoint = ElasticUtils.progress(moverGroup!.result,contentSize,maskSize) 18 | (self as Slidable3).setProgress(sliderProgress)/*moves the sliders*/ 19 | case NSEvent.Phase.mayBegin, NSEvent.Phase.began:/*same as onScrollWheelEnter()*/ 20 | showSlider() 21 | case NSEvent.Phase.ended://same as onScrollWheelExit() 22 | hideSlider() 23 | default:break; 24 | } 25 | if(event.momentumPhase == NSEvent.Phase.began){//simulates: onScrollWheelMomentumBegan() 26 | showSlider()//cancels out the hide call when onScrollWheelExit is called when you release after pan gesture 27 | } 28 | } 29 | func onScrollWheelCancelled() { 30 | Swift.print("ElasticSlidableScrollable3.onScrollWheelCancelled") 31 | hideSlider() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/fast/abstract/ScrollableFastListable3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol ScrollableFastListable3:FastListable3,Scrollable3{} 5 | 6 | extension ScrollableFastListable3{ 7 | func onScrollWheelChange(_ event:NSEvent) { 8 | //Swift.print("ScrollableFastListable3.onScrollWheelChange()") 9 | let primaryProgress:CGFloat = SliderListUtils.progress(event, dir, interval(dir), progress(dir)) 10 | (self as FastListable3).setProgress(primaryProgress)//TODO: ⚠️️ this is not good. that you have to call setProgress one after the other. 11 | (self as Scrollable3).setProgress(primaryProgress,dir) 12 | ScrollFastList3.numOfEvents! += 1 13 | } 14 | } 15 | //let progressVal:CGPoint = SliderListUtils.progress(event.delta, interval, progress) 16 | 17 | //How can you trottle something to always be bellow 60fps?, 18 | //actually 60fps is not static it could drop bellow that so you need to drop into onFrame 19 | //when onScrollWheelEnter -> start frameTicker 20 | //when onScrollWheelExit -> stop frameTicker 21 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/fast/abstract/SlideableScrollableFastListable3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol SlideableScrollableFastListable3:FastListable3,SlidableScrollable3 {} 5 | 6 | extension SlideableScrollableFastListable3{ 7 | func onScrollWheelChange(_ event:NSEvent) { 8 | let primaryProgress:CGFloat = SliderListUtils.progress(event, dir, interval(dir), progress(dir)) 9 | (self as FastListable3).setProgress(primaryProgress) 10 | (self as Scrollable3).setProgress(primaryProgress,dir) 11 | (self as Slidable3).setProgress(CGPoint(0,primaryProgress))//<-quickfix 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/utils/ElasticUtils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ElasticUtils { 5 | /** 6 | * Used when applying progress to the Slider thumb. When scrolling with elastisity 7 | * NOTE: The problem is that when the left over is small the overshoot gets huge. 8 | */ 9 | static func progress(_ value:CGFloat, _ itemsHeight:CGFloat, _ height:CGFloat)->CGFloat{ 10 | var progress:CGFloat = value / -(itemsHeight - height) 11 | if(progress < 0){ 12 | let overshot = (value/height) 13 | progress = progress.clip(0,1) - overshot 14 | }else if(progress > 1){ 15 | let overshot = ((-value-(itemsHeight-height))/height) 16 | //Swift.print("overshot: " + "\(overshot)")//(value-leftover)/height 17 | progress = progress.clip(0,1) + overshot 18 | } 19 | return progress 20 | } 21 | static func progress(_ point:CGPoint,_ contentSize:CGSize, _ maskSize:CGSize)->CGPoint{ 22 | let x:CGFloat = progress(point.x, contentSize.width, maskSize.width) 23 | let y:CGFloat = progress(point.y, contentSize.height, maskSize.height) 24 | return CGPoint(x,y) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/utils/FastList3Modifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class FastList3Modifier { 5 | /** 6 | * Sets selectedIndex in fastList and makes the appropriate UI changes to the visibleItems 7 | * PARAM: index: dataProvider index 8 | * NOTE: Does not unselect prev selected. Implement this else where? 9 | * IMPORTANT: ⚠️️ Only selects if it's not already selected. or unselects if its selected 10 | */ 11 | static func select(_ list:FastListable3, _ index:Int, _ isSelected:Bool = true){ 12 | list.selectedIdx = index/*set the cur selectedIdx in fastList*/ 13 | if let match = list.pool.first(where:{$0.idx == index}), let selectable = match.item as? Selectable,selectable.getSelected() != isSelected{//was-> for (i,_) in list.visibleItems.enumerate(){ 14 | selectable.setSelected(isSelected) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/utils/FastList3Parser.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class FastList3Parser { 5 | /** 6 | * We extract the index by searching for the origin among the visibleItems, the view doesn't store the index it self, but the visibleItems store absolute indecies 7 | */ 8 | static func idx(_ fastList:FastListable3, _ item:NSView) -> Int?{ 9 | return fastList.pool.first(where:{$0.item === item})?.idx 10 | } 11 | } 12 | 13 | 14 | class FastList5Parser { 15 | /** 16 | * We extract the index by searching for the origin among the visibleItems, the view doesn't store the index it self, but the visibleItems store absolute indecies 17 | */ 18 | static func idx(_ fastList:FastListable5, _ item:NSView) -> Int?{ 19 | return fastList.pool.first(where:{$0.item === item})?.idx 20 | } 21 | /** 22 | * New 23 | */ 24 | static func idx(fastList:FastListable5, dpIdx:Int) -> Int?{ 25 | return ArrayParser.first(fastList.pool, dpIdx, {$0.idx == $1})?.item.idx/*Converts dpIndex to lableContainerIdx*/ 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Element/ui/list/DEPRECATED/concrete/list/utils/SliderListUtils.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SliderListUtils{//⚠️️ This can probably be removed, as the same code is in SliderParser or alike, at least move to SliderParser 5 | /** 6 | * Returns the progress og the sliderList (used when we scroll with the scrollwheel/touchpad) (0-1) 7 | */ 8 | static func progress(_ delta:CGFloat,_ sliderInterval:CGFloat,_ sliderProgress:CGFloat)->CGFloat{ 9 | let scrollAmount:CGFloat = (delta/30)/sliderInterval/*_scrollBar.interval*/ 10 | // Swift.print("🍏scrollAmount: " + "\(scrollAmount)") 11 | let currentScroll:CGFloat = sliderProgress - scrollAmount/*The minus sign makes sure the scroll works like in OSX LION*/ 12 | // Swift.print("🍐 currentScroll: " + "\(currentScroll)") 13 | return currentScroll.clip(0, 1)/*Clamps the num between 0 and 1*/ 14 | } 15 | /** 16 | * new (0-1) 17 | */ 18 | static func progress(_ delta:CGPoint, _ interval:CGPoint, _ sliderProgress:CGPoint)->CGPoint{ 19 | let x:CGFloat = progress(delta.x, interval.x, sliderProgress.x) 20 | let y:CGFloat = progress(delta.y, interval.y, sliderProgress.y) 21 | return CGPoint(x,y) 22 | } 23 | /** 24 | * new (0-1) 25 | */ 26 | static func progress(_ event:NSEvent, _ dir:Dir, _ interval:CGFloat, _ progress:CGFloat)->CGFloat{ 27 | let delta:CGFloat = dir == .ver ? event.deltaY : event.deltaX 28 | let progress:CGFloat = SliderListUtils.progress(delta, interval, progress) 29 | return progress 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Element/ui/list/utils/MoverGroup.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ make this a struct? 5 | * TODO: ⚠️️ ContentSize and maskSize should be method refs, so that they can be updated from parent 👈 6 | */ 7 | struct MoverGroup{//rename to ElasticMoverGroup 8 | var xMover:RubberBand, yMover:RubberBand 9 | typealias FrameTick = (CGFloat,Dir)->Void 10 | 11 | init(_ callBack:@escaping FrameTick, _ maskSize:CGSize,_ contentSize:CGSize){ 12 | self.xMover = RubberBand(AnimProxy.shared,{ val in callBack(val,.hor)}/*👈important*/,(0,maskSize.width),(0,contentSize.width)) 13 | self.yMover = RubberBand(AnimProxy.shared,{ val in callBack(val,.ver)}/*👈important*/,(0,maskSize.height),(0,contentSize.height)) 14 | } 15 | } 16 | extension MoverGroup{ 17 | var event:EventSendable.CallBack {get {fatalError("not supported")}set{xMover.event = newValue;yMover.event = newValue}} 18 | func mover(_ dir:Dir)->RubberBand{/*Convenience*/ 19 | return dir == .hor ? xMover : yMover 20 | } 21 | var hasStopped:Bool{ 22 | get{fatalError("get is not supported")} 23 | set{xMover.hasStopped = newValue;yMover.hasStopped = newValue} 24 | } 25 | var value:CGPoint{ 26 | get{return CGPoint(xMover.value,yMover.value)} 27 | set{xMover.value = newValue.x;yMover.value = newValue.y} 28 | } 29 | var result:CGPoint{ 30 | get{return CGPoint(xMover.result,yMover.result)} 31 | set{xMover.result = newValue.x;yMover.result = newValue.y} 32 | } 33 | var velocity:CGPoint{ 34 | get{fatalError("get is not supported")} 35 | set{xMover.velocity = newValue.x;yMover.velocity = newValue.y;} 36 | } 37 | func start(){ 38 | xMover.start() 39 | yMover.start() 40 | } 41 | func stop(){ 42 | xMover.stop() 43 | yMover.stop() 44 | } 45 | func updatePosition(_ direct:Bool = false){ 46 | xMover.updatePosition(direct) 47 | yMover.updatePosition(direct) 48 | } 49 | } 50 | extension MoverGroup{ 51 | var isDirectlyManipulating:Bool{ 52 | get{fatalError("deprecated")/*return xMover.isDirectlyManipulating || yMover.isDirectlyManipulating*/} 53 | set{xMover.isDirectlyManipulating = newValue;yMover.isDirectlyManipulating = newValue;fatalError("deprecated")} 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Element/ui/list/utils/ScrollableUtils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ScrollableUtils { 5 | /** 6 | * Scrolls the list to a scalar position (value 0-1) 7 | */ 8 | /*static func scrollTo(_ scrollable:Containable, _ progress:CGFloat, _ dir:Dir = .ver){ 9 | let val:CGFloat = ScrollableUtils.scrollTo(progress, scrollable.height, scrollable.itemsHeight) 10 | scrollable.lableContainer?.layer?.position[dir] = val/*we offset the y position of the lableContainer*/ 11 | }*/ 12 | /** 13 | * Returns the y position of a "virtual" list 14 | * PARAM: progress: 0-1 15 | */ 16 | static func scrollTo(_ progress:CGFloat,_ maskHeight:CGFloat,_ itemsHeight:CGFloat)->CGFloat{ 17 | let scrollHeight:CGFloat = itemsHeight - maskHeight/*allItems.height - mask.height*/ 18 | //Swift.print("scrollHeight: " + "\(scrollHeight)") 19 | /* 20 | if(scrollHeight.isNegative && progress.isNegative){ 21 | scrollHeight = scrollHeight * -1//Swift.max(scrollHeight,0) 22 | } 23 | Swift.print("progress: " + "\(progress)") 24 | Swift.print("scrollHeight: " + "\(scrollHeight)") 25 | */ 26 | //continue here: get both directions working, clip the values in sliderlist only 27 | 28 | let y:CGFloat = round(progress * scrollHeight)//Things may actually be smoother if you remove the round variable 29 | return -y 30 | } 31 | } 32 | //⚠️️ DEPRECATED; remove 33 | /*extension ScrollableUtils{ 34 | /** 35 | * Scrolls the list to a scalar position (value 0-1) 36 | */ 37 | static func scrollTo(_ scrollable: DEPRECATEDIScrollable, _ progress:CGFloat){ 38 | let y:CGFloat = ScrollableUtils.scrollTo(progress, scrollable.height, scrollable.itemsHeight) 39 | scrollable.lableContainer!.y = y/*we offset the y position of the lableContainer*/ 40 | } 41 | } 42 | */ 43 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/ElasticScrollList5.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class ElasticScrollList5:ScrollerList5,Elastic5 { 4 | lazy var moverGroup:MoverGroup = self.createMoverGroup 5 | lazy var rbContainer:Container = self.createRBContainer/*Needed for the overshot animation, the rbContainer has the contentContainer*/ 6 | // 7 | private var elasticHandler:ElasticScrollerHandler5 {return handler as! ElasticScrollerHandler5}//move this to extension somewhere 8 | override lazy var handler:ProgressHandler = ElasticScrollerHandler5(progressable:self) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/ElasticScrollerFastList5.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class ElasticScrollerFastList5:ScrollerFastList5,Elastic5 { 4 | lazy var moverGroup:MoverGroup = MoverGroup(self.setProgressVal,self.maskSize,self.contentSize)//⚠️️ remember to add eventHandler 5 | lazy var rbContainer:Container = self.createRBContainer/*Needed for the overshot animation*/ 6 | // 7 | private var elasticHandler:ElasticScrollerFastListHandler {return handler as! ElasticScrollerFastListHandler}//move this to extension somewhere 8 | override lazy var handler:ProgressHandler = ElasticScrollerFastListHandler(progressable:self) 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/ElasticSliderScrollerFastList5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticSliderScrollerFastList5:SliderScrollerFastList5,Elastic5 { 5 | lazy var moverGroup:MoverGroup = MoverGroup(self.setProgressValue,self.maskSize,self.contentSize)//⚠️️ remember to add eventHandler 6 | lazy var rbContainer:Container = self.createRBContainer/*Needed for the overshot animation*/ 7 | // 8 | private var elasticHandler:ElasticSliderScrollerFastListHandler {return handler as! ElasticSliderScrollerFastListHandler}//move this to extension somewhere 9 | override lazy var handler:ProgressHandler = ElasticSliderScrollerFastListHandler(progressable:self) 10 | } 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/ElasticSliderScrollerList5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticSliderScrollerList5:SliderScrollerList5,Elastic5 { 5 | lazy var moverGroup:MoverGroup = self.createMoverGroup 6 | lazy var rbContainer:Container = self.createRBContainer/*Needed for the overshot animation*/ 7 | 8 | private var elasticHandler:ElasticSliderScrollerHandler {return handler as! ElasticSliderScrollerHandler}//⚠️️ this can be added to a prtocol and extension, in fact all handlers like this can be 9 | override lazy var handler:ProgressHandler = ElasticSliderScrollerHandler(progressable:self) 10 | 11 | override func onEvent(_ event:Event) { 12 | if event.type == AnimEvent.stopped { 13 | Swift.print("ElasticSlideScrollList3.onEvent: " + "\(event.type)") 14 | let dir:Dir = event.origin === moverGroup.yMover ? .ver : .hor 15 | Swift.print("bounce back anim stopp dir: \(dir)") 16 | hideSlider(dir)/*hides the slider when bounce back anim stopps*/ 17 | } 18 | super.onEvent(event) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/ScrollerFastList5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ScrollerFastList5:FastList5 { 5 | private var scrollHandler:ScrollerFastListHandler {return handler as! ScrollerFastListHandler} 6 | override lazy var handler:ProgressHandler = ScrollerFastListHandler(progressable:self) 7 | /** 8 | * Overrides the native scrollWheel method (this is the heart of scrolling) 9 | */ 10 | override open func scrollWheel(with event:NSEvent) { 11 | // Swift.print("ScrollerFastList5.scrollWheel") 12 | scrollHandler.scroll(event) 13 | super.scrollWheel(with: event) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/SliderScrollerFastList5.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class SliderScrollerFastList5:ScrollerFastList5,Slidable5{ 4 | lazy var hSlider:Slider = self.createHSlider 5 | lazy var vSlider:Slider = self.createVSlider 6 | private var sliderHandler:SliderScrollerFastListHandler {return handler as! SliderScrollerFastListHandler} 7 | override lazy var handler:ProgressHandler = SliderScrollerFastListHandler(progressable:self) 8 | } 9 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/SliderScrollerList5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SliderScrollerList5:SliderList5,Scrollable5 { 5 | private var scrollHandler:ScrollHandler {return handler as! ScrollHandler} 6 | override lazy var handler:ProgressHandler = SliderScrollerHandler(progressable:self) 7 | /** 8 | * TODO: ⚠️️ Try to override with generics ContainerView etc, in swift 4 this could probably be done with where Self:... nopp wont work 🚫 9 | */ 10 | override open func scrollWheel(with event: NSEvent) { 11 | scrollHandler.scroll(event) 12 | super.scrollWheel(with: event) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/ScrollerFastListHandler.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ScrollerFastListHandler:ScrollHandler,FastListableDecorator { 5 | override func onScrollWheelChange(_ event:NSEvent) { 6 | // Swift.print("ScrollerFastListHandler.onScrollWheelChange") 7 | // super.onScrollWheelChange(event) 8 | //Swift.print("ScrollableFastListable3.onScrollWheelChange()") 9 | let primaryProgress:CGFloat = SliderListUtils.progress(event, dir, interval(dir), progress(dir)) 10 | // setProgress(primaryProgress)//TODO: ⚠️️ this is not good. that you have to call setProgress one after the other. 11 | super.setProgress(primaryProgress,dir)//we move the containerView as normal 12 | (fastListable as? FastList5)?.setProgress(primaryProgress)//we use the pos of containerView to move the fastList items around 13 | // ScrollFastList3.numOfEvents! += 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/SliderScrollerFastListHandler.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SliderScrollerFastListHandler:ScrollerFastListHandler,SlidableDecorater { 5 | /** 6 | * TODO: ⚠️️ you could also override scroll and hock after the forward scroll call and then retrive the progress from the var. less code, but the value must be written in Displaceview, it could mess up Elastic, because it needs different progress. etc, do later 7 | */ 8 | override func onScrollWheelChange(_ event:NSEvent) { 9 | super.onScrollWheelChange(event) 10 | let progressVal:CGPoint = SliderListUtils.progress(event.delta, interval, progress) 11 | setProgress(progressVal) 12 | } 13 | override func onInDirectScrollWheelChange(_ event:NSEvent) {//enables momentum 14 | super.onInDirectScrollWheelChange(event) 15 | onScrollWheelChange(event) 16 | } 17 | override func onScrollWheelEnter() { 18 | super.onScrollWheelEnter() 19 | showSlider() 20 | } 21 | override func onScrollWheelCancelled() { 22 | super.onScrollWheelEnter() 23 | hideSlider() 24 | } 25 | override func onScrollWheelExit() { 26 | super.onScrollWheelExit() 27 | hideSlider() 28 | } 29 | override func onScrollWheelMomentumBegan(_ event:NSEvent) { 30 | super.onScrollWheelMomentumBegan(event) 31 | showSlider()//cancels out the hide call when onScrollWheelExit is called when you release after pan gesture 32 | } 33 | /** 34 | * Called only be called when scrollwheel becomes stationary. find the code that does this. 35 | */ 36 | override func onScrollWheelMomentumEnded() { 37 | super.onScrollWheelMomentumEnded() 38 | hideSlider() 39 | } 40 | /** 41 | * (0-1) 42 | */ 43 | override func setProgress(_ point:CGPoint){ 44 | // Swift.print("SliderScrollerHandler.setProgress") 45 | super.setProgress(point) 46 | //Swift.print("🏂 Slidable3.setProgress: " + "\(point)") 47 | slider(.hor).setProgressValue(point.x) 48 | slider(.ver).setProgressValue(point.y) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/decorator/ElasticDecorator.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | //rename to ....Accessor 4 | protocol ElasticDecorator:ProgressableDecorator,Elastic5 {} 5 | 6 | extension ElasticDecorator{ 7 | var moverGroup: MoverGroup {get{return elastic.moverGroup}set{elastic.moverGroup = newValue}} 8 | var rbContainer: Container {return elastic.rbContainer} 9 | var elastic:Elastic5 {return progressable as! Elastic5} 10 | } 11 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/decorator/FastListableDecorator.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | //rename to ....Accessor 4 | protocol FastListableDecorator:ProgressableDecorator,FastListable5 {} 5 | 6 | extension FastListableDecorator{ 7 | var selectedIdx: Int? {get{return fastListable.selectedIdx} set{fastListable.selectedIdx = newValue}} 8 | var pool: [FastListItem] {get{return fastListable.pool} set{fastListable.pool = newValue}} 9 | var inActive: [FastListItem] {get{return fastListable.inActive} set{fastListable.inActive = newValue}} 10 | //List 11 | var dp: DataProvider {get{return fastListable.dp}} 12 | var dir: Dir {get{return fastListable.dir}} 13 | func reUse(_ listItem: FastListItem) { 14 | fastListable.reUse(listItem) 15 | } 16 | func createItem(_ index: Int) -> Element { 17 | return fastListable.createItem(index) 18 | } 19 | //override scroll I think?, make sure fastlist gets it's setProgress 🏀 20 | 21 | var fastListable:FastListable5 {return progressable as! FastListable5} 22 | } 23 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/decorator/ProgressableDecorator.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | //rename to ....Accessor 4 | protocol ProgressableDecorator:Progressable5 { 5 | var progressable:Progressable5 {get} 6 | } 7 | extension ProgressableDecorator{ 8 | func progress(_ dir:Dir) -> CGFloat {return progressable.progress(dir)} 9 | func interval(_ dir:Dir) -> CGFloat {return progressable.interval(dir)}/*describes the speed when scrolling (distance per scroll tick)*/ 10 | // func setProgress(_ progress:CGFloat,_ dir:Dir) {progressable.setProgress(progress, dir)} 11 | // func setProgress(_ point:CGPoint) {progressable.setProgress(point)} 12 | // 13 | //⚠️️ maybe move to ContainableDecorator? 14 | var contentContainer:Element {get{return progressable.contentContainer}/*set{list.contentContainer = newValue}*/} 15 | var maskSize:CGSize {get{return progressable.maskSize}/*set{list.maskSize = newValue}*/} 16 | var contentSize:CGSize {get{return progressable.contentSize}/*set{list.contentSize = newValue}*/} 17 | var itemSize:CGSize {return progressable.itemSize/*fatalError("must be overriden in subclass")*//*return CGSize(24,24)*/}//temp, use a static interval like 4 or 8, then use itemsize only for listable etc 18 | } 19 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/decorator/SlidableDecorater.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | //rename to ....Accessor 4 | protocol SlidableDecorater:ProgressableDecorator,Slidable5 {} 5 | 6 | extension SlidableDecorater{ 7 | var vSlider:Slider {get{return slidable.vSlider}} 8 | var hSlider:Slider {get{return slidable.hSlider}} 9 | var slidable:Slidable5 {return progressable as! Slidable5} 10 | 11 | /*NOTE: If you need only one slider, then override both hor and ver with this slider*/ 12 | func slider(_ dir:Dir) -> Slider { return dir == .ver ? vSlider : hSlider}/*Convenience*/ 13 | } 14 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/elastic/ElasticSliderScrollerFastListHandler.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticSliderScrollerFastListHandler:ElasticScrollerFastListHandler,SlidableDecorater { 5 | /** 6 | * TODO: ⚠️️ you could also override scroll and hock after the forward scroll call and then retrive the progress from the var. less code, but the value must be written in Displaceview, it could mess up Elastic, because it needs different progress. etc, do later 7 | */ 8 | override func onScrollWheelChange(_ event:NSEvent) { 9 | super.onScrollWheelChange(event) 10 | let progressVal:CGPoint = SliderListUtils.progress(event.delta, interval, progress) 11 | setProgress(progressVal) 12 | } 13 | override func onInDirectScrollWheelChange(_ event:NSEvent) {//enables momentum 14 | super.onInDirectScrollWheelChange(event) 15 | onScrollWheelChange(event) 16 | } 17 | override func onScrollWheelEnter() { 18 | super.onScrollWheelEnter() 19 | showSlider() 20 | } 21 | override func onScrollWheelCancelled() { 22 | super.onScrollWheelEnter() 23 | hideSlider() 24 | } 25 | override func onScrollWheelExit() { 26 | super.onScrollWheelExit() 27 | hideSlider() 28 | } 29 | override func onScrollWheelMomentumBegan(_ event:NSEvent) { 30 | super.onScrollWheelMomentumBegan(event) 31 | showSlider()//cancels out the hide call when onScrollWheelExit is called when you release after pan gesture 32 | } 33 | /** 34 | * Called only be called when scrollwheel becomes stationary. find the code that does this. 35 | */ 36 | override func onScrollWheelMomentumEnded() { 37 | super.onScrollWheelMomentumEnded() 38 | hideSlider() 39 | } 40 | /** 41 | * (0-1) 42 | */ 43 | override func setProgress(_ point:CGPoint){ 44 | // Swift.print("SliderScrollerHandler.setProgress") 45 | super.setProgress(point) 46 | //Swift.print("🏂 Slidable3.setProgress: " + "\(point)") 47 | slider(.hor).setProgressValue(point.x) 48 | slider(.ver).setProgressValue(point.y) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/handlers/elastic/ElasticSliderScrollerHandler.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticSliderScrollerHandler:ElasticScrollerHandler5,SlidableDecorater { 5 | 6 | override func onScrollWheelChange(_ event:NSEvent) { 7 | Swift.print("ElasticSliderScrollerHandler.onScrollWheelChange") 8 | super.onScrollWheelChange(event) 9 | let progressVal:CGPoint = SliderListUtils.progress(event.delta, interval, progress) 10 | setProgress(progressVal) 11 | } 12 | override func onInDirectScrollWheelChange(_ event:NSEvent) {//enables momentum 13 | super.onInDirectScrollWheelChange(event) 14 | onScrollWheelChange(event) 15 | } 16 | override func onScrollWheelEnter() { 17 | Swift.print("SliderScrollerHandler.onScrollWheelEnter") 18 | super.onScrollWheelEnter() 19 | showSlider() 20 | } 21 | override func onScrollWheelCancelled() { 22 | super.onScrollWheelEnter() 23 | hideSlider() 24 | } 25 | override func onScrollWheelExit() { 26 | Swift.print("SliderScrollerHandler.onScrollWheelExit") 27 | super.onScrollWheelExit() 28 | hideSlider() 29 | } 30 | override func onScrollWheelMomentumBegan(_ event:NSEvent) { 31 | super.onScrollWheelMomentumBegan(event) 32 | showSlider()//cancels out the hide call when onScrollWheelExit is called when you release after pan gesture 33 | } 34 | /** 35 | * Called only be called when scrollwheel becomes stationary. find the code that does this. 36 | */ 37 | override func onScrollWheelMomentumEnded() { 38 | super.onScrollWheelMomentumEnded() 39 | hideSlider() 40 | } 41 | /** 42 | * (0-1) 43 | */ 44 | override func setProgress(_ point:CGPoint){ 45 | // Swift.print("SliderScrollerHandler.setProgress") 46 | // super.setProgress(point) 47 | //Swift.print("🏂 Slidable3.setProgress: " + "\(point)") 48 | slider(.hor).setProgressValue(point.x) 49 | slider(.ver).setProgressValue(point.y) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/protocol/Elastic5+Slidable5+FastListable5+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | extension Elastic5 where Self:Slidable5, Self:FastListable5 { 5 | func setProgressValue(_ value:CGFloat, _ dir:Dir) {/*Gets calls from MoverGroup*/ 6 | //Swift.print("ElasticSlidableScrollableFastListable3.setProgressValue(val,dir)") 7 | setProgressVal(value, dir)//forward 8 | let sliderProgress = ElasticUtils.progress(value,contentSize[dir],maskSize[dir])//doing some double calculations here 9 | slider(dir).setProgressValue(sliderProgress)//temp fix 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/composition/protocol/Elastic5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol Elastic5:Progressable5 { 5 | var moverGroup:MoverGroup {get set} 6 | var rbContainer:Container {get}/*Needed for the overshot animation*/ 7 | } 8 | extension Elastic5{ 9 | /** 10 | * TODO: ⚠️️ Probably move movergroup out of protocol extension as you need to be able to override setProgress in subclasses and that isn't possible if it resides inside a protocol extension 11 | */ 12 | var createMoverGroup:MoverGroup { 13 | let setProgress = { (_ value:CGFloat,_ dir:Dir) -> Void in 14 | // guard dir == .hor else {return} 15 | self.contentContainer.layer?.position[dir] = value 16 | } 17 | var group = MoverGroup(setProgress,self.maskSize,self.contentSize) 18 | group.event = (self as! EventSendable).onEvent/*Add an eventHandler for the mover object, , this has no functionality in this class, but may have in classes that extends this class, like hide progress-indicator when all animation has stopped*/ 19 | return group 20 | } 21 | var createRBContainer:Container { 22 | /*Swift.print("create rbContainer")*/ 23 | let rbContainer = (self as! NSView).addSubView(Container.init(size: (self as! ElementKind).skinSize, id: "rb"))//⚠️️TODO: move to lazy var later 24 | rbContainer.addSubview(contentContainer)/*Adds content Container inside rbContainer*/ 25 | //fatalError("the bellow must be fixed") 26 | Swift.print("ElasticScrollFastList3. the bellow must be fixed") 27 | //contentContainer.parent = rbContainer/*Set the correct parent*/ 28 | return rbContainer 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/fast/protocol/FastListable5.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol FastListable5:Progressable5,Listable5{ 4 | var selectedIdx:Int? {get set}/*TODO: ⚠️️ rename to just selected?*/ 5 | var pool:[FastListItem] {get set}/*Stores UI item and Absolute Index*/ 6 | func reUse(_ listItem:FastListItem) 7 | func createItem(_ index:Int) -> Element 8 | var inActive:[FastListItem] {get set} 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/fast/utils/FastListHandler.swift: -------------------------------------------------------------------------------- 1 | //import Cocoa 2 | //@testable import Utils 3 | // 4 | //class FastListHandler:ProgressHandler,FastListable5{ 5 | // var selectedIdx: Int? {get{return fastListable.selectedIdx}set{fastListable.selectedIdx = newValue}} 6 | // var pool: [FastListItem] {get{return fastListable.pool}set{fastListable.pool = newValue}} 7 | // var inActive: [FastListItem] {get{return fastListable.inActive}set{fastListable.inActive = newValue}} 8 | // func reUse(_ listItem:FastListItem) {fastListable.reUse(listItem)} 9 | // func createItem(_ index: Int) -> Element { 10 | // return fastListable.createItem(index) 11 | // } 12 | // var dir: Dir {return fastListable.dir} 13 | // var dp:DataProvider {get{return fastListable.dp}/*set{fastListable.dp = newValue}*/} 14 | // var fastListable:FastListable5 {return self.progressable as! FastListable5} 15 | // 16 | //} 17 | 18 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/list/List5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Investigate the theory: Too bost smmoothness of scrolling: Don’t place views on fractions of pixels x = 34.5, always use ceil 5 | */ 6 | class List5:ContainerView5,Listable5 { 7 | var dp:DataProvider {get{return config.dp} set{config.dp = newValue}} 8 | var dir:Dir {get{return config.dir} set{config.dir = newValue}} 9 | var itemSize:CGSize {get{return config.itemSize} set{config.itemSize = newValue}} 10 | // 11 | override var contentSize:CGSize { return dir == .hor ? CGSize(dp.count * itemSize.width ,maskSize.h) : CGSize(maskSize.w ,dp.count * itemSize.height) } 12 | 13 | struct Config {//TODO: ⚠️️ can this be moved into an extension? 14 | var itemSize:CGSize,dp:DP,dir:Dir 15 | static let defaultConfig:Config = .init(itemSize:CGSize(0,0), dp:DP.init(), dir:.ver) 16 | } 17 | var config:Config 18 | 19 | init(config:Config = Config.defaultConfig, size: CGSize = CGSize(), id: String? = "") { 20 | self.config = config 21 | super.init(size: size, id: id) 22 | self.dp.event = onEvent/*Add event handler for the dataProvider*/ 23 | layer!.masksToBounds = true/*masks the children to the frame, I don't think this works, seem to work now*/ 24 | } 25 | /** 26 | * Creates the components in the List Component 27 | */ 28 | override func resolveSkin() { 29 | super.resolveSkin() 30 | mergeAt(dp.items, 0) 31 | } 32 | /** 33 | * Creates and adds items to the _lableContainer 34 | * TODO: ⚠️️ possibly move into ListModifier, TreeList has it's mergeAt in an Utils class see how it does it 35 | */ 36 | func mergeAt(_ dictionaries:[[String:String]], _ index:Int){//TODO: possible rename to something better, placeAt? insertAt? 37 | var i:Int = index 38 | for dict:[String:String] in dictionaries { 39 | let item = createItem(dict,i) 40 | contentContainer.addSubviewAt(item, i+1)/*the first index is reserved for the List skin, what, this is not good! ⚠️️ ⚠️️ ⚠️️?*/ 41 | i += 1 42 | } 43 | } 44 | /** 45 | * Overridable 46 | */ 47 | func createItem(_ dict:[String:String], _ i:Int) -> NSView{//TODO: ⚠️️ add throws here 48 | let dictItem:String = dict["title"] ?? {fatalError("err")}() 49 | let item:SelectTextButton = SelectTextButton(itemSize.width, itemSize.height ,dictItem, false) 50 | return item 51 | } 52 | override func getClassType() -> String { 53 | return dir == .ver ? "List" : "VList"//<--this is actually wrong, use HList instead. and correct the css name 54 | } 55 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/list/protocol/Listable5.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | protocol Listable5:Containable5 {//extending Containable3 is new 5 | var dp:DataProvider {get} 6 | var dir:Dir {get}//the "primary" direction the list scroll in. the secondary direction is the opposite 7 | var itemSize:CGSize{get} 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/progress/ProgressableList5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ProgressableList5:List5,Progressable5 { 5 | var progressable:Progressable5 {return self}//move this somewhere else 6 | lazy var handler:ProgressHandler = .init(progressable:self) 7 | // 8 | func progress(_ dir:Dir) -> CGFloat {return handler.progress(dir)} 9 | func interval(_ dir:Dir) -> CGFloat {return handler.interval(dir)}/*describes the speed when scrolling (distance per scroll tick)*/ 10 | func setProgress(_ progress:CGFloat,_ dir:Dir) { 11 | handler.setProgress(progress, dir) 12 | } 13 | func setProgress(_ point:CGPoint) { 14 | handler.setProgress(point) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/progress/protocol/Progressable5.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | protocol Progressable5:Containable5{ 5 | func progress(_ dir:Dir) -> CGFloat/*0-1 atBegining <-> atEnd*/ 6 | func interval(_ dir:Dir) -> CGFloat/*describes the speed when scrolling (distance per scroll tick)*/ 7 | func setProgress(_ progress:CGFloat,_ dir:Dir) 8 | func setProgress(_ point:CGPoint)//added back recently 9 | var itemSize:CGSize {get} 10 | } 11 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/progress/utils/ProgressHandler.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class ProgressHandler:ProgressableDecorator { 5 | var progressable:Progressable5 6 | init(progressable:Progressable5){ 7 | self.progressable = progressable 8 | } 9 | func interval(_ dir:Dir) -> CGFloat{return floor(contentSize[dir] - maskSize[dir])/itemSize[dir]}// :TODO: use ScrollBarUtils.interval instead?// :TODO: explain what this is in a comment 10 | func progress(_ dir:Dir) -> CGFloat{ 11 | let sliderProgress:CGFloat = SliderParser.progress(contentContainer.layer!.position[dir], maskSize[dir], contentSize[dir]) 12 | return sliderProgress 13 | } 14 | var interval:CGPoint {return CGPoint(interval(.hor),interval(.ver))}//convenience 15 | var progress:CGPoint {return CGPoint(progress(.hor),progress(.ver))}//convenience 16 | 17 | /** 18 | * PARAM: progress: 0-1 19 | */ 20 | func setProgress(_ p:CGPoint){ 21 | // Swift.print("ProgressHandler.setProgress: " + "\(p)") 22 | setProgress(p.x, .hor) 23 | setProgress(p.y,.ver) 24 | } 25 | /** 26 | * PARAM: progress: 0-1 27 | */ 28 | func setProgress(_ progress:CGFloat,_ dir:Dir){ 29 | // Swift.print("ProgressHandler.setProgress: " + " progress: \(progress) dir: \(dir)") 30 | let progressValue = contentSize[dir] < maskSize[dir] ? 0 : progress/*pins the lableContainer to the top if itemsHeight is less than height*/ 31 | // Swift.print("progressValue: " + "\(progressValue)") 32 | ScrollableUtils.scrollTo(progressable,progressValue,dir) 33 | } 34 | } 35 | extension ScrollableUtils{//temp migration fix 36 | /** 37 | * NOTE: I'm unsure if disabling anim on the container.y pos is needed 38 | */ 39 | static func scrollTo(_ containable:Containable5, _ progress:CGFloat, _ dir:Dir){ 40 | // Swift.print("scrollTo") 41 | let val:CGFloat = ScrollableUtils.scrollTo(progress, containable.maskSize[dir], containable.contentSize[dir]) 42 | // Swift.print("val: " + "\(val)") 43 | containable.contentContainer.layer?.position[dir] = val/*we offset the y position of the lableContainer*/ 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/scroll/ScrollerList5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | class ScrollerList5:ProgressableList5,Scrollable5{ 4 | private var scrollHandler:ScrollHandler {return handler as! ScrollHandler} 5 | override lazy var handler:ProgressHandler = ScrollHandler(progressable:self) 6 | /** 7 | * Overrides the native scrollWheel and passes this call to the scrollHandler 8 | */ 9 | override open func scrollWheel(with event: NSEvent) { 10 | // Swift.print("ScrollList5.scrollWheel") 11 | scrollHandler.scroll(event) 12 | super.scrollWheel(with: event) 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/scroll/protocol/Scrollable5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | protocol Scrollable5:Progressable5{ 4 | // func onScrollWheelChange(_ event:NSEvent)/*Fires onle while direct scroll, not momentum*/ 5 | // func onScrollWheelEnter()/*fires while there still is momentum aka indirect scroll*/ 6 | // func onScrollWheelExit()/*This happens after there has been panning and the touches release*/ 7 | // func onScrollWheelCancelled()/*This happens when there has been no panning, just 2 finger touch and release with out moving around*/ 8 | // func onInDirectScrollWheelChange(_ event:NSEvent)//rename to onScrollWheelMomentumChange 9 | // /*Momentum*/ 10 | // func onScrollWheelMomentumEnded() 11 | // func onScrollWheelMomentumBegan(_ event:NSEvent)/*This happens right after touch release and there is enough velocity that momentum is engaged, we forward the event because we might need the scrollingDelta for velocity*/ 12 | } 13 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/slider/SliderList5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | //we dont use this class directly, but extend it 4 | class SliderList5:ProgressableList5,Slidable5 { 5 | lazy var hSlider:Slider = self.createHSlider 6 | lazy var vSlider:Slider = self.createVSlider 7 | 8 | override func onEvent(_ event:Event) { 9 | if let event:SliderEvent = event as? SliderEvent, event.type == SliderEvent.change { 10 | let dir:Dir = event.origin === hSlider ? .hor : .ver 11 | handler.setProgress(event.progress,dir) 12 | } 13 | super.onEvent(event) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Element/ui/list/v5/list/slider/protocol/Slidable5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol Slidable5:Progressable5 { 5 | var hSlider:Slider {get} 6 | var vSlider:Slider {get} 7 | /*func updateSlider()*/ 8 | func slider(_ dir:Dir) -> Slider/*?*/ 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/ui/other/graph/GraphLine.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * GraphLine represents the Visual Graph line int the Graph component. 5 | * NOTE: GraphLine avoids adding the skin to its view, and then passes on the StyleProperty to the custom PathLine. This enables you to set the style via CSS 6 | * TODO: ⚠️️ Contemplate Adding a new Skin type that you manually add instead of resolve, this way you dont have to add PathGraphic etc 7 | */ 8 | class GraphLine:Element{ 9 | var line:PathGraphic?//<--we could also use PolyLineGraphic, but we may support curvey Graphs in the future, TODO: ⚠️️ make it lazy 10 | var path:PathKind 11 | //update the init ⚠️️ 12 | init(_ width:CGFloat, _ height:CGFloat,_ path:PathKind, _ parent:ElementKind? = nil, _ id:String? = nil) { 13 | self.path = path 14 | super.init(size:CGSize(width,height),id:id) 15 | } 16 | override func resolveSkin() { 17 | //Swift.print("GraphLine.resolveSkin") 18 | skin = SkinResolver.skin(self)//you could use let style:IStyle = StyleResolver.style(element), but i think skin has to be created to not cause bugs 19 | //I think the most apropriate way is to make a custom skin and add it as a subView wich would implement :ISkin etc, see TextSkin for details 20 | //Somehow derive the style data and make a basegraphic with it 21 | let lineStyle:LineStylable = StylePropertyParser.lineStyle(skin!)!//<--grab the style from that was resolved to this component 22 | //LineStyleParser.describe(lineStyle) 23 | line = PathGraphic(path,nil,lineStyle) 24 | _ = addSubView(line!.graphic) 25 | line!.draw() 26 | } 27 | //override func setSkinState(_ skinState:String) { 28 | //update the line, implement this if you want to be able to set the theme of this component 29 | //} 30 | override func setSize(_ width:CGFloat, _ height:CGFloat) { 31 | //update the line, implement this if you need win resize support for this component 32 | } 33 | required init(coder:NSCoder) {fatalError("init(coder:) has not been implemented") } 34 | } 35 | -------------------------------------------------------------------------------- /src/Element/ui/other/misc/FilePicker.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * When you need to browse for files or urls 5 | * NOTE: this is a Composition of other UI components 6 | * TODO: ⚠️️ Add some event logic to this class, and modal popup 7 | * TODO: ⚠️️ Add data accessorts to this class 8 | * TODO: ⚠️️ add FilePickerEvent eventually 9 | */ 10 | class FilePicker:Element{ 11 | typealias InitText = (text:String,input:String,button:String) 12 | lazy var textInput:TextInput = createTextInput() 13 | lazy var button:TextButton = createButton() 14 | private let text:InitText 15 | init(text:InitText, size:CGSize = CGSize(NaN,NaN), id:String? = nil) { 16 | self.text = text 17 | super.init(size:size, id:id) 18 | } 19 | override func resolveSkin() { 20 | super.resolveSkin() 21 | addSubview(textInput) 22 | addSubview(button) 23 | } 24 | override func onEvent(_ event: Event) { 25 | if event.assert(.upInside) { 26 | onBrowseButtonClick() 27 | } 28 | super.onEvent(event) 29 | } 30 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented") } 31 | } 32 | extension FilePicker{ 33 | func onBrowseButtonClick(){ 34 | //Swift.print("onBrowseButtonClick") 35 | let dialog:NSOpenPanel = NSOpenPanel()//prompt the file viewer 36 | dialog.canCreateDirectories = true 37 | dialog.title = "Select path" 38 | dialog.canChooseDirectories = true 39 | dialog.canChooseFiles = true 40 | dialog.directoryURL = { 41 | let dirURLStr:String = textInput.inputText 42 | return dirURLStr.tildePath.url 43 | }() 44 | let respons = dialog.runModal() 45 | if let url = dialog.url,respons == NSApplication.ModalResponse.OK{ 46 | textInput.setInputText(url.path.tildify) 47 | onEvent(Event.init(Event.update, self)) 48 | } 49 | } 50 | func createTextInput()->TextInput{ 51 | return .init(self.getWidth(),self.getHeight(),text.text,text.input,self,"inputText") 52 | } 53 | func createButton()->TextButton{ 54 | return .init(self.getWidth(), self.getHeight(), text.button, self) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Element/ui/other/misc/abstract/Disablable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol Disableable:SkinStateChangeable{ 4 | var isDisabled:Bool {get set} 5 | } 6 | extension Disableable{ 7 | func setDisabled(_ disabled:Bool){ 8 | self.isDisabled = disabled 9 | self.skinState = {return skinState}() 10 | //you have to set: isInteractive = !disabled//This is the only way to disable things in NSView 11 | } 12 | func getDisabled()->Bool{ 13 | return self.isDisabled 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/Element/ui/other/misc/abstract/Focusable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol Focusable:SkinStateChangeable{ 4 | var isFocused:Bool {get set} 5 | } 6 | extension Focusable{ 7 | func setFocused(_ focused:Bool){ 8 | self.isFocused = focused 9 | self.skinState = {return skinState}() 10 | } 11 | func getFocused()->Bool{ 12 | return self.isFocused 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/Element/ui/other/misc/abstract/Lable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol LableKind{ 4 | var text:Text {get set} 5 | func getText()->String 6 | func setTextValue(_ text:String) 7 | } 8 | -------------------------------------------------------------------------------- /src/Element/ui/other/misc/abstract/SkinStateChangeable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * This exist so that focusable and disableable can work 4 | */ 5 | protocol SkinStateChangeable:class { 6 | var skinState:String {get set} 7 | // func getSkinState() -> String 8 | // func setSkinState(_ state:String) 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/abstract/Containable3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol Containable3:class{/*We extend class because it will use NSView anyway*/ 5 | var maskSize:CGSize {get} 6 | var contentSize:CGSize {get} 7 | var contentContainer:Element {get} 8 | } 9 | extension Containable3{ 10 | var width:CGFloat {return maskSize.width} 11 | var height:CGFloat {return maskSize.height} 12 | } 13 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/abstract/Elastic3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol Elastic3:Progressable3{ 5 | var moverGroup:MoverGroup? {get set} 6 | } 7 | extension Elastic3{ 8 | /** 9 | * PARAM: value: contentContainer x/y value 10 | */ 11 | func setProgress(_ value:CGFloat,_ dir:Dir){ 12 | disableAnim {contentContainer.layer?.position[dir] = value} 13 | } 14 | func setProgress(_ point:CGPoint){ 15 | disableAnim {contentContainer.layer?.position = point} 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/abstract/Progressable3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * NOTE: itemSize is used in the calculations of the interval. 5 | * TODO: ⚠️️ In the future it could be considered optional so we can either set an intervall or base intervall on itemSize etc. 6 | */ 7 | protocol Progressable3:Containable3{ 8 | func progress(_ dir:Dir) -> CGFloat/*0-1 atBegining <-> atEnd*/ 9 | func interval(_ dir:Dir) -> CGFloat/*describes the speed when scrolling (distance per scroll tick)*/ 10 | func setProgress(_ progress:CGFloat,_ dir:Dir) 11 | //func setProgress(_ point:CGPoint) 12 | var itemSize:CGSize {get} 13 | } 14 | extension Progressable3{ 15 | var itemSize:CGSize {fatalError("must be overriden in subclass")/*return CGSize(24,24)*/}//temp, use a static interval like 4 or 8, then use itemsize only for listable etc 16 | func interval(_ dir:Dir) -> CGFloat{return floor(contentSize[dir] - maskSize[dir])/itemSize[dir]}// :TODO: use ScrollBarUtils.interval instead?// :TODO: explain what this is in a comment 17 | func progress(_ dir:Dir) -> CGFloat{return SliderParser.progress(contentContainer.layer!.position[dir], maskSize[dir], contentSize[dir])} 18 | var interval:CGPoint {return CGPoint(interval(.hor),interval(.ver))}//convenience 19 | var progress:CGPoint {return CGPoint(progress(.hor),progress(.ver))}//convenience 20 | /** 21 | * PARAM: progress: 0-1 22 | */ 23 | func setProgress(_ progress:CGFloat,_ dir:Dir){ 24 | //Swift.print("Progressable3.setProgress: " + " progress: \(progress) dir: \(dir)") 25 | let progressValue = self.contentSize[dir] < maskSize[dir] ? 0 : progress/*pins the lableContainer to the top if itemsHeight is less than height*/ 26 | ScrollableUtils.scrollTo(self,progressValue,dir) 27 | } 28 | /** 29 | * PARAM: progress: 0-1 30 | */ 31 | func setProgress(_ p:CGPoint){ 32 | //Swift.print("Progressable3.setProgress: " + "\(point)") 33 | setProgress(p.x,.hor) 34 | setProgress(p.y,.ver) 35 | } 36 | } 37 | private extension ScrollableUtils{//temp migration fix 38 | /** 39 | * NOTE: I'm unsure if disabling anim on the container.y pos is needed 40 | */ 41 | static func scrollTo(_ containable:Containable3, _ progress:CGFloat, _ dir:Dir = .ver){ 42 | let val:CGFloat = ScrollableUtils.scrollTo(progress, containable.maskSize[dir], containable.contentSize[dir]) 43 | disableAnim {(containable.contentContainer as! Container).layerPos(val,dir)}/*we offset the y position of the lableContainer*/ 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/composition/ElasticSlidableScrollable3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol ElasticSlidableScrollable3:Slidable3,ElasticScrollable3{} 5 | extension ElasticSlidableScrollable3{ 6 | /** 7 | * PARAM: value represents real contentContainer x/y value, not 0-1 val 8 | */ 9 | func setProgress(_ value:CGFloat, _ dir:Dir) { 10 | //Swift.print("👻🏂📜ElasticSlidableScrollable3.setProgress() dir: \(dir) value: \(value)") 11 | let sliderProgress = ElasticUtils.progress(value,contentSize[dir],maskSize[dir]) 12 | slider(dir).setProgressValue(sliderProgress)//temp fix 13 | disableAnim{contentContainer.layer?.position[dir] = value}/*using layer.position is alot smoother than frame.origin*/ 14 | } 15 | func scroll(_ event:NSEvent) { 16 | //Swift.print("👻🏂📜 ElasticSlidableScrollable3.scroll()") 17 | (self as Scrollable3).scroll(event)//forward the event 18 | switch event.phase{ 19 | case NSEvent.Phase.changed://Direct scroll, ⚠️️That you need a hock here is not that great 20 | let sliderProgress:CGPoint = ElasticUtils.progress(moverGroup!.result,contentSize,maskSize) 21 | (self as Slidable3).setProgress(sliderProgress) 22 | case NSEvent.Phase.mayBegin, NSEvent.Phase.began:/*same as onScrollWheelEnter()*/ 23 | showSlider() 24 | case NSEvent.Phase.ended://same as onScrollWheelExit() 25 | hideSlider() 26 | default:break; 27 | } 28 | if(event.momentumPhase == NSEvent.Phase.began){//simulates: onScrollWheelMomentumBegan() 29 | showSlider()//cancels out the hide call when onScrollWheelExit is called when you release after pan gesture 30 | } 31 | } 32 | func onScrollWheelCancelled() { 33 | Swift.print("ElasticSlidableScrollable3.onScrollWheelCancelled") 34 | hideSlider() 35 | } 36 | func onInDirectScrollWheelChange(_ event:NSEvent) {}/*override to cancel out the event, put this more central*/ 37 | } 38 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/composition/SlidableScrollable3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: When progress hits 0 or 1 you should hide the slider 5 | */ 6 | protocol SlidableScrollable3:Slidable3,Scrollable3 {} 7 | extension SlidableScrollable3 { 8 | /** 9 | * TODO: you could also override scroll and hock after the forward scroll call and then retrive the progress from the var. less code, but the value must be written in Displaceview, it could mess up Elastic, because it needs different progress. etc, do later 10 | */ 11 | func onScrollWheelChange(_ event:NSEvent) { 12 | //Swift.print("🏂📜 SlidableScrollable3.onScrollWheelChange: \(event.type)") 13 | /*let horProg:CGFloat = SliderListUtils.progress(event.delta[.hor], interval(.hor), slider(.hor).progress)//TODO: ⚠️️ merge these 2 lines into one and make a method in SliderListUtils that returns point 14 | let verProg:CGFloat = SliderListUtils.progress(event.delta[.ver], /*5*/interval(.ver), slider(.ver).progress)*/ 15 | let progressVal:CGPoint = SliderListUtils.progress(event.delta, interval, progress) 16 | (self as Slidable3).setProgress(progressVal) 17 | (self as Scrollable3).setProgress(progressVal) 18 | } 19 | func onInDirectScrollWheelChange(_ event:NSEvent) {//enables momentum 20 | onScrollWheelChange(event) 21 | } 22 | func onScrollWheelEnter() { 23 | showSlider() 24 | } 25 | func onScrollWheelCancelled() { 26 | hideSlider() 27 | } 28 | func onScrollWheelExit() { 29 | hideSlider() 30 | } 31 | func onScrollWheelMomentumBegan(_ event:NSEvent) { 32 | showSlider()//cancels out the hide call when onScrollWheelExit is called when you release after pan gesture 33 | } 34 | /** 35 | * Called only be called when scrollwheel becomes stationary. find the code that does this. 36 | */ 37 | func onScrollWheelMomentumEnded() { 38 | hideSlider() 39 | } 40 | } 41 | 42 | /*func progress(_ dir:Dir)->CGFloat{ 43 | return SliderListUtils.progress(event.delta[dir], interval(dir), slider(dir).progress) 44 | }*/ 45 | 46 | 47 | /*if(event.scrollingDeltaX == 0){ 48 | onScrollWheelMomentumEnded(.hor) 49 | } 50 | if(event.scrollingDeltaY == 0){ 51 | onScrollWheelMomentumEnded(.ver) 52 | }*/ 53 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/concrete/ContainerView3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ContainerView3:Element,Containable3 { 5 | var maskSize:CGSize {return CGSize(super.skinSize.w,super.skinSize.h)}/*Represents the visible part of the content *///TODO: could be ranmed to maskRect, say if you need x and y aswell 6 | var contentSize:CGSize {return CGSize(super.skinSize.w,super.skinSize.h)} 7 | lazy var contentContainer:Element = self.createContentContainer() //was content, but we want to use old css 8 | override init(size:CGSize = CGSize(0,0),id:String? = nil){ 9 | super.init(size:CGSize(size.width,size.height),id:id) 10 | } 11 | override func resolveSkin() { 12 | super.resolveSkin() 13 | _ = contentContainer/*Inits the lazy instance*/ 14 | layer!.masksToBounds = true/*masks the children to the frame, I don't think this works, seem to work now 👍, Works!*/ 15 | } 16 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 17 | } 18 | 19 | extension ContainerView3 {//private maybe? 20 | /** 21 | * TODO: ⚠️️ Try to override with generics ContainerView etc, in swift 4 this could probably be done with where Self:... nopp wont work 🚫 22 | */ 23 | override open func scrollWheel(with event: NSEvent) { 24 | //Swift.print("ContainerView3.scrollWheel") 25 | if(self is ElasticSlidableScrollableFastListable3){ 26 | (self as! ElasticSlidableScrollableFastListable3).scroll(event) 27 | }else if(self is ElasticSlidableScrollable3){ 28 | (self as! ElasticSlidableScrollable3).scroll(event) 29 | }else if(self is Scrollable3){ 30 | (self as! Scrollable3).scroll(event) 31 | }else{ 32 | fatalError("type not supported: \(self)") 33 | } 34 | super.scrollWheel(with: event) 35 | } 36 | func createContentContainer() -> Container { 37 | return self.addSubView(Container(size:self.skinSize,id:"lable"))//<-- ⚠️️ misspelled 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/concrete/ElasticScrollView3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticScrollView3:ContainerView3,ElasticScrollable3{ 5 | override func resolveSkin() { 6 | super.resolveSkin() 7 | } 8 | lazy var moverGroup:MoverGroup? = MoverGroup(self.setProgress,self.maskSize,self.contentSize) 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/concrete/ElasticSlideScrollView3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticSlideScrollView3:SlideView3,ElasticSlidableScrollable3 { 5 | lazy var moverGroup:MoverGroup? = MoverGroup(self.setProgress,self.maskSize,self.contentSize) 6 | override func resolveSkin() { 7 | super.resolveSkin() 8 | moverGroup!.xMover.event = onEvent/*Add an eventHandler for the mover object, , this has no functionality in this class, but may have in classes that extends this class, like hide progress-indicator when all animation has stopped*/ 9 | moverGroup!.yMover.event = onEvent 10 | } 11 | override func onEvent(_ event:Event) { 12 | if(event.type == AnimEvent.stopped){ 13 | //Swift.print("ElasticSlideScrollList3.onEvent: " + "\(event.type)") 14 | let dir:Dir = event.origin === moverGroup!.yMover ? .ver : .hor 15 | Swift.print("bounce back anim stopp dir: \(dir)") 16 | hideSlider(dir)/*hides the slider when bounce back anim stopps*/ 17 | } 18 | super.onEvent(event) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/concrete/ScrollView3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * TODO: Using this alone seems to not to scroll anything. Using ElasticScrollView seems to work 4 | */ 5 | class ScrollView3:ContainerView3,Scrollable3{} 6 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/concrete/SlideScrollView3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class SlideScrollView3:SlideView3,SlidableScrollable3{} 4 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/DEPRECATED/concrete/SlideView3.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SlideView3:ContainerView3, Slidable3 { 5 | var horSlider:Slider? 6 | var verSlider:Slider? 7 | var itemSize:CGSize {return CGSize(24,24)}//sort of a quick fix, one should really override interval with a value of 4 or 10 or something 8 | override func resolveSkin() { 9 | super.resolveSkin() 10 | /*slider*/ 11 | horSlider = self.addSubView(Slider(60,6,.hor,CGSize(30,6),0,self)) 12 | verSlider = self.addSubView(Slider(6,60,.ver,CGSize(6,30),0,self)) 13 | 14 | //let intervalX:CGFloat = floor(contentSize.height - maskSize.height)/24// :TODO: use ScrollBarUtils.interval instead?// :TODO: explain what this is in a comment 15 | /*ver slider*/ 16 | let thumbHeight:CGFloat = SliderParser.thumbSize(skinSize.h/contentSize.height, verSlider!.skinSize.h) 17 | verSlider!.setThumbSide(thumbHeight) 18 | //verSlider!.thumb!.fadeOut()//inits fade out anim on init/**/ 19 | 20 | /*horSlider*/ 21 | /*let thumbWidth:CGFloat = SliderParser.thumbSize(width/itemSize.width, horSlider!.width) 22 | horSlider!.setThumbSide(thumbWidth) 23 | horSlider!.thumb!.fadeOut()//inits fade out anim on init*/ 24 | } 25 | override func onEvent(_ event:Event) { 26 | if(event == SliderEvent.change){ 27 | let dir:Dir = event.origin === horSlider ? .hor : .ver 28 | setProgress((event as! SliderEvent).progress,dir) 29 | }/*events from the slider*/ 30 | super.onEvent(event) 31 | } 32 | } 33 | /*extension SlideView3{ 34 | override open func scrollWheel(with event: NSEvent) { 35 | Swift.print("SlideView3.scrollWheel() \(event.type)") 36 | super.scrollWheel(with: event) 37 | 38 | /*else if(event.phase == NSEventPhase.ended || event.phase == NSEventPhase.cancelled){ 39 | //hideSlider() 40 | }*/ 41 | } 42 | }*/ 43 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/ContainerView5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * 5 | */ 6 | class ContainerView5:Element,Containable5 { 7 | var maskSize:CGSize {return CGSize(super.skinSize.w,super.skinSize.h)}/*Represents the visible part of the content *///TODO: could be ranmed to maskRect, say if you need x and y aswell 8 | var contentSize:CGSize {return CGSize(super.skinSize.w,super.skinSize.h)}//override this in subClasses if content is of a different size 9 | lazy var contentContainer:Element = self.createContentContainer() //was content, but we want to use old css 10 | override init(size:CGSize = CGSize(0,0),id:String? = nil){ 11 | super.init(size:CGSize(size.width,size.height),id:id) 12 | } 13 | override func resolveSkin() { 14 | super.resolveSkin() 15 | _ = contentContainer/*Inits the lazy instance*/ 16 | layer!.masksToBounds = true/*masks the children to the frame, I don't think this works, seem to work now 👍, Works!*/ 17 | } 18 | override func getClassType() -> String {//new 19 | return "ContainerView" 20 | } 21 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 22 | } 23 | 24 | extension ContainerView5 {//private maybe? 25 | func createContentContainer() -> Container { 26 | // Swift.print("self.skinSize: " + "\(self.skinSize)") 27 | return self.addSubView(Container(size:self.skinSize,id:"lable"))//<-- ⚠️️ misspelled 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/ElasticScrollerView5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticScrollerView5:ScrollerView5,Elastic5 { 5 | lazy var moverGroup:MoverGroup = self.createMoverGroup 6 | lazy var rbContainer:Container = self.createRBContainer/*Needed for the overshot animation*/ 7 | // 8 | private var elasticHandler:ElasticScrollerHandler5 {return handler as! ElasticScrollerHandler5}//move this to extension somewhere 9 | override lazy var handler:ProgressHandler = ElasticScrollerHandler5(progressable:self) 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/ElasticSliderScrollerView5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElasticSliderScrollerView5:SliderScrollerView5,Elastic5 { 5 | lazy var moverGroup:MoverGroup = self.createMoverGroup 6 | lazy var rbContainer:Container = self.createRBContainer/*Needed for the overshot animation*/ 7 | 8 | private var elasticHandler:ElasticSliderScrollerHandler {return handler as! ElasticSliderScrollerHandler}//⚠️️ this can be added to a prtocol and extension, in fact all handlers like this can be 9 | override lazy var handler:ProgressHandler = ElasticSliderScrollerHandler(progressable:self) 10 | 11 | override func onEvent(_ event:Event) { 12 | if event.type == AnimEvent.stopped { 13 | Swift.print("ElasticSlideScrollList3.onEvent: " + "\(event.type)") 14 | let dir:Dir = event.origin === moverGroup.yMover ? .ver : .hor 15 | Swift.print("bounce back anim stopp dir: \(dir)") 16 | hideSlider(dir)/*hides the slider when bounce back anim stopps*/ 17 | } 18 | super.onEvent(event) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/ProgressableView5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ProgressableView5:ContainerView5,Progressable5 { 5 | lazy var handler:ProgressHandler = .init(progressable:self)//⚠️️ rename to handler and make it override somehow 6 | // 7 | var itemSize:CGSize {return CGSize(24,24)}//sort of a quick fix, one should really override interval with a value of 4 or 10 or something 8 | func progress(_ dir:Dir) -> CGFloat {return handler.progress(dir)} 9 | func interval(_ dir:Dir) -> CGFloat {return handler.interval(dir)}/*describes the speed when scrolling (distance per scroll tick)*/ 10 | func setProgress(_ progress:CGFloat,_ dir:Dir) { 11 | handler.setProgress(progress, dir) 12 | } 13 | func setProgress(_ point:CGPoint) { 14 | handler.setProgress(point) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/ScrollerView5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * IMPORTANT: ⚠️️ Make sure there is a fill in ContainerView. The alpha can be zero. But there has to be a background in order for scrollwheel to work 5 | */ 6 | class ScrollerView5:ProgressableView5,Scrollable5{ 7 | private var scrollHandler:ScrollHandler {return handler as! ScrollHandler} 8 | override lazy var handler:ProgressHandler = ScrollHandler(progressable:self) 9 | /** 10 | * TODO: ⚠️️ Try to override with generics ContainerView etc, in swift 4 this could probably be done with where Self:... nopp wont work 🚫 11 | */ 12 | override open func scrollWheel(with event: NSEvent) { 13 | // Swift.print("scrollWheel") 14 | scrollHandler.scroll(event) 15 | super.scrollWheel(with: event) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/SliderScrollerView5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SliderScrollerView5:SliderView5,Scrollable5 { 5 | private var scrollerHandler:ScrollHandler {return handler as! ScrollHandler} 6 | override lazy var handler:ProgressHandler = { 7 | return SliderScrollerHandler(progressable:self) 8 | }() 9 | /** 10 | * TODO: ⚠️️ Try to override with generics ContainerView etc, in swift 4 this could probably be done with where Self:... nopp wont work 🚫 11 | */ 12 | override open func scrollWheel(with event: NSEvent) { 13 | scrollerHandler.scroll(event) 14 | super.scrollWheel(with: event) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/SliderView5.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | //we dont use this class directly, but extend it 4 | class SliderView5:ProgressableView5,Slidable5 { 5 | override lazy var handler:ProgressHandler = { 6 | return SliderScrollerHandler(progressable:self) 7 | }() 8 | lazy var hSlider:Slider = self.createHSlider 9 | lazy var vSlider:Slider = self.createVSlider 10 | 11 | override func onEvent(_ event:Event) { 12 | if let event:SliderEvent = event as? SliderEvent, event.type == SliderEvent.change { 13 | let dir:Dir = event.origin === hSlider ? .hor : .ver 14 | handler.setProgress(event.progress,dir) 15 | } 16 | super.onEvent(event) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Element/ui/other/view/v5/protocol/Containable5.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | protocol Containable5:class{/*We extend class because it will use NSView anyway*/ 5 | var maskSize:CGSize {get} 6 | var contentSize:CGSize {get} 7 | var contentContainer:Element {get} 8 | } 9 | extension Containable5{ 10 | var width:CGFloat {return maskSize.width}//try to remove this 11 | var height:CGFloat {return maskSize.height}//try to remove this 12 | } 13 | -------------------------------------------------------------------------------- /src/Element/ui/slider/VolumeSlider.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Upgrade this to use Slider 5 | */ 6 | class VolumeSlider:Slider{ 7 | lazy var volumeGraphic:Element = { 8 | self.addSubViewAt(Element(1,1,self,"volumeGraphic"),self.indexOf(self.thumb)) 9 | }()//lazy so that its only created once 10 | override func resolveSkin() { 11 | super.resolveSkin() 12 | _ = volumeGraphic//TODO: add the volume graphic bellow the thumb 13 | } 14 | @objc override func onMouseMove(event:NSEvent) -> Void { //NSEvent? 15 | volumeGraphic.setSize(thumb.x+thumb.skinSize.w/2, getHeight())//TODO: this should be set after super 16 | super.onMouseMove(event: event) 17 | } 18 | @objc override func onThumbMove(event:NSEvent) -> NSEvent? { 19 | volumeGraphic.setSize(thumb.x+thumb.skinSize.w/2, getHeight())//TODO: this should be set after super 20 | return super.onThumbMove(event:event) 21 | } 22 | @objc override func setProgressValue(_ progress:CGFloat) { 23 | super.setProgressValue(progress) 24 | volumeGraphic.setSize(thumb.x+thumb.skinSize.w/2, getHeight()) 25 | } 26 | override func setSize(_ width:CGFloat, _ height:CGFloat) { 27 | super.setSize(width,height) 28 | volumeGraphic.setSize(thumb.x+thumb.skinSize.w/2, getHeight()) 29 | } 30 | override func getClassType() -> String { 31 | return "\(VolumeSlider.self)" 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/Element/ui/slider/event/SliderEvent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * The progress was refrenced in an extension at first, but it seems more precise to include it in the event. Think multi threaded CPU etc 5 | */ 6 | class SliderEvent:Event{ 7 | static var change:String = "sliderEventChange" 8 | var progress:CGFloat 9 | init(_ type: String, _ progress:CGFloat, _ origin: AnyObject) { 10 | self.progress = progress 11 | super.init(type, origin) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Element/ui/spinner/event/SpinnerEvent.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SpinnerEvent:Event{ 5 | static var change:String = "spinnerEventChange" 6 | var value:CGFloat 7 | init(_ type:String, _ value:CGFloat, _ origin:NSView,_ immediate:AnyObject){ 8 | self.value = value 9 | super.init(type, origin) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Element/ui/stepper/Stepper.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class Stepper:Element{ 5 | lazy var plusButton:Button = createPlusButton() 6 | lazy var minusButton:Button = createMinusButton() 7 | override func resolveSkin() { 8 | super.resolveSkin() 9 | _ = plusButton/*Init the UI*/ 10 | _ = minusButton/*Init the UI*/ 11 | } 12 | } 13 | extension Stepper{ 14 | func createPlusButton() -> Button{ 15 | return self.addSubView(Button(self.skinSize.h,self.skinSize.h,self,"plus")) 16 | } 17 | func createMinusButton() -> Button{ 18 | return self.addSubView(Button(self.skinSize.h,self.skinSize.h,self, "minus")) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/ui/stepper/event/StepperEvent.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class StepperEvent:Event{ 5 | static var change:String = "stepperEventChange" 6 | var value:CGFloat 7 | init(_ type:String, _ value:CGFloat, _ origin:NSView,_ immediate:AnyObject){ 8 | self.value = value 9 | super.init(type, origin) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Element/ui/text/Text.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class Text:Element,TextKind{ 5 | let initText:String//this value is accessed by the TextSkin (It is not meant for external accessing from other classes) 6 | // override var skinSize: CGSize { 7 | // get { 8 | // if let skin = skin { 9 | // return CGSize(skin.getWidth(),getHeight()) 10 | // } 11 | // return super.skinSize 12 | // 13 | // } 14 | // set {super.skinSize = newValue} 15 | // } 16 | init(text:String,size:CGSize = CGSize(0,0),id:String? = nil){ 17 | initText = text 18 | super.init(size: size, id: id) 19 | } 20 | override func resolveSkin() { 21 | super.resolveSkin() 22 | // Swift.print("Text resolveSkin.completed") 23 | } 24 | /** 25 | * Returns "Text" 26 | * NOTE: This function is used to find the correct class type when synthezing the element cascade, in the event that a class subclasses this class 27 | */ 28 | override func getClassType() -> String { 29 | return "\(Text.self)" 30 | } 31 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 32 | //DEPRECATED: 33 | init(_ width:CGFloat, _ height:CGFloat, _ text:String = "defaultText", _ parent:ElementKind? = nil, _ id:String? = nil){ 34 | initText = text 35 | super.init(size:CGSize(width,height),id:id) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Element/ui/text/TextArea.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * :TODO: ⚠️️ Maybe rename text to defaultText and _text to _text 5 | * :TODO: Add support for setting size via css for the TextArea. Its currently not working 6 | */ 7 | class TextArea:Element { 8 | //(self.getWidth(),self.getHeight(),self.textString,self) 9 | lazy var text:Text = {self.addSubView(Text.init(text: self.textString))}() 10 | private var textString:String/*Interim value*/ 11 | 12 | init(text:String,size:CGSize = CGSize(0,0),id:String? = nil){ 13 | self.textString = text 14 | super.init(size: size, id: id) 15 | } 16 | override func resolveSkin() { 17 | super.resolveSkin() 18 | _ = text 19 | } 20 | override func setSize(_ width:CGFloat, _ height:CGFloat) { 21 | super.setSize(width, height) 22 | text.setSize(width, height) 23 | } 24 | func setTextValue(_ textStr:String) { 25 | // Swift.print("TextArea.setTextValue") 26 | text.setText(textStr) 27 | } 28 | func getTextValue() -> String { 29 | // Swift.print("getTextValue() \(text.getText())") 30 | return text.getText() 31 | } 32 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 33 | //DEPRECATED 34 | init(_ width:CGFloat,_ height:CGFloat, _ text:String = "defaultText", _ parent:ElementKind? = nil, _ id:String? = nil) { 35 | self.textString = text 36 | super.init(size:CGSize(width,height),id:id) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Element/ui/text/TextInput.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * TODO: ⚠️️ Through extension you should add a way to set inputTextArea text value. 5 | * TODO: ⚠️️ Rename inputTextArea to inputText 6 | */ 7 | class TextInput:Element{ 8 | lazy var text:Text = createText() 9 | lazy var inputTextArea:TextArea = createInputTextArea() 10 | private let initData:(text:String,input:String)/*interim use only, use inputText etc to get data*/ 11 | 12 | init(text:String,inputText:String,size:CGSize = CGSize(0,0),id:String? = nil){ 13 | self.initData = (text,inputText) 14 | super.init(size: size, id: id) 15 | } 16 | override func resolveSkin() { 17 | super.resolveSkin() 18 | 19 | addSubview(text) 20 | addSubview(inputTextArea) 21 | } 22 | override func setSize(_ width:CGFloat, _ height:CGFloat) { 23 | super.setSize(width, height) 24 | inputTextArea.setSize(width, height)//⚠️️ shouldn't this be setSkin rather? 25 | text.setSize(width, height)//⚠️️ shouldn't this be setSkin rather? 26 | } 27 | override var skinState:String { 28 | get {return super.skinState} 29 | set { 30 | super.skinState = newValue 31 | inputTextArea.skinState = (newValue)//I'm not sure about these anymore. The skinState works different now 32 | text.skinState = (newValue) 33 | } 34 | } 35 | required init(coder: NSCoder) {fatalError("init(coder:) has not been implemented")} 36 | //DEPRECATE 37 | init(_ width:CGFloat, _ height:CGFloat, _ textString:String, _ inputString:String, _ parent:ElementKind? = nil, _ id:String? = nil) { 38 | self.initData = (textString,inputString) 39 | super.init(size:CGSize(width,height),id:id) 40 | } 41 | } 42 | extension TextInput{ 43 | func setInputText(_ text:String){/*Convenience*/ 44 | // Swift.print("TextInput.setInputText") 45 | inputTextArea.setTextValue(text) 46 | } 47 | var inputText:String {return inputTextArea.text.getText()} 48 | func createText() -> Text{ 49 | return Text.init(text: self.initData.text, id: "text")//(self.getWidth(),self.getHeight(),) 50 | } 51 | func createInputTextArea()->TextArea{ 52 | //(self.getWidth(),self.getHeight(),self.initData.input,self,"inputText") 53 | return TextArea.init(text: self.initData.input, id: "inputText") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Element/ui/text/utils/TextKind.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | protocol TextKind:ElementKind { 5 | func setText(_ text:String) 6 | func getTextField()->NSTextField 7 | var initText:String{get} 8 | } 9 | extension TextKind{ 10 | var textField:NSTextField{ 11 | Swift.print("textField") 12 | return (skin as! TextKind).textField 13 | 14 | } 15 | /** 16 | * Sets text to the textfield, remember to set textformat after 17 | * NOTE: to access htmlText: ITextSkin2(_skin).textField.htmlText = htmlText; 18 | * NOTE: Apperently setText() is occupied by obj-c, use var text {get set} in the future 19 | */ 20 | func setText(_ text:String){//<--- rename to setText, figure out away to either rename the text or rename setText, like setTheText or alike, setTextValue 21 | // Swift.print("Thext.setText") 22 | (skin as? TextSkinable)?.setText(text) 23 | } 24 | /** 25 | * Returns the textField text and 26 | */ 27 | func getText()->String{ 28 | return getTextField().stringValue 29 | } 30 | func getTextField()->NSTextField{ 31 | return (skin as! TextSkinable).textField 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/events/TreeListEvent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class TreeListEvent:Event { 5 | static var change:String = "treeListEventChange" 6 | override init(_ type:String = "", _ origin:AnyObject) { 7 | super.init(type, origin) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/TreeList3Asserter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class TreeList3Asserter { 4 | 5 | /** 6 | * idx2d 7 | */ 8 | static func isOpen(_ treeList:TreeListable3, _ idx2d:Int) -> Bool{ 9 | return TreeDPParser.getProp(treeList.treeDP, idx2d, "isOpen") == "true" 10 | } 11 | /** 12 | * idx3d 13 | */ 14 | static func isOpen(_ treeList:TreeListable3, _ idx3d:[Int]) -> Bool{ 15 | return TreeDPParser.getProp(treeList.treeDP, idx3d, "isOpen") == "true" 16 | } 17 | /** 18 | * New 19 | */ 20 | static func hasChildren(_ treeList:TreeListable3,_ idx3d:[Int]) -> Bool{ 21 | return TreeAsserter.hasChildren(treeList.treeDP.tree, idx3d) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/TreeList3Item.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class TreeList3Item:SelectCheckBoxButton/*,ITreeListItem*/ { 5 | override func getClassType() -> String { 6 | return "\(TreeList3Item.self)" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/TreeList3Parser.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class TreeList3Parser { 5 | /** 6 | * Returns idx2d for idx3d 7 | */ 8 | static func idx2d(_ treeList:TreeListable3,_ idx3d:[Int])->Int?{ 9 | return treeList.treeDP[idx3d] 10 | } 11 | /** 12 | * Returns idx3d for idx2d 13 | */ 14 | static func idx3d(_ treeList:TreeListable3,_ idx2d:Int)->[Int]?{ 15 | return treeList.treeDP[idx2d] 16 | } 17 | /** 18 | * Returns the seleted idx3d 19 | * NOTE: to get idx2d, you can use treeList.selectedIdx 20 | */ 21 | static func selected(_ treeList:TreeListable3) -> [Int]?{ 22 | guard let idx2d = treeList.selectedIdx else { 23 | return nil 24 | } 25 | return treeList.treeDP[idx2d] 26 | } 27 | /** 28 | * Returns the selected Tree 29 | */ 30 | static func selected(_ treeList:TreeListable3) -> Tree?{ 31 | guard let idx3d:[Int] = TreeList3Parser.selected(treeList) else{ 32 | return nil 33 | } 34 | return treeList.treeDP.tree.child(idx3d) 35 | } 36 | /** 37 | * Returns idx3d for nsView 38 | */ 39 | static func index(_ treeList:TreeListable3,_ nsView:NSView) -> [Int]?{ 40 | if let match = treeList.pool.first(where: {$0.item === nsView}){ 41 | let idx2d:Int = match.idx 42 | return idx3d(treeList,idx2d) 43 | };return nil 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/TreeList3Utils.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | typealias ItemData3 = (title:String/*,hasChildren:Bool*/,isOpen:Bool,/*isVisible:Bool,*/isSelected:Bool) 4 | class TreeList3Utils { 5 | /** 6 | * Creates a data instance to make it easier to work with the attributes in the xml 7 | */ 8 | private static func itemData(_ tree:Tree)->ItemData3? { 9 | if let props = tree.props{ 10 | let title:String = props["title"] ?? "" 11 | let isOpen:Bool = props["isOpen"] != nil ? props["isOpen"] == "true" : false//<- you can shorten this by doing ?? 12 | let isSelected:Bool = props["isSelected"] != nil ? props["isSelected"] == "true" : false//<- you can shorten this by doing ?? 13 | return ItemData3(title, isOpen, isSelected) 14 | };return nil 15 | } 16 | /** 17 | * New 18 | */ 19 | static func itemData(_ treeList:TreeListable3,_ idx3d:[Int]) -> ItemData3? { 20 | if let tree:Tree = treeList.treeDP.tree[idx3d]{ 21 | return itemData(tree) 22 | } 23 | return nil 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/TreeListable3+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | extension TreeListable3 { 5 | //add convenience methods here 6 | var selected:Tree? {return TreeList3Parser.selected(self)} 7 | func open(_ idx3d:[Int]){/*Convenience*/ 8 | TreeList3Modifier.open(self, idx3d) 9 | } 10 | func close(_ idx3d:[Int]){/*Convenience*/ 11 | TreeList3Modifier.close(self, idx3d) 12 | } 13 | func select(_ idx3d:[Int],_ isSelected:Bool = true) { 14 | TreeList3Modifier.select(self, idx3d, isSelected) 15 | } 16 | func explode(_ idx3d:[Int]) { 17 | TreeList3AdvanceModifier.explode(self, idx3d) 18 | } 19 | func explodeAll(_ idx3d:[Int]) { 20 | TreeList3AdvanceModifier.explodeAll(self, idx3d) 21 | } 22 | func collapse(_ idx3d:[Int]){ 23 | TreeList3AdvanceModifier.collapse(self, idx3d) 24 | } 25 | func collapseAll(_ idx3d:[Int]){ 26 | TreeList3AdvanceModifier.collapseAll(self, idx3d) 27 | } 28 | func unSelectAll(){ 29 | TreeList3Modifier.unSelectAll(self) 30 | } 31 | /** 32 | * TODO: move the idx3d after the item 33 | */ 34 | func insert(_ idx3d:[Int],_ item:Tree){ 35 | TreeList3Modifier.insert(self,idx3d,item) 36 | } 37 | func remove(_ idx3d:[Int]){ 38 | TreeList3Modifier.remove(self, idx3d) 39 | } 40 | func append(_ idx3d:[Int],_ item:Tree){ 41 | TreeList3Modifier.append(self, idx3d, item) 42 | } 43 | var selectedIdx3d:[Int]? {return TreeList3Parser.selected(self)} 44 | var xml:XML {return self.treeDP.tree.xml} 45 | var tree:Tree {return self.treeDP.tree} 46 | var hashList:[[Int]] {return self.treeDP.hashList} 47 | /** 48 | * Returns prop value for idx3d and key 49 | */ 50 | subscript(idx3d:[Int],key:String) -> String? { 51 | return TreeDPParser.getProp(self.treeDP, idx3d, key) 52 | } 53 | /** 54 | * New 55 | */ 56 | subscript(idx3d:[Int])->Tree?{ 57 | return self.tree.child(idx3d) 58 | } 59 | /** 60 | * New 61 | */ 62 | func hasChildren(_ idx3d:[Int])-> Bool{ 63 | return TreeList3Asserter.hasChildren(self,idx3d) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/TreeListable3.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | protocol TreeListable3:FastListable3 {//possibly rename to TreeListKind 5 | var treeDP:TreeDP {get} 6 | } 7 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/dp/TreeDP+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | extension TreeDP { 5 | /** 6 | * Returns idx3d for idx2d 7 | */ 8 | subscript(_ idx2d:Int) -> [Int] { 9 | return hashList[idx2d] 10 | } 11 | /** 12 | * Returns idx2d for idx3d 13 | */ 14 | subscript(_ idx3d:[Int]) -> Int? { 15 | return hashList.index(where: {$0 == idx3d}) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/dp/TreeDP.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * TODO: Use DataProvidable don't extend DataProvider 5 | * NOTE: This would allow fast translation between 2d and 3d data structure https://github.com/CosmicMind/Algorithm/blob/master/Sources/SortedDictionary.swift 6 | * NOTE: for now we have a fast way to translate 2d -> 3d and slow 3d -> 2d. But the later doesn't need to be fast as it is only used when editing 3d structure. 2d -> 3d however needs to be super fast since its accessed very frequently when doing animation 7 | * TreeDP surogates item(at:Int) in DataProvider. You dont need to touch DataProvider.items 8 | */ 9 | class TreeDP:DataProvider { 10 | var tree:Tree 11 | var hashList:[[Int]]/*Stores the idx3d indecies*/ 12 | init(_ tree:Tree){ 13 | self.tree = tree 14 | self.hashList = TreeUtils.pathIndecies(tree,at:[],with:TreeUtils.isOpen)/*flattens 3d to 2d*/ 15 | super.init([]) 16 | } 17 | /** 18 | * PARAM: at: 2d idx 19 | * RETURNS: DataProvider item (aka Dictionary) 20 | */ 21 | override func item(_ at:Int) -> [String:String]?{ 22 | if let treeIdx:[Int] = hashList[safe:at], let child:Tree = tree[treeIdx]{/*find the 3d-idx*/ 23 | return child.props 24 | } 25 | return nil 26 | } 27 | override var count:Int{ 28 | return hashList.count//return tree.count 29 | } 30 | convenience init(_ fileURLStr:String){ 31 | let xml = FileParser.xml(fileURLStr) 32 | self.init(xml) 33 | } 34 | convenience init(_ xml:XML) { 35 | let tree:Tree = TreeConverter.tree(xml) 36 | self.init(tree) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/dp/TreeDPAsserter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class TreeDPAsserter { 4 | static func hasChildren(_ dp: TreeDP, _ idx2d:Int) -> Bool{ 5 | return hasChildren(dp,dp[idx2d]) 6 | } 7 | static func hasChildren(_ dp: TreeDP, _ idx3d:[Int]) -> Bool{ 8 | return TreeAsserter.hasChildren(dp.tree, idx3d) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/dp/TreeDPParser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class TreeDPParser { 5 | /** 6 | * Returns prop-value for idx2d and key 7 | * EXAMPLE: TreeDP2Parser.getProp(treeDP, idx, "isOpen") 8 | */ 9 | static func getProp(_ dp: TreeDP, _ idx2d:Int, _ key:String)->String?{ 10 | return getProps(dp, idx2d)?[key] 11 | } 12 | /** 13 | * Returns prop-value for idx3d and key 14 | */ 15 | static func getProp(_ dp: TreeDP, _ idx3:[Int], _ key:String)->String?{ 16 | return getProps(dp, idx3)?[key] 17 | } 18 | /** 19 | * Returns properties 20 | */ 21 | static func getProps(_ dp: TreeDP, _ idx2d:Int)->[String:String]?{ 22 | if let idx3d:[Int] = dp.hashList[safe:idx2d]{ 23 | return getProps(dp,idx3d) 24 | } 25 | fatalError("no item at: \(idx2d)") 26 | } 27 | static func getProps(_ dp: TreeDP, _ idx3d:[Int])->[String:String]?{ 28 | return dp.tree.getProps(idx3d) 29 | } 30 | /** 31 | * TODO: ⚠️️ You can probably chain indices as well 32 | * TODO: Use .filte instead 33 | */ 34 | static func values(_ dp: TreeDP, _ idx:[Int], _ key:String)->[String]{ 35 | var indecies:[[Int]] = TreeUtils.pathIndecies(dp.tree,at:idx,with:TreeUtils.isOpen)/*flattens 3d to 2d*/ 36 | //Swift.print("indecies: " + "\(indecies)") 37 | indecies = indecies.map{idx + $0}//prepend the parent pathIdx to get complete pathIndecies 38 | return indecies.lazy.map{ idx -> String? in 39 | guard let tree = dp.tree[idx],let props:[String:String] = tree.props,let value = props[key] else {return nil} 40 | return value 41 | }.flatMap{$0}/*removes nil*/ 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/handler/TreeListHandler.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class TreeListHandler:ElasticScrollerFastListHandler { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/tree/Tree.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | /** 3 | * NOTE: Check regexident forest code. and lorentey [Element] ? etc? 4 | * TODO: ⚠️️ maybe rearrange arguments to: name,content,props,children? 5 | * EXAMPLE: let tree = Tree("a",[Tree("a.1"),Tree("a.2",[Tree("a.2.1")])]); 6 | * EXAMPLE: TreeUtils.applyAll(tree:tree:,idx3d:[1],{$0.name = $0.name.capitalize}); 7 | * EXAMPLE: TreeParser.child(tree,[1,0]) //A.2.1 8 | */ 9 | //typealias Element = T 10 | struct Tree{ 11 | var children:[Tree]//should be optional, then we would have to init it too often maybe, maybe lazy actually? 12 | var props:[String:String]? 13 | var name:String? 14 | var content:String?/*Could be Generic as well*/ 15 | init(_ name:String? = nil,_ children:[Tree] = [],_ content:String? = nil, _ props:[String:String]? = nil ) { 16 | self.children = children 17 | self.props = props 18 | self.name = name 19 | self.content = content 20 | } 21 | } 22 | /* 23 | Terminologies used in Trees: 24 | 25 | Root – The top node in a tree. 26 | Child – A node directly connected to another node when moving away from the Root. 27 | Parent – The converse notion of a child. 28 | Siblings – Nodes with the same parent. 29 | Descendant – A node reachable by repeated proceeding from parent to child. 30 | Ancestor – A node reachable by repeated proceeding from child to parent. 31 | Leaf – A node with no children. 32 | Internal node – A node with at least one child 33 | External node – A node with no children. 34 | Degree – Number of sub trees of a node. 35 | Edge – Connection between one node to another. 36 | Path – A sequence of nodes and edges connecting a node with a descendant. 37 | Level – The level of a node is defined by 1 + (the number of connections between the node and the root). 38 | Height of node – The height of a node is the number of edges on the longest path between that node and a leaf. 39 | Height of tree – The height of a tree is the height of its root node. 40 | Depth – The depth of a node is the number of edges from the node to the tree's root node. 41 | Forest – A forest is a set of n ≥ 0 disjoint trees. 42 | */ 43 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/tree/utils/HashList2Modifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class HashList2Modifier { 5 | /** 6 | * Inserts descendants into the 2d list 7 | */ 8 | static func addDescendants(_ list:inout [[Int]], _ idx2d:Int, _ idx3d:[Int], _ tree:Tree) -> Int{ 9 | //Swift.print("idx3d: " + "\(idx3d)") 10 | //you want all open descendants at 3dIdx 11 | var indecies:[[Int]] = TreeUtils.pathIndecies(tree,at:idx3d,with:TreeUtils.isOpen)/*flattens 3d to 2d*/ 12 | indecies = indecies.map{idx3d + $0}//prepend the parent pathIdx to get complete pathIndecies 13 | //Swift.print("idx: " + "\(idx)") 14 | //Swift.print("indecies: " + "\(indecies)") 15 | let indexAfter:Int = idx2d+1/*we need to inser after idx*/ 16 | list.insert(contentsOf: indecies, at: indexAfter) 17 | return indecies.count 18 | } 19 | /** 20 | * Remove descendants into the 2d list 21 | */ 22 | static func removeDescendants(_ list:inout [[Int]],_ idx2d:Int,_ idx3d:[Int], _ tree:Tree) -> Int{ 23 | //Swift.print("🍐 removeDescendants") 24 | //Swift.print("at: " + "\(at)") 25 | //Swift.print("list: " + "\(list)") 26 | //Swift.print("idx3d: " + "\(idx3d)") 27 | let child:Tree = tree[idx3d]! 28 | //Swift.print("child.children.count: " + "\(child.children.count)") 29 | let count:Int = child.count(TreeUtils.isOpen)/*recursive count that traverses the hierarchy*/ 30 | //Swift.print("removeDescendants.count: " + "\(count)") 31 | let end:Int = idx2d + count 32 | Swift.print("end: " + "\(end)") 33 | _ = ArrayModifier.removeRange(&list, idx2d + 1,end + 1) 34 | return count 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/tree/utils/TreeAsserter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class TreeAsserter { 4 | /** 5 | * New 6 | * idx3d 7 | */ 8 | static func hasAttribute(_ tree:Tree, _ idx3d:[Int], _ key:String) -> Bool{ 9 | return tree.getProps(idx3d)?[key] != nil 10 | } 11 | /** 12 | * Asserts if a tree at PARAM: idx3d has children 13 | */ 14 | static func hasChildren(_ tree:Tree,_ idx3d:[Int])->Bool{ 15 | if let child:Tree = tree[idx3d]{ 16 | return !child.children.isEmpty 17 | } 18 | fatalError("no child at: \(idx3d)") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/ui/treelist/utils/tree/utils/TreeKind.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol TreeKind { 4 | //associatedtype Element 5 | var children:[TreeKind] {get} 6 | var props:[String:String]? {get} 7 | var name:String? {get} 8 | var content:String? {get}//or use Any or T 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/Element/ui/window/view/PopupView.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * NOTE: the difference between local and global monitors is that local takes care of events that happen inside an app window, and the global handle events that also is detected outside an app window. 5 | * TODO: add support for NSNotification: windowDidResignKey -> close the popup window when this event happens 6 | */ 7 | class PopupView:WindowView{ 8 | var leftMouseDownEventListener:Any? 9 | override func resolveSkin() { 10 | //Swift.print("PopupView.resolveSkin") 11 | StyleManager.addStyle("Window#special{fill:green;}") 12 | super.resolveSkin() 13 | if(leftMouseDownEventListener == nil) {leftMouseDownEventListener = NSEvent.addLocalMonitorForEvents(matching:[.leftMouseDragged], handler:self.onMouseDown ) }//we add a global mouse move event listener 14 | else {fatalError("This shouldn't be possible, if it throws this error then you need to remove he eventListener before you add it")} 15 | } 16 | func onMouseDown(event:NSEvent) -> NSEvent? { 17 | //Swift.print("PopupView.onMouseDown()") 18 | //Swift.print("self.localPos: " + "\(self.localPos())") 19 | if(!CGRect(CGPoint(),frame.size).contains(self.localPos())){/*click outside window, but must hit another app window*/ 20 | super.onEvent(Event(Event.update,self))/*notifies the initiator of the PopupWin that it will close*/ 21 | if(leftMouseDownEventListener != nil){ 22 | NSEvent.removeMonitor(leftMouseDownEventListener!) 23 | leftMouseDownEventListener = nil 24 | } 25 | //TODO: Set the event to it self again here 26 | self.window!.close() 27 | } 28 | return event 29 | } 30 | } 31 | /* 32 | 33 | Implement this if you want the popupwin to be closed if the app looses focus: 34 | 35 | - (void)setupWindowForEvents{ 36 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignMainNotification object:self]; 37 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:self]; 38 | } 39 | 40 | -(void)windowDidResignKey:(NSNotification *)note { 41 | NSLog(@"notification"); 42 | [self close]; 43 | } 44 | 45 | */ 46 | -------------------------------------------------------------------------------- /src/Element/ui/window/view/TitleView.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * NOTE: the titleView has a working model for centering things in css 5 | * NOTE: the title must be set after the init of the Window instance 6 | */ 7 | class TitleView:CustomView{ 8 | lazy var textArea:TextArea = {self.addSubView(TextArea(NaN,24,"Title goes here",self,"winTitle"))}() 9 | var leftMouseDraggedEventListener:Any? 10 | var mouseDownPos:CGPoint?/*Store the mouseDown offset, when moving the window*/ 11 | // override init(_ width:CGFloat, _ height:CGFloat, _ parent:ElementKind? = nil, _ id:String? = "") { 12 | // super.init(size:CGSize(width,height),id:id) 13 | // } 14 | override func resolveSkin() { 15 | super.resolveSkin() 16 | textArea.text.isInteractive = false/*Disable interactivity on the text*/ 17 | } 18 | override func mouseDown(_ event:MouseEvent) { 19 | if(event.immediate === textArea){ 20 | mouseDownPos = self.winMousePos 21 | if(leftMouseDraggedEventListener == nil) {leftMouseDraggedEventListener = NSEvent.addLocalMonitorForEvents(matching:[.leftMouseDragged], handler:self.onMove ) }//we add a global mouse move event listener 22 | else {fatalError("This shouldn't be possible, if it throws this error then you need to remove he eventListener before you add it")} 23 | } 24 | } 25 | override func mouseUp(_ event:MouseEvent) { 26 | if(leftMouseDraggedEventListener != nil){ 27 | NSEvent.removeMonitor(leftMouseDraggedEventListener!) 28 | leftMouseDraggedEventListener = nil//<--This part may not be needed, seems to be needed 29 | } 30 | } 31 | func onMove(event:NSEvent) -> NSEvent? { 32 | let winPos:CGPoint = self.window!.unFlipScreenPosition(self.window!.flippedScreenPosition - mouseDownPos!) 33 | WinModifier.position(self.window!, winPos) 34 | return event 35 | } 36 | required init(coder:NSCoder) {fatalError("init(coder:) has not been implemented")} 37 | } 38 | -------------------------------------------------------------------------------- /src/Element/ui/window/view/WindowView.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | /** 3 | * NOTE: WindowView needs the interactiveView so that Element has, so that events can propegate to it 4 | * TODO: WindowView should have a background element to target. check legacy code to confirm theory 5 | */ 6 | class WindowView:Element{ 7 | /** 8 | * Draws the graphics 9 | */ 10 | override func resolveSkin() { 11 | super.resolveSkin() 12 | } 13 | /** 14 | * Returns the class type of the Class instance 15 | */ 16 | override func getClassType()->String{ 17 | return "\(Window.self)"/*Window can be targeted via the id so we use Window for all Window subclasses, although this can be overriden in said subclasses*/ 18 | } 19 | override func setSize(_ width: CGFloat, _ height: CGFloat) { 20 | super.setSize(width, height) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Element/utils/Container.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Cocoa 3 | @testable import Utils 4 | /** 5 | * IMPORTANT: ⚠️️ Container does not add the skin to the stage, Use Section if you need a skin added to 6 | * TODO: Rename to Div,Division,Section,Segment? or? Div sounds best and is closley related to css, too closley reletate. Container is a good name! 7 | * IMPRTANT: ⚠️️ May have probs with interactions like scrollWhell. use Section instead, which has a background 8 | */ 9 | class Container:Element{ 10 | override func resolveSkin() { 11 | // Swift.print("Container.b") 12 | super.resolveSkin() 13 | (skin as! NSView).isHidden = true 14 | // Swift.print("Container.a") 15 | //skin = SkinResolver.skin(self)/*We still need to generate the skin, why? I can't recall*/ 16 | } 17 | /** 18 | * New 19 | * Temp solution, until we figure out a better way to toggle major direction etc. revoced ⚠️️ 20 | */ 21 | func layerPos(_ val:CGFloat,_ dir:Dir){ 22 | // if dir == .ver { 23 | self.layer?.position[dir] = val 24 | // } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Element/utils/ElementExtenions.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | extension Element{ 5 | /** 6 | * Convenience 7 | */ 8 | convenience init(_ width: CGFloat, _ height: CGFloat, _ isDisabled:Bool, _ isFocused:Bool = false, _ parent:ElementKind? = nil,_ id:String? = nil){ 9 | self.init(width,height,parent,id) 10 | self.isDisabled = isDisabled 11 | self.isFocused = isFocused 12 | } 13 | /** 14 | * Convenience 15 | */ 16 | convenience init(_ width: CGFloat , _ height: CGFloat , _ x:CGFloat , _ y:CGFloat , _ parent:ElementKind? = nil,_ id:String? = nil){ 17 | self.init(width,height,parent,id) 18 | setPosition(CGPoint(x,y)) 19 | } 20 | /** 21 | * New 22 | * NOTE: check out UnfoldParser.retrieveUnFoldable for hirarchical version of this method 23 | */ 24 | func element(_ id:String,_ type:T.Type? = nil) -> T?{ 25 | return ElementParser.element(self, id, type) 26 | } 27 | } 28 | extension Event{//TODO: ⚠️️ rename to Element+Event.swift 29 | /** 30 | * new 31 | */ 32 | func assert(_ type:String, id:String) -> Bool{ 33 | return self.type == type && (self.origin as? ElementKind)?.id == id 34 | } 35 | /** 36 | * new 37 | */ 38 | func assert(_ type:String, parentID:String) -> Bool{ 39 | return self.type == type && assert(parentID:parentID) 40 | } 41 | /** 42 | * New 43 | */ 44 | func assert(parentID:String) -> Bool{ 45 | return isChildOf(parentID:parentID) 46 | } 47 | /** 48 | * New 49 | * is origin child of a parent with ID == parentID 50 | */ 51 | func isChildOf(parentID:String) -> Bool{ 52 | let matchMethod:NSViewAsserter.MatchMethod = {(a,_) in 53 | guard let elementParent = (a as? ElementKind) else {return false} 54 | return elementParent.id == parentID 55 | } 56 | return NSViewAsserter.hasParent(self.origin as? NSView, nil,matchMethod:matchMethod) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Element/utils/ElementParser.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class ElementParser{ 5 | /** 6 | * Returns all children in PARAM: element that is of type IElement 7 | * NOTE: If this doesnt work just use the array casting technique with the NSParser.children method 8 | */ 9 | static func children(_ view:NSView,_ type:T.Type? = nil)->[T] { 10 | return NSViewParser.childrenOfType(view, type) 11 | } 12 | /** 13 | * Returns an Array instance comprised of Selector instances for each (element,classId,id and state) in the element "cascade" (the spesseficity) 14 | * NOTE: To get the stackString use: ElementParser.stackString(button) and StyleParser.describe(StyleResolver.style(button)) 15 | */ 16 | static func selectors(_ element:ElementKind)->[SelectorKind]{ 17 | let elements:[ElementKind] = parents(element) + [element] 18 | return elements.map{selector($0)} 19 | } 20 | static func selector(_ element:ElementKind)->SelectorKind{ 21 | let elmnt:String = element.getClassType() 22 | //if(e.classId != null) selector.classIds = e.classId.indexOf(" ") != -1 ? e.classId.split(" ") : [e.classId] 23 | let id:String = element.id ?? "" 24 | let states:[String] = (element.skin?.state ?? element.skinState).split(" ")/*match("\\b\\w+\\b") Matches words with spaces between them*/ 25 | return Selector(elmnt,[],id,states) 26 | } 27 | /** 28 | * Returns an array populated with IElement parents of the target (Basically the ancestry) 29 | * NOTE: loops up hierarachy 30 | */ 31 | static func parents(_ element:ElementKind)->[ElementKind] { 32 | var parents:[ElementKind] = [] 33 | var parent:ElementKind? = (element as? NSView)?.superview as? ElementKind// :TODO: seperate this into a check if its DO then that, if its Window then do that 34 | while parent != nil {/*loops up the object hierarchy as long as the parent is a Element supertype*/ 35 | parents.append(parent!) 36 | parent = (parent as? NSView)?.superview as? ElementKind 37 | } 38 | return parents.reversed() 39 | } 40 | /** 41 | * This method can be used to print the StyleSelector for an Element instance 42 | * Returns the absolute ancestry as a space delimited string in this format: elementId:classIds#id:states 43 | * TODO: ⚠️️ returns an trailing : could be a bug? 44 | */ 45 | static func stackString(_ element:ElementKind)->String{ 46 | return SelectorParser.string(selectors(element)) 47 | } 48 | /** 49 | * New 50 | */ 51 | static func element(_ parent:NSView, _ id:String, _ type:T.Type? = nil) -> T?{ 52 | return parent.subviews.lazy.flatMap{$0 as? T}.first(where: {$0.id! == id}) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Element/utils/Section.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * NOTE: Unlike Container, section can have a style applied 5 | * NOTE: you dont need anything in this method actually, the getClassType works if Section is the last SubClass in the subclass hierarchy 6 | * TODO: ⚠️️ make this as an typealias instead 7 | */ 8 | class Section:Element {} 9 | -------------------------------------------------------------------------------- /src/Element/utils/check/CheckModifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class CheckModifier { 5 | /** 6 | * UnChecks all in PARAM: items exept PARAM: target 7 | */ 8 | static func unCheckAllExcept(_ exceptionItem:Checkable, _ checkables:[Checkable]) {// :TODO: refactor this function// :TODO: rename to unSelectAllExcept 9 | checkables.forEach { if($0 !== exceptionItem && $0.getChecked()) { $0.setChecked(false)} } 10 | } 11 | /** 12 | * Removes the RadioButton passed through the PARAM: radioButton 13 | */ 14 | static func removeCheckable(_ checkables:inout [Checkable], _ item:Checkable)->Checkable? { 15 | if let i = checkables.index(where: {$0 === item}){ 16 | return checkables.splice2(i, 1)[0]// :TODO: dispatch something? 17 | } 18 | return nil 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Element/utils/check/CheckParser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class CheckParser { 5 | /** 6 | * Returns an ICheckable at a spessific index 7 | */ 8 | func getCheckableAt(_ checkables:[Checkable],_ index:Int)->Checkable? {// :TODO: consider moving in to util class or just write it up as a note 9 | return checkables[safe:index] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Element/utils/group/CheckGroup.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /* 4 | * NOTE: remeber to add the group to the view or else the eventBubbling may make errors in other components 5 | * NOTE: this class would be more logical if it extended EventSender but it extends View so that the event bubbling on the ICheckable objects works 6 | * NOTE: the constructor checked parameter is just a reference no action is applied to that checked item. 7 | * TODO: In the future make a MultipleSelectionCheckGroup that can select many icheckable items with the use of shift key for instance (do not add this functionality in this class its not the correct abstraction level) 8 | * TODO: fix the bubbling stuff this should need to be added to the view or be a sprite. 9 | */ 10 | class CheckGroup:EventSender { 11 | var checkables:[Checkable] = [] 12 | var checked:Checkable? 13 | init(_ checkables:[Checkable], _ checked:Checkable? = nil){ 14 | super.init() 15 | addCheckables(checkables) 16 | self.checked = checked! 17 | } 18 | func addCheckables(_ checkables:[Checkable]) { 19 | for checkable:Checkable in checkables{ addCheckable(checkable)} 20 | } 21 | /** 22 | * NOTE: Use a weak ref so that we don't have to remove the event if the selectable is removed from the SelectGroup or view 23 | */ 24 | func addCheckable(_ checkable:Checkable) { 25 | if(checkable is EventSendable){(checkable as! EventSendable).event = onEvent} 26 | checkables.append(checkable); 27 | } 28 | override func onEvent(_ event:Event) {// :TODO: make protected see SelectGroup 29 | if(event.type == CheckEvent.check){ 30 | //Swift.print("CheckGroup.onEvent() immediate: " + "\(event.immediate)" + " type: " + "\(event.type)") 31 | self.event(CheckGroupEvent(CheckGroupEvent.check,checked,self)) 32 | checked = event.immediate as? Checkable 33 | CheckModifier.unCheckAllExcept(checked!, checkables) 34 | super.onEvent(CheckGroupEvent(CheckGroupEvent.change,checked,self)) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Element/utils/group/SelectGroup.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | /** 4 | * NOTE: this class also works great with RadioBullets 5 | * NOTE: Remember to add the selectGroup instance to the view so that the event works correctly // :TODO: this is a bug try to fix it 6 | * NOTE: Use the SelectGroupModifier and SelectGroupParser for Modifing and parsing the SelectGroup 7 | * TODO: ⚠️️ You could add a SelectGroupExtension class that adds Modifing and parsing methods to the SelectGroup instance! 8 | * EXAMPLE: See BasicView in Explorer 9 | */ 10 | class SelectGroup:EventSender{ 11 | var selectables:[Selectable] = [] 12 | var selected:Selectable? 13 | init(_ selectables:[Selectable], _ selected:Selectable? = nil){ 14 | super.init() 15 | self.selected = selected 16 | addSelectables(selectables) 17 | } 18 | func addSelectables(_ selectables:[Selectable]){ 19 | selectables.forEach{addSelectable($0)} 20 | } 21 | /** 22 | * NOTE: use a weak ref so that we dont have to remove the event if the selectable is removed from the SelectGroup or view 23 | */ 24 | func addSelectable(_ selectable:Selectable) { 25 | if(selectable is EventSendable){ (selectable as! EventSendable).event = onEvent } 26 | selectables.append(selectable) 27 | } 28 | override func onEvent(_ event:Event){ 29 | if(event.type == SelectEvent.select){ 30 | self.event(SelectGroupEvent(SelectGroupEvent.select,selected,self)) 31 | selected = event.immediate as? Selectable 32 | SelectModifier.unSelectAllExcept(selected!, selectables) 33 | super.onEvent(SelectGroupEvent(SelectGroupEvent.change,selected,self)) 34 | } 35 | super.onEvent(event)/*We don't want to block any event being passed through, so we forward all events right through the SelectGroup*/ 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Element/utils/group/event/CheckGroupEvent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * 5 | */ 6 | class CheckGroupEvent:Event { 7 | static var change:String = "checkGroupChange"/*this event is dispatched after the checked variable is set in the CheckGroup instance*/// 8 | static var check:String = "checkGroupCheck"/*This event is dispatched before the checked variable is set in the CheckGroup instance*/ 9 | var checked:Checkable? 10 | init(_ type:String, _ checked:Checkable? = nil,_ origin:AnyObject) { 11 | self.checked = checked 12 | super.init(type,origin) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Element/utils/group/event/SelectGroupEvent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | /** 4 | * NOTE: It would be convenient to make an extension that could return the selected 5 | */ 6 | class SelectGroupEvent:Event { 7 | static var change:String = "selectGroupChange"/*this event is dispatched after the _selected variable is set in the SelectGroup instance*/// :TODO: possibly rename to SELECT_GROUP_SELECTED 8 | static var select:String = "selectGroupSelect"/*This event is dispatched before the _selected variable is set in the SelectGroup instance*/ 9 | //static var deSelect : String = "selectGroupDeSelect" 10 | var selectable:Selectable?//TODO: rename to selected 11 | init(_ type: String, _ selectable:Selectable? = nil, _ origin:AnyObject) { 12 | self.selectable = selectable 13 | super.init(type,origin) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Element/utils/group/utils/SelectGroupExtensions.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | extension SelectGroup { 5 | /*Parsing*/ 6 | func getSelected() -> Selectable? {return SelectParser.selected(self.selectables)}/*Convenience*/ 7 | func index(_ selectable:Selectable)->Int{return SelectGroupParser.index(self, selectable)}/*Convenience*/ 8 | var indexOfSelected:Int{return SelectGroupParser.indexOfSelected(self)}/*Convenience*/ 9 | func selectableAt(_ index:Int)->Selectable?{return SelectGroupParser.selectableAt(self, index)}/*Convenience*/ 10 | /*Modifiers*/ 11 | func selectedAt(_ index:Int){SelectGroupModifier.select(self, index)}/*Convenience*///TODO: Rename to select? 12 | func select(_ selectable:Selectable){SelectGroupModifier.select(self, selectable)}/*Convenience*/ 13 | func removeSelectable(_ item:Selectable)->Selectable? {return SelectGroupModifier.removeSelectable(self, item)}/*Convenience*/ 14 | } 15 | -------------------------------------------------------------------------------- /src/Element/utils/group/utils/SelectGroupModifier.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class SelectGroupModifier { 5 | /** 6 | * 7 | */ 8 | static func select(_ selectGroup:SelectGroup,_ index:Int){// :TODO: move this to the SlectUtils since you mihgt not always want to unselectAllExcept when you select, and you may want to impose different functionality, like multi select etc 9 | let selectable:Selectable = SelectGroupParser.selectableAt(selectGroup,index)! 10 | selectable.setSelected(true) 11 | selectGroup.selected = selectable 12 | SelectModifier.unSelectAllExcept(selectable, selectGroup.selectables) 13 | } 14 | /** 15 | * 16 | */ 17 | static func select(_ selectGroup:SelectGroup,_ selectable:Selectable) { 18 | selectable.setSelected(true) 19 | selectGroup.selected = selectable 20 | SelectModifier.unSelectAllExcept(selectable, selectGroup.selectables) 21 | } 22 | /** 23 | * Removes the RadioButton passed through the PARAM: radioButton 24 | */ 25 | static func removeSelectable(_ selectGroup:SelectGroup,_ item:Selectable)->Selectable? { 26 | if let i:Int = selectGroup.selectables.index(where: {$0 === item}){ 27 | return selectGroup.selectables.splice2(i,1)[0]// :TODO: dispatch something? 28 | } 29 | return nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Element/utils/group/utils/SelectGroupParser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Utils 3 | 4 | class SelectGroupParser { 5 | /** 6 | * Returns the selected ISelectable instance in PARAM: selectGroup 7 | */ 8 | static func selected(_ selectGroup:SelectGroup)->Selectable? { 9 | return SelectParser.selected(selectGroup.selectables) 10 | } 11 | /** 12 | * Returns the index of PARAM: selectable in _selectables 13 | */ 14 | static func index(_ selectGroup:SelectGroup,_ selectable:Selectable)->Int { 15 | return selectGroup.selectables.index(where: {$0 === selectable}) ?? -1 16 | } 17 | /** 18 | * Returns the index of a selected ISelectable in PARAM: selectGroup 19 | */ 20 | static func indexOfSelected(_ selectGroup:SelectGroup)->Int { 21 | if let selected:Selectable = SelectGroupParser.selected(selectGroup){ 22 | return index(selectGroup, selected) 23 | } 24 | return -1 25 | } 26 | /** 27 | * Returns an RadioButton at a spessific index 28 | */ 29 | static func selectableAt(_ selectGroup:SelectGroup,_ index:Int)->Selectable? { 30 | if(index <= selectGroup.selectables.count) {return selectGroup.selectables[index]} 31 | Swift.print("no ISelectable at the index of " + "\(index)") 32 | return nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Element/utils/select/SelectAsserter.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class SelectAsserter { 4 | static func hasSelected(_ view:NSView) -> Bool { 5 | return SelectParser.selected(view) != nil 6 | } 7 | static func hasSelected(_ selectables:[Selectable]) -> Bool { 8 | return SelectParser.selected(selectables) != nil 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Element/utils/select/SelectModifier.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SelectModifier { 5 | /** 6 | * Unselects all in PARAM: items except PARAM: target 7 | */ 8 | static func unSelectAllExcept(_ exceptionItem:Selectable, _ selectables:[Selectable]) {// :TODO: refactor this function// :TODO: rename to unSelectAllExcept 9 | selectables.forEach{ 10 | if $0 !== exceptionItem && $0.getSelected() { $0.setSelected(false) } 11 | } 12 | } 13 | static func selectAll(_ selectables:[Selectable],_ isSelected:Bool = true) { 14 | selectables.forEach{$0.setSelected(isSelected)} 15 | } 16 | /** 17 | * TODO: I think this is more complicated than it needs to be. Try to clean it up 18 | */ 19 | static func unSelectAllExceptThese(_ selectables:[Selectable], exceptions:[Selectable]) { 20 | let unSelectedItems:[Selectable] = ArrayParser.difference(selectables, exceptions) 21 | unSelectedItems.forEach {if($0.getSelected()){$0.setSelected(false)}}//deselect the selected 22 | exceptions.forEach {if(!$0.getSelected()) {$0.setSelected(true)}} 23 | } 24 | /** 25 | * Selects all selectables within a range (from, to) 26 | * // :TODO: use the Range class here!?!? 27 | */ 28 | static func selectRange(_ selectables:[Selectable], _ from:Int, _ to:Int,_ isSelected:Bool = true) { 29 | for i in from...to{ 30 | let selectable:Selectable = selectables[i] 31 | if(!selectable.getSelected()) { selectable.setSelected(isSelected) } 32 | } 33 | } 34 | } 35 | extension SelectModifier{ 36 | /** 37 | * Supports NSView 38 | */ 39 | static func unSelectAllExcept(_ exceptionItem:Selectable, _ view:NSView){ 40 | let selectables:[Selectable] = SelectParser.selectables(view) 41 | unSelectAllExcept(exceptionItem,selectables) 42 | } 43 | static func selectAll(_ view:NSView,_ isSelected:Bool = true) { 44 | view.subviews.filter() {$0 is Selectable}.forEach{($0 as! Selectable).setSelected(isSelected)} 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Element/utils/select/SelectParser.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | @testable import Utils 3 | 4 | class SelectParser { 5 | /** 6 | * Returns the first selected ISelectable in an array of an NSView with ISelectables (returns nil if it doesn't exist) 7 | * TODO: Rename to firstSelected 8 | */ 9 | static func selected(_ view:NSView) -> Selectable? { 10 | let selectables:[Selectable] = self.selectables(view) 11 | return selected(selectables) 12 | } 13 | /** 14 | * Returns the first selected ISelectable in an array of ISelectables or a 15 | * TODO: Rename to firstSelected 16 | */ 17 | static func selected(_ selectables:[Selectable]) -> Selectable? { 18 | return selectables.first(where: {$0.getSelected()}) 19 | } 20 | /** 21 | * Returns an array from every child that is an ISelectable in PARAM: displayObjectContainer 22 | */ 23 | static func selectables(_ view:NSView)->[Selectable] { 24 | return NSViewParser.childrenOfType(view, Selectable.self) 25 | } 26 | /** 27 | * Returns all selectables that are selected 28 | */ 29 | static func allSelected(_ selectables:[Selectable])->[Selectable] { 30 | return selectables.filter(){$0.getSelected()} 31 | } 32 | /** 33 | * Returns the index of the first selected ISelectable instance in a NSView instance (returns -1 if non is found) 34 | * TODO: make a similar method for Array instead of NSView 35 | * NOTE: you could return nil instead of -1 36 | */ 37 | static func index(_ view:NSView) -> Int{ 38 | return view.subviews.filter() {$0 as? Selectable != nil}.index(where: {($0 as! Selectable).selected}) ?? -1 39 | } 40 | } 41 | --------------------------------------------------------------------------------