├── 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 |    [](https://github.com/apple/swift-package-manager) [](https://codebeat.co/projects/github-com-eonist-element)
2 |
3 |
4 |
5 | ### Description:
6 | Programmatic UI Framework for macOS. Swift handles app logic, CSS/SVG handles design and JSON handles struture.
7 |
8 |
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 |
46 |
47 | ## More...
48 | More about Element 👉 [wiki](https://github.com/eonist/Element/wiki)
49 |
50 | ## Sponsors:
51 | [
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 |
--------------------------------------------------------------------------------