· 9 years ago · Oct 05, 2016, 02:20 AM
1local PROJECT_NAME = "gloo"
2
3if _G[PROJECT_NAME] then return end
4
5---- SETTINGS
6local ENTRY_SIZE = 17 -- default size of rows, scrollbars, etc
7local WEAK_TABLES = false -- whether weak tables are enabled (causes problems under certain circumstances)
8----/SETTINGS
9
10local lib = {}
11local doc = {}
12local version = "0.11"
13
14doc["Version"] = [==[
15Version ( ) [function]
16 returns: string `version`
17
18Returns the current version of the library (currently ]==]..version..[==[).
19]==]
20
21function lib.Version()
22 return version
23end
24
25doc["NULL"] = [==[
26NULL [const]
27
28Represents a "non-nil nil value". Used with tables that need a way to declare an entry as nil while still being defined in the table.
29]==]
30
31local NULL = {}
32lib.NULL = NULL
33
34local SORT = {
35 NONE = 0;
36 ASCENDING = 1;
37 DESCENDING = 2;
38}
39lib.SORT = SORT
40
41local WEAK_MODE = {
42 K = {__mode="k"};
43 V = {__mode="v"};
44 KV = {__mode="kv"};
45}
46
47local function GetIndex(table,value)
48 for i,v in pairs(table) do
49 if v == value then
50 return i
51 end
52 end
53end
54
55local function ClampIndex(table,index)
56 local max = #table
57 index = math.floor(index)
58 return index < 1 and 1 or index > max and max or index
59end
60
61local function Create(ty)
62 return function(data)
63 local obj = Instance.new(ty)
64 for k, v in pairs(data) do
65 if type(k) == 'number' then
66 v.Parent = obj
67 else
68 obj[k] = v
69 end
70 end
71 return obj
72 end
73end
74
75local function Modify(obj)
76 return function(data)
77 for k, v in pairs(data) do
78 if type(k) == 'number' then
79 v.Parent = obj
80 else
81 obj[k] = v
82 end
83 end
84 return obj
85 end
86end
87
88--[[
89local ENUM_MT = {
90 __tostring = function(e)
91 return "enum: "..e.__type
92 end;
93}
94
95local function CastToEnum(value,enum)
96 if value ~= '__type' then
97 for k,e in pairs(enum) do
98 if k == value then
99 return e
100 end
101 end
102 end
103 error('cannot cast `'..tostring(value)..'` to enum '..enum.__type,3)
104end
105--]]
106
107--[[DEPEND:]]
108
109doc["SetZIndex"] = [==[
110SetZIndex ( Instance `object`, number `zindex` ) [function]
111 returns: (nothing)
112
113Sets the ZIndex of `object`, then calls SetZIndex on every child of `object`.
114
115Arguments:
116 `object`
117 The instance to set the ZIndex of.
118 `zindex`
119 The ZIndex to set the object to.
120]==]
121
122doc["SetZIndexOnChanged"] = [==[
123SetZIndexOnChanged ( Instance `object` )
124 returns: RBXScriptConnection `connection`
125
126Sets an object to call SetZIndex whenever its ZIndex changes.
127
128Arguments:
129 `object`
130 The instance to set.
131
132Returns:
133 `connection`
134 The resulting event connection.
135]==]
136
137local ZIndexLock = {}
138local function SetZIndex(object,z)
139 if not ZIndexLock[object] then
140 ZIndexLock[object] = true
141 object.ZIndex = z
142 for _,child in pairs(object:GetChildren()) do
143 SetZIndex(child,z)
144 end
145 ZIndexLock[object] = nil
146 end
147end
148
149local function SetZIndexOnChanged(object)
150 return object.Changed:connect(function(p)
151 if p == "ZIndex" then
152 SetZIndex(object,object.ZIndex)
153 end
154 end)
155end
156
157lib.SetZIndex = SetZIndex
158lib.SetZIndexOnChanged = SetZIndexOnChanged
159
160--[[DEPEND:]]
161
162doc["GetScreen"] = [==[
163GetScreen ( Instance `object` ) [function]
164 returns ScreenGui `screen`
165
166Gets the nearest ascending ScreenGui of `object`.
167Returns `object` if it is a ScreenGui.
168Returns nil if `object` isn't the descendant of a ScreenGui.
169
170Arguments:
171 `object`
172 The instance to get the ascending ScreenGui from.
173
174Returns:
175 `screen`
176 The ascending screen.
177 Will be nil if `object` isn't the descendant of a ScreenGui.
178]==]
179
180local function GetScreen(object)
181 local screen = object
182 while not screen:IsA("ScreenGui") do
183 screen = screen.Parent
184 if screen == nil then return nil end
185 end
186 return screen
187end
188
189lib.GetScreen = GetScreen
190
191--[[DEPEND:]]
192
193doc["GetPadding"] = [==[
194GetPadding ( GuiObject `object` ) [function]
195 returns: number `padding`
196
197Gets the padding amount for a Frame or GuiButton that has its Style property set.
198
199Arguments:
200 `object`
201 The Frame or GuiButton to get he padding from.
202
203Returns:
204 `padding`
205 The padding amount of `object`
206]==]
207
208local function GetPadding(object)
209 local base_size = 0
210 local base_pad = 0
211 if object:IsA"Frame" then
212 if object.Style == Enum.FrameStyle.ChatBlue
213 or object.Style == Enum.FrameStyle.ChatGreen
214 or object.Style == Enum.FrameStyle.ChatRed then
215 base_size = 60
216 base_pad = 17
217 elseif object.Style == Enum.FrameStyle.RobloxSquare
218 or object.Style == Enum.FrameStyle.RobloxRound then
219 base_size = 21
220 base_pad = 8
221 else
222 return 0
223 end
224 elseif object:IsA"GuiButton" then
225 if object.Style == Enum.ButtonStyle.RobloxButtonDefault
226 or object.Style == Enum.ButtonStyle.RobloxButton then
227 base_size = 36
228 base_pad = 12
229 else
230 return 0
231 end
232 else
233 return 0
234 end
235 local size = math.min(object.AbsoluteSize.x,object.AbsoluteSize.y)
236 if size < base_size then
237 return size/base_size*base_pad
238 else
239 return base_pad
240 end
241end
242
243lib.GetPadding = GetPadding
244
245--[[DEPEND:]]
246
247doc["Sprite"] = [==[
248Sprite ( Content `sprite_map`, GuiObject `sprite_frame`, Vector2, `sprite_size`, Vector2 `sprite_map_size`, bool `fix_blur` ) [constructor]
249 returns: Sprite `object`, GuiObject `sprite_frame`
250
251Creates a sprite from a sprite map (an image that holds smaller "sub-images").
252
253Arguments:
254 `sprite_map`
255 The image to use as the sprite map.
256
257 `frame`
258 The object that will contain the sprite image.
259 Optional; defaults to a new Frame
260
261 `sprite_size`
262 The dimensions of one sprite on the sprite map.
263 Optional; defaults to [32, 32]
264
265 `sprite_map_size`
266 The dimensions of the sprite map.
267 Optional; defaults to [256, 256]
268
269 `fix_blur`
270 Indicates whether image blurriness should be fixed.
271 Blurriness occurs because GUIs are offset by half a pixel, causing images to render "in-between" pixels.
272 This can be fixed by using Scaled position to offset the image by 0.5 pixels.
273 Optional; defaults to true
274
275Returns:
276 `object`
277 The Sprite object.
278
279 `sprite_frame`
280 The sprite GUI.
281
282
283Sprite Class:
284 This class contains the following members:
285 Readonly:
286 GUI
287 The sprite GUI.
288
289 Methods:
290 SetOffset ( number `row`, number `column` )
291 Sets the offset of the sprite on the sprite map.
292 `row` and `column` represent the row and column on the sprite map, starting from 0.
293 For example, an offset of [0, 2] would select the third sprite in the first row.
294
295 GetOffset ( )
296 Returns the current offset of the sprite.
297
298 Destroy ( )
299 Releases the resources used by this object.
300 Run this if you're no longer using this object.
301]==]
302
303local function CreateSprite(sprite_map,SpriteFrame,sprite_size,map_size,fix_blur)
304 sprite_size = sprite_size or Vector2.new(32,32)
305 map_size = map_size or Vector2.new(256,256)
306 if fix_blur == nil then fix_blur = true end
307
308 if not SpriteFrame then
309 SpriteFrame = Create'Frame'{
310 Name = "Sprite";
311 BackgroundTransparency = 1;
312 }
313 end
314 SpriteFrame.ClipsDescendants = true;
315 local MapFrame = Create'ImageLabel'{
316 Name = "SpriteMap";
317 Active = false;
318 BackgroundTransparency = 1;
319 Image = sprite_map;
320 Size = UDim2.new(map_size.x/sprite_size.x,0,map_size.y/sprite_size.y,0);
321 Parent = SpriteFrame;
322 };
323
324 local off_row,off_col = 0,0
325
326 local SetOffset = fix_blur
327 and function(row,col)
328 local size = SpriteFrame.AbsoluteSize
329 MapFrame.Position = UDim2.new(-col - 0.5/size.x,0,-row - 0.5/size.y,0)
330 off_row,off_col = row,col
331 end
332 or function(row,col)
333 MapFrame.Position = UDim2.new(-col,0,-row,0)
334 off_row,off_col = row,col
335 end;
336
337 if fix_blur then
338 SpriteFrame.Changed:connect(function(p)
339 if p == "AbsoluteSize" then
340 SetOffset(off_row,off_col)
341 end
342 end)
343 end
344
345 local Class = {
346 GUI = SpriteFrame;
347 SetOffset = SetOffset;
348 GetOffset = function()
349 return off_row,off_col
350 end;
351 }
352
353 function Class.Destroy()
354 for k in pairs(Class) do
355 Class[k] = nil
356 end
357 SpriteFrame:Destroy()
358 end
359
360 return Class,SpriteFrame
361end
362
363lib.Sprite = CreateSprite
364
365--[[DEPEND:]]
366
367doc["Stylist"] = [==[
368Stylist ( table `style` ) [constructor]
369 returns: Stylist `object`, table `style`
370
371Creates a new stylist, which manages the properties of an entire group of objects.
372
373More stylists can also be added, refered to as "substylists".
374
375When a property of a stylist is updated, the properties in the stylist's objects are updated to reflect it.
376The properties of objects in each substylist are also updated, but only if that property isn't defined in the substylist's Style.
377
378Arguments:
379 `style`
380 A table of property/value pairs.
381 The stylist's "Style".
382 Optional; defaults to an empty table.
383
384Returns:
385 `object`
386 The Stylist object.
387
388 `style`
389 The style table.
390
391
392Stylist Class:
393 Contains the following members:
394
395 Readonly:
396 Style
397 The `style` table.
398
399 Methods:
400 AddObject ( Instance `object`, table `alias_map` )
401 Adds `object` to the stylist and updates its properties.
402 If `alias_map` is specified, then for this object, properties in the style will first be mapped though this table (see Alias Maps).
403 Returns `object`
404
405 AddObjects ( table `objects`, table `alias_map` )
406 Adds multiple objects to the stylist.
407 `objects` is a list of objects to be added to the stylist.
408 If `alias_map` is specified, it will be applied to all objects in the list.
409 Returns `objects`
410
411 RemoveObject ( Instance `object` )
412 Removes `object` from the stylist.
413 Returns `object`
414
415 RemoveObjects ( table `objects` )
416 Removes multiple objects from the stylist.
417 `objects` is a list of objects to be removed from the stylist.
418 Returns `objects`
419
420 GetObjects ( )
421 Returns a list of objects added to this stylist.
422 Items in the list have no defined order.
423
424 ObjectIn ( Instance `object` )
425 If `object` is in the stylist, returns true.
426 If `object` has an alias in the stylist, returns the alias.
427 Otherwise, returns false.
428
429 SetProperty ( string `property`, * `value` )
430 Sets `property` in each object to `value`.
431 Also sets the value in the `style` table.
432
433 SetProperties ( table `properties` )
434 Similar to SetProperty, but sets multiple properties at once.
435 `properties` is a table of property/value pairs.
436 To set a property to nil, use ]==]..PROJECT_NAME..[==[.NULL as the value.
437
438 ClearProperties ( )
439 Clears all set properties in the style table.
440
441 Update ( )
442 Updates each object's properties to reflect values in the `style` table.
443
444 SetInternal ( string `property`, * `value` )
445 Sets the properties of the stylist's objects and substylists without modifying the stylist's Style.
446
447 Destroy ( )
448 Releases the resources used by this object.
449 Run this if you're no longer using this object.
450
451 AddStylist ( Stylist `stylist`, table `alias_map` )
452 Adds `stylist` to this stylist and updates its properties.
453 If `alias_map` is specified, then for this stylist, properties in the style will first be mapped though this table (see Alias Maps).
454 Adding two stylists to each other is not recommended.
455
456 RemoveStylist ( Stylist `stylist` )
457 Removes `stylist` for this stylist.
458
459 GetStylists ( )
460 Returns a list of stylists added to this stylist.
461 Items in the list have no defined order.
462
463 StylistIn ( Stylist `stylist` )
464 If `stylist` is in the stylist, returns true.
465 If `stylist` has an alias in the stylist, returns the alias.
466 Otherwise, returns false.
467
468
469Alias Maps:
470 These let you set the property of an object as if it were another.
471 For example, if an alias map contains ["TextColor3"] = "BackgroundColor3", then
472 the object will have its BackgroundColor3 property set using the TextColor3 value in the Style.
473 This implies that both properties have the same type, or at least be convertible.
474]==]
475
476local function CreateStylist(StyleTable)
477 StyleTable = StyleTable or {}
478 local ParentStylists = {}
479 local Class = {
480 Style = StyleTable;
481 ParentStylists = ParentStylists;
482 }
483
484 local ObjectLookup = WEAK_TABLES and setmetatable({},WEAK_MODE.K) or {}
485 local AliasObjectLookup = WEAK_TABLES and setmetatable({},WEAK_MODE.K) or {}
486 -- ISSUE: objects are getting dropped when they obviously haven't been collected
487 local StylistLookup = WEAK_TABLES and setmetatable({},WEAK_MODE.K) or {}
488 local AliasStylistLookup = WEAK_TABLES and setmetatable({},WEAK_MODE.K) or {}
489
490 local function pset(t,k,v)
491 t[k] = v
492 end
493
494 local function set_object(object,property,value)
495 pcall(pset,object,property,value)
496 end
497
498 local function set_stylist(stylist,property,value)
499 if stylist.Style[property] == nil then
500 stylist.SetInternal(property,value)
501 end
502 end
503
504 local function set_alias_object(object,alias_map,property,value)
505 local alias = alias_map[property]
506 if alias then
507 pcall(pset,object,alias,value)
508 else
509 pcall(pset,object,property,value)
510 end
511 end
512
513 local function set_alias_stylist(stylist,alias_map,property,value)
514 local alias = alias_map[property]
515 if alias then
516 if stylist.Style[alias] == nil then
517 stylist.SetInternal(alias,value)
518 end
519 else
520 if stylist.Style[property] == nil then
521 stylist.SetInternal(property,value)
522 end
523 end
524 end
525
526 local function AddObject(object,alias_map)
527 if type(alias_map) == "table" then
528 AliasObjectLookup[object] = alias_map
529 for property,value in pairs(StyleTable) do
530 set_alias_object(object,alias_map,property,value)
531 end
532 else
533 ObjectLookup[object] = true
534 for property,value in pairs(StyleTable) do
535 set_object(object,property,value)
536 end
537 end
538 for parent in pairs(ParentStylists) do
539 parent.Update(Class)
540 end
541 return object
542 end
543
544 local function AddObjects(objects,alias_map)
545 if type(alias_map) == "table" then
546 for i,object in pairs(objects) do
547 AliasObjectLookup[object] = alias_map
548 for property,value in pairs(StyleTable) do
549 set_alias_object(object,alias_map,property,value)
550 end
551 end
552 else
553 for i,object in pairs(objects) do
554 ObjectLookup[object] = true
555 for property,value in pairs(StyleTable) do
556 set_object(object,property,value)
557 end
558 end
559 end
560 for parent in pairs(ParentStylists) do
561 parent.Update(Class)
562 end
563 return objects
564 end
565
566 local function RemoveObject(object)
567 ObjectLookup[object] = nil
568 AliasObjectLookup[object] = nil
569 return object
570 end
571
572 local function RemoveObjects(objects)
573 for i,object in pairs(objects) do
574 ObjectLookup[object] = nil
575 AliasObjectLookup[object] = nil
576 end
577 return objects
578 end
579
580 local function GetObjects()
581 local list = {}
582 for object in pairs(ObjectLookup) do
583 list[#list+1] = object
584 end
585 for object in pairs(AliasObjectLookup) do
586 list[#list+1] = object
587 end
588 return list
589 end
590
591 local function ObjectIn(object)
592 if ObjectLookup[object] then
593 return true
594 elseif AliasObjectLookup[object] then
595 return ObjectAliasLookup[object]
596 else
597 return false
598 end
599 end
600
601 local function AddStylist(stylist,alias_map)
602 stylist.ParentStylists[Class] = true
603 if alias_map and type(alias_map) == "table" then
604 AliasStylistLookup[stylist] = alias_map
605 for property,value in pairs(StyleTable) do
606 set_alias_stylist(stylist,alias_map,property,value)
607 end
608 else
609 StylistLookup[stylist] = true
610 for property,value in pairs(StyleTable) do
611 set_stylist(stylist,property,value)
612 end
613 end
614 for parent in pairs(ParentStylists) do
615 parent.Update(Class)
616 end
617 return stylist
618 end
619
620 local function RemoveStylist(stylist)
621 stylist.ParentStylists[Class] = nil
622 StylistLookup[stylist] = nil
623 AliasStylistLookup[stylist] = nil
624 return stylist
625 end
626
627 local function GetStylists()
628 local list = {}
629 for stylist in pairs(StylistLookup) do
630 list[#list+1] = stylist
631 end
632 for stylist in pairs(AliasStylistLookup) do
633 list[#list+1] = stylist
634 end
635 return list
636 end
637
638 local function StylistIn(stylist)
639 if StylistLookup[stylist] then
640 return true
641 elseif AliasStylistLookup[stylist] then
642 return StylistAliasLookup[stylist]
643 else
644 return false
645 end
646 end
647
648 local function SetInternal(property,value)
649 for object in pairs(ObjectLookup) do
650 set_object(object,property,value)
651 end
652 for stylist in pairs(StylistLookup) do
653 set_stylist(stylist,property,value)
654 end
655 for object,alias_map in pairs(AliasObjectLookup) do
656 set_alias_object(object,alias_map,property,value)
657 end
658 for stylist,alias_map in pairs(AliasStylistLookup) do
659 set_alias_stylist(stylist,alias_map,property,value)
660 end
661 end
662
663 local function SetProperty(property,value)
664 if value == nil or value == NULL then
665 StyleTable[property] = nil
666 for parent in pairs(ParentStylists) do
667 parent.Update(Class)
668 end
669 else
670 StyleTable[property] = value
671 SetInternal(property,value)
672 end
673 end
674
675 local function SetProperties(new_style)
676 local continue = false
677 for property,value in pairs(new_style) do
678 if value == NULL then
679 StyleTable[property] = nil
680 continue = true
681 else
682 StyleTable[property] = value
683 SetInternal(property,value)
684 end
685 end
686 if continue then
687 for parent in pairs(ParentStylists) do
688 parent.Update(Class)
689 end
690 end
691 end
692
693 local function ClearProperties()
694 for property,value in pairs(StyleTable) do
695 StyleTable[property] = nil
696 end
697 for parent in pairs(ParentStylists) do
698 parent.Update(Class)
699 end
700 end
701
702 local function Update(object)
703 for parent in pairs(ParentStylists) do
704 parent.Update(Class)
705 end
706 if object then
707 if ObjectLookup[object] then
708 for property,value in pairs(StyleTable) do
709 set_object(object,property,value)
710 end
711 elseif StylistLookup[object] then
712 for property,value in pairs(StyleTable) do
713 set_stylist(object,property,value)
714 end
715 elseif AliasObjectLookup[object] then
716 local alias_map = AliasObjectLookup[object]
717 for property,value in pairs(StyleTable) do
718 set_alias_object(object,alias_map,property,value)
719 end
720 elseif AliasStylistLookup[object] then
721 for property,value in pairs(StyleTable) do
722 set_alias_stylist(object,alias_map,property,value)
723 end
724 end
725 else
726 for property,value in pairs(StyleTable) do
727 SetInternal(property,value)
728 end
729 end
730 end
731
732 Class.AddObject = AddObject;
733 Class.AddObjects = AddObjects;
734 Class.RemoveObject = RemoveObject;
735 Class.RemoveObjects = RemoveObjects;
736 Class.GetObjects = GetObjects;
737 Class.ObjectIn = ObjectIn;
738 Class.AddStylist = AddStylist;
739 Class.RemoveStylist = RemoveStylist;
740 Class.GetStylists = GetStylists;
741 Class.StylistIn = StylistIn;
742 Class.SetInternal = SetInternal;
743 Class.SetProperty = SetProperty;
744 Class.SetProperties = SetProperties;
745 Class.ClearProperties = ClearProperties;
746 Class.Update = Update;
747
748 function Class.Destroy()
749 for parent in pairs(ParentStylists) do
750 parent.RemoveStylist(Class)
751 ParentStylists[parent] = nil
752 end
753 for k in pairs(Class) do
754 Class[k] = nil
755 end
756 for k in pairs(ObjectLookup) do
757 ObjectLookup[k] = nil
758 end
759 for k in pairs(AliasObjectLookup) do
760 AliasObjectLookup[k] = nil
761 end
762 for k in pairs(StylistLookup) do
763 StylistLookup[k] = nil
764 end
765 for k in pairs(AliasStylistLookup) do
766 AliasStylistLookup[k] = nil
767 end
768 end
769
770 return Class,StyleTable
771end
772
773lib.Stylist = CreateStylist
774
775--[[DEPEND:]]
776
777doc["AutoSizeLabel"] = [==[
778AutoSizeLabel ( GuiText `label` ) [constructor]
779 returns: AutoSizeLabel `object`, GuiText `label`
780
781Creates a text GUI that automatically resizes to its text.
782Note that this is dependant on the TextBounds property.
783
784Arguments:
785 `label`
786 An object to turn into an auto-sizing label.
787 Optional; defaults to a new TextLabel
788
789Returns:
790 `object`
791 The AutoSizingLabel object.
792 `label`
793 The auto-sizing label GUI.
794
795
796AutoSizeLabel Class:
797 This class contains the following members:
798
799 Read-Only:
800 GUI
801 The auto-sizing label GUI.
802
803 Methods:
804 LockAxis ( number `x`, number `y` )
805 Locks the size of the label on an axis to a specific amount.
806 `x` sets the amount, in pixels, to lock to on the x axis.
807 `y` sets the amount, in pixels, to lock to on the y axis.
808 Passing either value as nil will unlock that respective axis.
809
810 SetPadding ( number `pt`, number `pr`, number `pb`, number `pl` )
811 Sets the padding for each side of the label.
812
813 Passing four values specifies the top, right, bottom, and left, in that order.
814 Passing three values specifies the top (`pt`), right/left (`pr`), and bottom (`pb`).
815 Passing two values specifies the top/bottom (`pt`), and right/left (`pr`).
816 Passing one value specifies that all sides have that value.
817 Passing no values sets all sides to 0.
818
819 If the text is aligned to a certain side, the padding on that side will be ignored.
820
821 Update ( )
822 Updates the label.
823
824 Destroy ( )
825 Releases the resources used by this object.
826 Run this if you're no longer using this object.
827]==]
828
829local function CreateAutoSizeLabel(Label)
830 local Class = {}
831 if not Label then
832 Label = Create'TextLabel'{
833 Name = "AutoSizeLabel";
834 BackgroundColor3 = Color3.new(0,0,0);
835 BorderColor3 = Color3.new(1,1,1);
836 TextColor3 = Color3.new(1,1,1);
837 FontSize = "Size14";
838 Font = "ArialBold";
839 }
840 end
841 Class.GUI = Label
842
843 local pt,pr,pb,pl = 0,0,0,0
844 local t,r,b,l = 0,0,0,0
845 local lx,ly
846 local function Update()
847 local bounds = Label.TextBounds
848 local x = lx or bounds.x+l+r
849 local y = ly or bounds.y+t+b
850 Label.Size = UDim2.new(0,x,0,y)
851 end
852 Class.Update = Update
853
854 local function reflectPadding()
855 t,r,b,l = pt,pr,pb,pl
856 if Label.TextXAlignment == Enum.TextXAlignment.Left then
857 l = 0
858 elseif Label.TextXAlignment == Enum.TextXAlignment.Right then
859 r = 0
860 end
861 if Label.TextYAlignment == Enum.TextYAlignment.Top then
862 t = 0
863 elseif Label.TextYAlignment == Enum.TextYAlignment.Bottom then
864 b = 0
865 end
866 Update()
867 end
868
869 local function SetPadding(nt,nr,nb,nl)
870 if nl then
871 pt,pr,pb,pl = nt,nr,nb,nl
872 elseif nb then
873 pt,pr,pb,pl = nt,nr,nb,nr
874 elseif nr then
875 pt,pr,pb,pl = nt,nr,nt,nr
876 elseif nt then
877 pt,pr,pb,pl = nt,nt,nt,nt
878 else
879 pt,pr,pb,pl = 0,0,0,0
880 end
881 reflectPadding()
882 end
883 Class.SetPadding = SetPadding
884
885 local function LockAxis(x,y)
886 lx,ly = x,y
887 Update()
888 end
889 Class.LockAxis = LockAxis
890
891 local con = Label.Changed:connect(function(p)
892 if p == "TextBounds" then
893 Update()
894 elseif p == "TextXAlignment" or p == "TextYAlignment" then
895 reflectPadding()
896 end
897 end)
898
899 local function Destroy()
900 for k in pairs(Class) do
901 Class[k] = nil
902 end
903 con:disconnect()
904 end
905 Class.Destroy = Destroy
906
907 Update()
908
909 return Class,Label
910end
911
912lib.AutoSizeLabel = CreateAutoSizeLabel
913
914--[[DEPEND:]]
915
916doc["TruncatingLabel"] = [==[
917TruncatingLabel ( GuiText `label` ) [constructor]
918 returns: GuiText `label`
919
920Creates a label that displays truncated text.
921When the label is hovered over, the full text is displayed.
922
923Arguments:
924 `label`
925 An object that will be turned into a truncating label.
926 Optional; defaults to a new GuiText
927
928Returns:
929 `label`
930 The label GUI.
931]==]
932
933local function CreateTruncatingLabel(Label)
934 if not Label then
935 Label = Create'TextLabel'{
936 BackgroundColor3 = Color3.new(0,0,0);
937 BorderColor3 = Color3.new(1,1,1);
938 TextColor3 = Color3.new(1,1,1);
939 FontSize = "Size14";
940 Font = "ArialBold";
941 Text = "";
942 }
943 end
944 Label.ClipsDescendants = true;
945
946 local FullTextLabel = Create'TextLabel'{
947 Name = "FullTextLabel";
948 BackgroundColor3 = Label.BackgroundColor3;
949 BorderColor3 = Label.BorderColor3;
950 TextColor3 = Label.TextColor3;
951 FontSize = Label.FontSize;
952 Font = Label.Font;
953 Text = Label.Text;
954 Visible = false;
955 ZIndex = 9;
956 Parent = Label;
957 }
958
959 local ex = {
960 Name = true;
961 Parent = true;
962 Position = true;
963 Size = true;
964 ClipsDescendants = true;
965 ZIndex = true;
966 Visible = true;
967 }
968
969 local function pset(t,k,v)
970 t[k] = v
971 end
972
973 Label.Changed:connect(function(p)
974 if not ex[p] then
975 pcall(pset,FullTextLabel,p,Label[p])
976 end
977 end)
978
979 Label.MouseEnter:connect(function()
980 local align = Label.TextXAlignment
981 local bound = math.max(Label.TextBounds.x+4,Label.AbsoluteSize.x)
982 if align == Enum.TextXAlignment.Center then
983 FullTextLabel.Size = UDim2.new(0,bound,1,0)
984 FullTextLabel.Position = UDim2.new(0.5,-bound/2,0,0)
985 elseif align == Enum.TextXAlignment.Right then
986 FullTextLabel.Size = UDim2.new(0,bound,1,0)
987 FullTextLabel.Position = UDim2.new(1,-bound,0,0)
988 else
989 FullTextLabel.Size = UDim2.new(0,bound,1,0)
990 FullTextLabel.Position = UDim2.new(0,0,0,0)
991 end
992 Label.ClipsDescendants = false
993 SetZIndex(FullTextLabel,9)
994 FullTextLabel.Visible = true
995 end)
996
997 FullTextLabel.MouseLeave:connect(function()
998 FullTextLabel.Visible = false
999 Label.ClipsDescendants = true
1000 end)
1001
1002 return Label
1003end
1004
1005lib.TruncatingLabel = CreateTruncatingLabel
1006
1007--[[DEPEND:]]
1008
1009doc["DockedSide"] = [==[
1010DockedSide [enum]
1011
1012Represents a side that a dockable was docked on.
1013
1014Enumerators:
1015 0:None
1016 Represents no sides of a dockable
1017 1:Left
1018 Represents the left side of a dockable
1019 2:Right
1020 Represents the right side of a dockable
1021 4:Top
1022 Represents the top side of a dockable
1023 8:Bottom
1024 Represents the bottom side of a dockable
1025]==]
1026
1027local DockedSide = {
1028 None = 0;
1029 Left = 1;
1030 Top = 2;
1031 Right = 4;
1032 Bottom = 8;
1033 [0] = 'None';
1034 [1] = 'Left';
1035 [2] = 'Top';
1036 [4] = 'Right';
1037 [8] = 'Bottom';
1038}
1039lib.DockedSide = DockedSide
1040
1041--[===[
1042doc["DockedSide"] = [==[
1043[enum] DockedSide
1044
1045Represents a side that a dockable was docked on.
1046
1047Enumerators:
1048 Left
1049 Represents the left side of a dockable
1050 Right
1051 Represents the right side of a dockable
1052 Top
1053 Represents the top side of a dockable
1054 Bottom
1055 Represents the bottom side of a dockable
1056
1057 XAxis
1058 Represents docking on the XAxis
1059 Equal to Left and Right
1060 YAxis
1061 Represents docking on the YAxis
1062 Equal to Top and Bottom
1063 Inner
1064 Represents docking on the inner sides (towards 0,0)
1065 Equal to Left and Top
1066 Outer
1067 Represents docking on the outer sides (away from 0,0)
1068 Equal to Right and Bottom
1069
1070 None
1071 Represents no sides, or neither the X nor Y axis
1072 Equal to Center
1073 Center
1074 Represents no docking, or docked on neither the inner or outer sides
1075 Equal to None
1076
1077Value Casts:
1078 -1, "-1"
1079 -> Inner
1080 0, "0"
1081 -> Center
1082 1, "1"
1083 -> Outer
1084 "x", "X"
1085 -> XAxis
1086 "y", "Y"
1087 -> YAxis
1088 "" (empty string)
1089 -> None
1090]==]
1091
1092local DockedSide do
1093 local enum = setmetatable({__type = "DockedSide"},ENUM_MT)
1094 local mt = {
1095 __eq = function(a,b)
1096 local n = b[1]
1097 for i = 1,#a do
1098 if a[i] == n then
1099 return true
1100 end
1101 end
1102 return false
1103 end;
1104 __tostring = function(e)
1105 return e.__type..'.'..e[1]
1106 end;
1107 }
1108
1109---- Enumerators
1110 enum.Left = setmetatable({__type = enum.__type, 'Left','XAxis','Inner'},mt)
1111 enum.Right = setmetatable({__type = enum.__type, 'Right','XAxis','Outer'},mt)
1112 enum.Top = setmetatable({__type = enum.__type, 'Top','YAxis','Inner'},mt)
1113 enum.Bottom = setmetatable({__type = enum.__type, 'Bottom','YAxis','Outer'},mt)
1114
1115 enum.XAxis = setmetatable({__type = enum.__type, 'XAxis','Left','Right'},mt)
1116 enum.YAxis = setmetatable({__type = enum.__type, 'YAxis','Top','Bottom'},mt)
1117 enum.Inner = setmetatable({__type = enum.__type, 'Inner','Left','Top'},mt)
1118 enum.Outer = setmetatable({__type = enum.__type, 'Outer','Right','Bottom'},mt)
1119
1120 enum.None = setmetatable({__type = enum.__type, 'None','Center'},mt)
1121 enum.Center = setmetatable({__type = enum.__type, 'Center','None'},mt)
1122
1123---- Value Casts
1124 enum['x'] = enum.XAxis
1125 enum['y'] = enum.YAxis
1126 enum[''] = enum.None
1127
1128 enum['X'] = enum.XAxis
1129 enum['Y'] = enum.YAxis
1130
1131 enum[-1] = enum.Inner
1132 enum[0] = enum.Center
1133 enum[1] = enum.Outer
1134
1135 enum['-1'] = enum.Inner
1136 enum['0'] = enum.Center
1137 enum['1'] = enum.Outer
1138
1139---- Finish
1140 DockedSide = enum
1141 lib.DockedSide = enum
1142end
1143]===]
1144
1145doc["DockContainer"] = [==[
1146DockContainer ( GuiBase `container` ) [constructor]
1147 returns: DockContainer `object`, GuiBase `container`
1148
1149Creates a container whose children can snap to each others' edges when dragged (referred to as "dockables").
1150Only children that are GuiButtons will be made draggable.
1151However, they will still dock to any sibling that is a GuiObject.
1152
1153Arguments:
1154 `container`
1155 An object that becomes the dock container.
1156 Optional; defaults to a new ScreenGui
1157
1158Returns:
1159 `object`
1160 The DockContainer object.
1161
1162 `container`
1163 The container GUI.
1164
1165DockContainer Class:
1166 This class contains the following members:
1167
1168 Readonly:
1169 GUI
1170 The container GUI.
1171
1172 Fields:
1173 SnapWidth
1174 A number indicating the space (in pixels) required between the edges of two dockables before one snaps to the other.
1175 Initially, this value is 16.
1176
1177 ConstrainToContainer
1178 A bool indicating whether dockables can't be dragged outside the container.
1179 Initially, this value is false.
1180
1181 SnapToContainerEdge
1182 A bool indicating whether dockables can snap to the edge of the container.
1183 Only applies if ConstrainToContainer is false.
1184 Initially, this value is true.
1185
1186 PositionScaled
1187 A bool indicating whether the position of dockables are set as a Scale or an Offset.
1188 Initially, this value is true.
1189
1190 DragZIndex
1191 A number indicating the amount to increase the ZIndex of a dockable by when it is dragged.
1192 Initially, this value is 1.
1193
1194 Methods:
1195 InvokeDrag ( GuiObject `dragged`, Vector2 `mouse_offset` )
1196 Starts dragging `dragged` as if it were clicked.
1197 `mouse_offset` is the position of the mouse when it "clicked" the object, in relation to the object.
1198
1199 StopDrag ( )
1200 Stops the dragging of an object, if there is an object being dragged.
1201
1202 Callbacks:
1203 DragBeginCallback ( GuiObject `dragged`, Vector2 `mouse_offset` )
1204 Called before an object starts being dragged.
1205 `dragged` is the object being dragged.
1206 `mouse_offset` is the position of the mouse, in relation to the object.
1207 If the function returns false, then the drag will be canceled.
1208
1209 DragCallback ( GuiObject `dragged`, Vector2 `mouse_offset` )
1210 Called when an object is dragged, before the object updates.
1211 `dragged` is the object being dragged.
1212 `mouse_offset` is the location of the mouse when it started dragging, in relation to the object.
1213 If the function returns false, then the object's position will not be updated.
1214
1215 DockCallback ( GuiObject `dragged`, GuiObject `docked`, DockedSide `docked_side` )
1216 Called before the currently dragged object snaps to another dockable.
1217 `dragged` is the current object being dragged.
1218 `docked` is the dockable that `dragged` snapped to.
1219 `docked_side` is the side of `dragged` that was docked on.
1220 Note that `docked` can be the container, if objects are allowed to snap to it.
1221 If the function returns false, then the snap to the dockable will be canceled.
1222
1223 Events:
1224 DragBegin ( GuiObject `dragged`, Vector2 `mouse_offset` )
1225 Fired after an object start being dragged.
1226 `dragged` is the object being dragged.
1227 `mouse_offset` is the position of the mouse, in relation to the object.
1228
1229 DragStopped ( GuiObject `dragged`, Vector2 `mouse_pos` )
1230 Called after an object stops being dragged.
1231 `dragged` is the object that was dragged.
1232 `mouse_offset` is the position of the mouse when it started dragging, in relation to the object.
1233
1234 ObjectDocked ( GuiObject `dragged`, GuiObject `docked`, DockedSide `docked_side` )
1235 Fired after the currently dragged objects snaps to another dockable.
1236 `dragged` is the current object being dragged.
1237 `docked` is the dockable that `dragged` snapped to.
1238 `docked_side` is the side of `dragged` that was docked on.
1239 Note that `docked` can be the container, if objects are allowed to snap to it.
1240]==]
1241
1242local function CreateDockContainer(Container)
1243 if not Container then
1244 Container = Instance.new("ScreenGui")
1245 Container.Name = "DockContainer"
1246 end
1247
1248 local Class = {
1249 GUI = Container;
1250 SnapWidth = 16;
1251 SnapToEdge = true;
1252 ConstrainToContainer = false;
1253 PositionScaled = true;
1254 DragZIndex = 1;
1255 }
1256
1257 local DragEvent = {}
1258 local MouseDrag = Create'ImageButton'{
1259 Active = false;
1260 Size = UDim2.new(1.5, 0, 1.5, 0);
1261 AutoButtonColor = false;
1262 BackgroundTransparency = 1;
1263 Name = "MouseDrag";
1264 Position = UDim2.new(-0.25, 0, -0.25, 0);
1265 ZIndex = 10;
1266 }
1267
1268 local function stopDragDefault()
1269 return false,"no object is being dragged"
1270 end
1271 Class.StopDrag = stopDragDefault
1272
1273 local eventDragBegin = Instance.new("BindableEvent")
1274 Class.DragBegin = eventDragBegin.Event
1275
1276 local eventDragStopped = Instance.new("BindableEvent")
1277 Class.DragStopped = eventDragStopped.Event
1278
1279 local eventObjectDocked = Instance.new("BindableEvent")
1280 Class.ObjectDocked = eventObjectDocked.Event
1281
1282 local function InvokeDrag(dockable,mouse_offset)
1283 if Class.DragBeginCallback then
1284 if Class.DragBeginCallback(dockable,mouse_offset)
1285 == false then return end
1286 end
1287
1288 local drag_con
1289 local up_con
1290
1291 drag_con = MouseDrag.MouseMoved:connect(function(x,y)
1292 if Class.DragCallback then
1293 if Class.DragCallback(dockable,mouse_offset)
1294 == false then return end
1295 end
1296
1297 local snapWidth = Class.SnapWidth
1298
1299 local cApos = Container.AbsolutePosition
1300 local Apos = Vector2.new(x,y) - mouse_offset
1301
1302 local cAsize = Container.AbsoluteSize
1303 local Asize = dockable.AbsoluteSize
1304
1305 local APX,APY = Apos.x,Apos.y
1306 local ASX,ASY = Asize.x,Asize.y
1307
1308 x = Apos.x - cApos.x
1309 y = Apos.y - cApos.y
1310
1311 local docked_x,docked_y
1312 local side_x,side_y
1313
1314 if Class.DockCallback then
1315 for i,sibling in pairs(Container:GetChildren()) do
1316 if sibling:IsA"GuiObject" and sibling ~= dockable and sibling.Visible then
1317 local sApos = sibling.AbsolutePosition
1318 local sAsize = sibling.AbsoluteSize
1319
1320 if Apos.x + Asize.x >= sApos.x and Apos.x <= sApos.x + sAsize.x then
1321 if math.abs((Apos.y + Asize.y) - sApos.y) <= snapWidth then
1322 if Class.DockCallback(dockable,sibling,DockedSide.Bottom) ~= false then
1323 -- docked on bottom side
1324 y = sApos.y - cApos.y - Asize.y
1325 eventObjectDocked:Fire(dockable,sibling,DockedSide.Bottom)
1326 end
1327 elseif math.abs(Apos.y - (sApos.y + sAsize.y)) <= snapWidth then
1328 if Class.DockCallback(dockable,sibling,DockedSide.Top) ~= false then
1329 -- docked on top side
1330 y = sApos.y - cApos.y + sAsize.y
1331 eventObjectDocked:Fire(dockable,sibling,DockedSide.Top)
1332 end
1333 end
1334 end
1335 if Apos.y + Asize.y >= sApos.y and Apos.y <= sApos.y + sAsize.y then
1336 if math.abs((Apos.x + Asize.x) - sApos.x) <= snapWidth then
1337 if Class.DockCallback(dockable,sibling,DockedSide.Right) ~= false then
1338 -- docked on right side
1339 x = sApos.x - cApos.x - Asize.x
1340 eventObjectDocked:Fire(dockable,sibling,DockedSide.Right)
1341 end
1342 elseif math.abs(Apos.x - (sApos.x + sAsize.x)) <= snapWidth then
1343 if Class.DockCallback(dockable,sibling,DockedSide.Left) ~= false then
1344 -- docked on left side
1345 x = sApos.x - cApos.x + sAsize.x
1346 eventObjectDocked:Fire(dockable,sibling,DockedSide.Left)
1347 end
1348 end
1349 end
1350 end
1351 end
1352 if Class.ConstrainToContainer then
1353 if APY < cApos.y then
1354 if Class.DockCallback(dockable,Container,DockedSide.Top) ~= false then
1355 -- docked on top side
1356 y = 0
1357 eventObjectDocked:Fire(dockable,Container,DockedSide.Top)
1358 end
1359 elseif APY + ASY > cApos.y + cAsize.y then
1360 if Class.DockCallback(dockable,Container,DockedSide.Bottom) ~= false then
1361 -- docked on bottom side
1362 y = cAsize.y - ASY
1363 eventObjectDocked:Fire(dockable,Container,DockedSide.Bottom)
1364 end
1365 end
1366 if APX < cApos.x then
1367 if Class.DockCallback(dockable,Container,DockedSide.Left) ~= false then
1368 -- docked on left side
1369 x = 0
1370 eventObjectDocked:Fire(dockable,Container,DockedSide.Left)
1371 end
1372 elseif APX + ASX > cApos.x + cAsize.x then
1373 if Class.DockCallback(dockable,Container,DockedSide.Right) ~= false then
1374 -- docked on right side
1375 x = cAsize.x - ASX
1376 eventObjectDocked:Fire(dockable,Container,DockedSide.Right)
1377 end
1378 end
1379 elseif Class.SnapToEdge then
1380 if math.abs(APY - cApos.y) <= snapWidth then
1381 if Class.DockCallback(dockable,Container,DockedSide.Top) ~= false then
1382 -- docked on top side
1383 y = 0
1384 eventObjectDocked:Fire(dockable,Container,DockedSide.Top)
1385 end
1386 elseif math.abs((APY+ASY) - (cApos.y+cAsize.y)) <= snapWidth then
1387 if Class.DockCallback(dockable,Container,DockedSide.Bottom) ~= false then
1388 -- docked on bottom side
1389 y = cAsize.y - ASY
1390 eventObjectDocked:Fire(dockable,Container,DockedSide.Bottom)
1391 end
1392 end
1393 if math.abs(APX - cApos.x) <= snapWidth then
1394 if Class.DockCallback(dockable,Container,DockedSide.Left) ~= false then
1395 -- docked on left side
1396 x = 0
1397 eventObjectDocked:Fire(dockable,Container,DockedSide.Left)
1398 end
1399 elseif math.abs((APX+ASX) - (cApos.x+cAsize.x)) <= snapWidth then
1400 if Class.DockCallback(dockable,Container,DockedSide.Right) ~= false then
1401 -- docked on right side
1402 x = cAsize.x - ASX
1403 eventObjectDocked:Fire(dockable,Container,DockedSide.Right)
1404 end
1405 end
1406 end
1407 else
1408 for i,sibling in pairs(Container:GetChildren()) do
1409 if sibling:IsA"GuiObject" and sibling ~= dockable and sibling.Visible then
1410 local sApos = sibling.AbsolutePosition
1411 local sAsize = sibling.AbsoluteSize
1412
1413 if Apos.x + Asize.x >= sApos.x and Apos.x <= sApos.x + sAsize.x then
1414 if math.abs((Apos.y + Asize.y) - sApos.y) <= snapWidth then
1415 y = sApos.y - cApos.y - Asize.y
1416 eventObjectDocked:Fire(dockable,sibling,DockedSide.Bottom)
1417 elseif math.abs(Apos.y - (sApos.y + sAsize.y)) <= snapWidth then
1418 y = sApos.y - cApos.y + sAsize.y
1419 eventObjectDocked:Fire(dockable,sibling,DockedSide.Top)
1420 end
1421 end
1422 if Apos.y + Asize.y >= sApos.y and Apos.y <= sApos.y + sAsize.y then
1423 if math.abs((Apos.x + Asize.x) - sApos.x) <= snapWidth then
1424 x = sApos.x - cApos.x - Asize.x
1425 eventObjectDocked:Fire(dockable,sibling,DockedSide.Right)
1426 elseif math.abs(Apos.x - (sApos.x + sAsize.x)) <= snapWidth then
1427 x = sApos.x - cApos.x + sAsize.x
1428 eventObjectDocked:Fire(dockable,sibling,DockedSide.Left)
1429 end
1430 end
1431 end
1432 end
1433 if Class.ConstrainToContainer then
1434 if APY < cApos.y then
1435 y = 0
1436 eventObjectDocked:Fire(dockable,Container,DockedSide.Top)
1437 elseif APY + ASY > cApos.y + cAsize.y then
1438 y = cAsize.y - ASY
1439 eventObjectDocked:Fire(dockable,Container,DockedSide.Bottom)
1440 end
1441 if APX < cApos.x then
1442 x = 0
1443 eventObjectDocked:Fire(dockable,Container,DockedSide.Left)
1444 elseif APX + ASX > cApos.x + cAsize.x then
1445 x = cAsize.x - ASX
1446 eventObjectDocked:Fire(dockable,Container,DockedSide.Right)
1447 end
1448 elseif Class.SnapToEdge then
1449 if math.abs(APY - cApos.y) <= snapWidth then
1450 y = 0
1451 eventObjectDocked:Fire(dockable,Container,DockedSide.Top)
1452 elseif math.abs((APY+ASY) - (cApos.y+cAsize.y)) <= snapWidth then
1453 y = cAsize.y - ASY
1454 eventObjectDocked:Fire(dockable,Container,DockedSide.Bottom)
1455 end
1456 if math.abs(APX - cApos.x) <= snapWidth then
1457 x = 0
1458 eventObjectDocked:Fire(dockable,Container,DockedSide.Left)
1459 elseif math.abs((APX+ASX) - (cApos.x+cAsize.x)) <= snapWidth then
1460 x = cAsize.x - ASX
1461 eventObjectDocked:Fire(dockable,Container,DockedSide.Right)
1462 end
1463 end
1464 end
1465
1466 local sx,sy = 0,0
1467 if Class.PositionScaled then
1468 sx = x/cAsize.x
1469 sy = y/cAsize.y
1470 x = 0
1471 y = 0
1472 end
1473 dockable.Position = UDim2.new(sx,x,sy,y)
1474 end)
1475 local zIndex = dockable.ZIndex
1476 local function mouse_up()
1477 Class.StopDrag = stopDragDefault
1478 MouseDrag.Parent = nil
1479 drag_con:disconnect(); drag_con = nil
1480 up_con:disconnect(); drag = nil
1481 SetZIndex(dockable,zIndex)
1482 eventDragStopped:Fire(dockable,mouse_offset)
1483 return true
1484 end
1485 up_con = MouseDrag.MouseButton1Up:connect(mouse_up)
1486 SetZIndex(dockable,zIndex + Class.DragZIndex)
1487 MouseDrag.Parent = GetScreen(dockable)
1488 Class.StopDrag = mouse_up
1489 eventDragBegin:Fire(dockable,mouse_offset)
1490 end
1491 Class.InvokeDrag = InvokeDrag
1492
1493 local function ChildAdded(child)
1494 if child:IsA"GuiButton" then
1495 DragEvent[child] = child.MouseButton1Down:connect(function(x,y)
1496 InvokeDrag(child,Vector2.new(x,y) - child.AbsolutePosition)
1497 end)
1498 end
1499 end
1500
1501 local function ChildRemoved(child)
1502 if DragEvent[child] then
1503 DragEvent[child]:disconnect()
1504 DragEvent[child] = nil
1505 end
1506 end
1507
1508 Container.ChildAdded:connect(ChildAdded)
1509 Container.ChildRemoved:connect(ChildRemoved)
1510
1511 for i,dockable in pairs(Container:GetChildren()) do
1512 ChildAdded(dockable)
1513 end
1514
1515 return Class,Container
1516end
1517
1518lib.DockContainer = CreateDockContainer
1519
1520--[[DEPEND:
1521Stylist.lua;
1522]]
1523
1524doc["Graphic"] = [==[
1525Graphic ( string `polygon`, Vector2 `size`, table `style`, table `config` ) [constructor]
1526 returns Graphic `object`, Frame `graphic`
1527
1528Creates a basic GUI graphic from a polygon or specified preset.
1529
1530Arguments:
1531 `polygon`
1532 May be a string, referencing a preset (see Presets).
1533 May also be a table that contains two tables, which represent the x and y coordinates (respectively) of each point in the polygon.
1534 If coordinates are not between 0 and 1, a 3rd entry may be specified, which is the number to divide each coordinate by.
1535
1536 `size`
1537 The size, in pixels, of the graphic.
1538 May also be a table that contains the x and y size of the graphic.
1539
1540 `style`
1541 A table that will be used with the graphic's Stylist, which controls the appearance of the graphic.
1542 Optional; defaults to an empty table.
1543 Note that the graphic essentially shares the same properties as a Frame object.
1544 So, if ["BackgroundColor3"] = Color3.new(1,1,1) were in the table, the graphic's color would be set to white.
1545
1546 `config`
1547 A table that alters how the graphic will be drawn.
1548 It can contain the following possible values:
1549 method
1550 May be "scaled" or "static".
1551 Determines if pixels will be scaled to the parent or static.
1552 round
1553 May be "half", "ceil", or "floor".
1554 If static method is chosen, this determines how to round each pixel.
1555 offset
1556 A Vector2. This offsets the polygon on the final image.
1557
1558Returns:
1559 `object`
1560 The Graphic object.
1561
1562 `graphic`
1563 The Frame object which makes up the graphic.
1564
1565Graphic Class:
1566 This class contains the following members:
1567
1568 Readonly:
1569 GUI
1570 The Frame object which makes up the graphic.
1571
1572 Style
1573 The Stylist object used to change the appearance of the graphic.
1574
1575 Methods:
1576 Destroy ( )
1577 Releases the resources used by this object.
1578 Run this if you're no longer using this object.
1579
1580
1581Presets:
1582 arrow-up
1583 arrow-down
1584 arrow-left
1585 arrow-right
1586 check-mark
1587 pin
1588 wrench
1589 cross
1590 grip
1591 vgrip
1592]==]
1593
1594--[[
1595 polygon:
1596 string: a reference to a predefined polygon
1597 table: {Vector2, ...} a list of Vector2 points
1598 table: {{number, ...},{number, ...}} two lists of x and y axes
1599 size
1600 table: {number, number}
1601 Vector2
1602 style
1603 config
1604 method = scaled|static Whether pixels will be scaled to the parent or static
1605 round = ceil|floor|half If static method is chosen, this determines how to round each pixel
1606 offset = Vector2 Offsets the polygon on the final image. The polygon will be clipped so that it doesn't render outside the image region
1607]]
1608
1609--[[
1610local polyX = {3,8,9,13,13,10,8,6,2}
1611local polyY = {2,6,2,9,13,10,15,2,10}
1612local polyCorners = 9
1613]]
1614
1615local internal_polygon = {
1616 ["arrow-up"] = {
1617 {2,4,6};
1618 {5,3,5};
1619 8;
1620 };
1621 ["arrow-down"] = {
1622 {2,4,6};
1623 {3,5,3};
1624 8;
1625 };
1626 ["arrow-left"] = {
1627 {5,3,5};
1628 {2,4,6};
1629 8;
1630 };
1631 ["arrow-right"] = {
1632 {3,5,3};
1633 {2,4,6};
1634 8;
1635 };
1636 ["check-mark"] = {
1637 {1,3,7,7,3,1};
1638 {3,5,1,3,7,5};
1639 8;
1640 };
1641 ["pin"] = {
1642 {4,11,11,12,12,8,8,7,7,3,3,4, 4,5,7,7,5,5};
1643 {2,2,9,9,10,10,14,14,10,10,9,9, 2,3,3,9,9,3};
1644 16;
1645 };
1646 ["wrench"] = {
1647 { 2; 8; 18; 25; 29; 29; 24; 20; 17; 17; 22; 16; 12; 12};
1648 { 24; 30; 20; 20; 16; 10; 15; 15; 12; 8; 3; 3; 7; 14};
1649 32,
1650 };
1651 ["cross"] = {
1652 {1; 2; 4; 6; 7; 7; 5; 7; 7; 6; 4; 2; 1; 1; 3; 1};
1653 {1; 1; 3; 1; 1; 2; 4; 6; 7; 7; 5; 7; 7; 6; 4; 2};
1654 8;
1655 };
1656 ["grip"] = function(size,class,config)
1657 local GraphicFrame = class.GUI
1658 GraphicFrame.Size = UDim2.new(0,size.x*(size.y == 0 and 2 or size.y),0,size.x*2)
1659 for i=1,size.x do
1660 local p = Instance.new("Frame",GraphicFrame)
1661 p.BackgroundColor3 = Color3.new(0,0,0)
1662 p.BorderSizePixel = 0
1663 p.Size = UDim2.new(1,0,0,1)
1664 p.Position = UDim2.new(0,0,0,(i-1)*(size.y == 0 and 2 or size.y))
1665 class.Stylist.AddObject(p)
1666 end
1667
1668 return class,GraphicFrame
1669 end;
1670 ["vgrip"] = function(size,class,config)
1671 local GraphicFrame = class.GUI
1672 GraphicFrame.Size = UDim2.new(0,size.x*2,0,size.x*(size.y == 0 and 2 or size.y))
1673 for i=1,size.x do
1674 local p = Instance.new("Frame",GraphicFrame)
1675 p.BackgroundColor3 = Color3.new(0,0,0)
1676 p.BorderSizePixel = 0
1677 p.Size = UDim2.new(0,1,1,0)
1678 p.Position = UDim2.new(0,(i-1)*(size.y == 0 and 2 or size.y),0,0)
1679 class.Stylist.AddObject(p)
1680 end
1681
1682 return class,GraphicFrame
1683 end;
1684}
1685
1686local function CreateGraphic(polygon,size,style,config)
1687--[[ local function round(d)
1688 local i = floor(d)
1689 d = d - i
1690 if d < 0.5 then
1691 return i
1692 elseif d > 0.5 then
1693 return i + 1
1694 elseif i%2==0 then
1695 return i
1696 else
1697 return i + 1
1698 end
1699 end
1700]]
1701 local function round(n)
1702 if n < 0 then
1703 return ceil(n - 0.5)
1704 else
1705 return floor(n + 0.5)
1706 end
1707 end
1708
1709 local GraphicFrame = Instance.new("Frame")
1710 GraphicFrame.Name = "Graphic"
1711 GraphicFrame.BackgroundTransparency = 1
1712
1713 local GraphicStylist = CreateStylist(style)
1714
1715 local Class = {
1716 GUI = GraphicFrame;
1717 Stylist = GraphicStylist;
1718 }
1719
1720 function Class.Destroy()
1721 for k in pairs(Class) do
1722 Class[k] = nil
1723 end
1724 GraphicStylist.Destroy()
1725 GraphicFrame:Destroy()
1726 end
1727
1728 local polygonX,polygonY = {},{}
1729 if type(polygon) == "table" then
1730 polygonX = polygon[1]
1731 polygonY = polygon[2]
1732 local div = polygon[3]
1733 if div then
1734 for i=1,#polygonX do
1735 polygonX[i] = (polygonX[i])/div
1736 end
1737 for i=1,#polygonY do
1738 polygonY[i] = (polygonY[i])/div
1739 end
1740 end
1741 elseif type(polygon) == "string" then
1742 local in_poly = internal_polygon[polygon]
1743 if type(in_poly) == "table" then
1744 local div = in_poly[3] or 1
1745 for i=1,#in_poly[1] do
1746 polygonX[i] = (in_poly[1][i])/div
1747 end
1748 for i=1,#in_poly[2] do
1749 polygonY[i] = (in_poly[2][i])/div
1750 end
1751 elseif type(in_poly) == "function" then
1752 return in_poly(size,Class,config)
1753 else
1754 error("\'"..tostring(polygon).."\' is not a valid internal polygon",2)
1755 end
1756 else
1757 error("invalid polygon",2)
1758 end
1759 local posX,posY,sizeX,sizeY = 0,0,0,0
1760 config = config or {}
1761 local method = config.method or "scaled"
1762 local round = round
1763 if config.round == "ceil" then
1764 round = math.ceil
1765 elseif config.round == "floor" then
1766 round = math.floor
1767 elseif config.round == "half" then
1768 round = round
1769 end
1770 if config.offset then
1771 posX,posY = -config.offset.x,-config.offset.y
1772 end
1773 if type(size) == "userdata" then
1774 sizeX = size.x
1775 sizeY = size.y
1776 elseif type(size) == "table" then
1777 sizeX = size[1] or size.x
1778 sizeY = size[2] or size.y
1779 else
1780 error("invalid size",2)
1781 end
1782 polygonN = #polygonX
1783 for i=1,polygonN do
1784 polygonX[i] = polygonX[i]*sizeX
1785 end
1786 for i=1,polygonN do
1787 polygonY[i] = polygonY[i]*sizeY
1788 end
1789
1790 GraphicFrame.Size = UDim2.new(0,sizeX,0,sizeY)
1791
1792 local p = Instance.new("Frame")
1793 p.BorderSizePixel = 0
1794 p.BackgroundColor3 = Color3.new()
1795 p.Size = UDim2.new(0,1,0,1)
1796
1797 local fillLine
1798 if method == "scaled" then
1799 fillLine = function(x1,x2,y)
1800 x2 = x2-x1
1801 if x2 ~= 0 then
1802 local c = p:Clone()
1803 GraphicStylist.AddObject(c)
1804 c.Position = UDim2.new(x1/sizeX,0,y/sizeY,0)
1805 c.Size = UDim2.new(x2/sizeX,0,1/sizeY,0)
1806 c.Parent = GraphicFrame
1807 end
1808 end
1809 elseif method == "static" then
1810 fillLine = function(x1,x2,y)
1811 x1 = round(x1,1)
1812 x2 = round(x2,1)-x1
1813 if x2 ~= 0 then
1814 local c = p:Clone()
1815 GraphicStylist.AddObject(c)
1816 c.Position = UDim2.new(0,x1,0,y)
1817 c.Size = UDim2.new(0,x2,0,1)
1818 c.Parent = GraphicFrame
1819 end
1820 end
1821 else
1822 error("invalid method",2)
1823 end
1824
1825 for pixelY = posY,sizeY+posY-1 do
1826 local nodes = 0
1827 local nodeX = {}
1828 local j = polygonN;
1829 for i=1,polygonN do
1830 if polygonY[i] < pixelY and polygonY[j] >= pixelY or polygonY[j] < pixelY and polygonY[i] >= pixelY then
1831 nodeX[nodes] = (polygonX[i] + (pixelY - polygonY[i])/(polygonY[j] - polygonY[i])*(polygonX[j] - polygonX[i]))
1832 nodes = nodes + 1
1833 end
1834 j = i
1835 end
1836
1837 local i = 0
1838 while i < nodes - 1 do
1839 if nodeX[i] > nodeX[i+1] then
1840 nodeX[i],nodeX[i+1] = nodeX[i+1],nodeX[i]
1841 if i ~= 0 then i = i - 1 end
1842 else
1843 i = i + 1
1844 end
1845 end
1846
1847 local modX,modY = posX + sizeX, posY + sizeY
1848
1849 local i = 0
1850 while i < nodes - 1 do
1851 if nodeX[i] >= modX then
1852 break
1853 end
1854 if nodeX[i+1] > posX then
1855 if nodeX[i] < posX then
1856 nodeX[i] = posX
1857 end
1858 if nodeX[i+1] > modX then
1859 nodeX[i+1] = modX
1860 end
1861 fillLine(nodeX[i]-posX,nodeX[i+1]-posX,pixelY-posY)
1862 end
1863 i = i + 2
1864 end
1865 end
1866
1867 return Class,GraphicFrame
1868end
1869
1870lib.Graphic = CreateGraphic
1871
1872--[[DEPEND:
1873SetZIndex.lua;
1874GetScreen.lua;
1875Graphic.lua;
1876]]
1877
1878doc["ScrollBar"] = [==[
1879ScrollBar ( bool `horizontal`, number `size` ) [constructor]
1880 returns: ScrollBar `object`, Frame `scroll_bar`
1881
1882Creates a primative scroll bar.
1883This scroll bar features a draggable thumb, paging buttons at either end, and a clickable track.
1884
1885Arguments:
1886 `horizontal`
1887 If true, the scroll bar will appear horizontally instead of vertically.
1888 Optional; defaults to false.
1889 `size`
1890 Sets the width or height of the scroll bar.
1891 Optional; defaults to ]==]..ENTRY_SIZE..[==[
1892
1893Returns:
1894 `object`
1895 The ScrollBar object.
1896
1897 `scroll_bar`
1898 The scroll bar GUI.
1899
1900
1901ScrollBar Class:
1902 This class contains the following members:
1903
1904 Readonly:
1905 GUI
1906 The scroll bar itself.
1907
1908 Fields:
1909 ScrollIndex
1910 A number indicating the current position of the scroll bar.
1911
1912 TotalSpace
1913 A number indicating the total span of the scrollable space.
1914
1915 VisibleSpace
1916 A number indicating the visible span of the scrollable space.
1917
1918 PageIncrement
1919 The amount to increase or decrease the ScrollIndex when ScrollDown or ScrollUp is called.
1920
1921 Methods:
1922 Update ( )
1923 Updates the scroll bar to reflect any changes.
1924
1925 CanScrollDown ( )
1926 Returns whether the scroll bar can scroll down (or right if `horizontal` is true).
1927
1928 CanScrollRight ( )
1929 Alias of CanScrollDown.
1930
1931 CanScrollUp ( )
1932 Returns whether the scroll bar can scroll up (or left if `horizontal` is true).
1933
1934 CanScrollLeft ( )
1935 Alias of CanScrollUp.
1936
1937 ScrollDown ( )
1938 Scrolls down (or right) by the current PageIncrement.
1939
1940 ScrollRight( )
1941 Alias of ScrollDown.
1942
1943 ScrollUp ( )
1944 Scrolls up (or left) by the current PageIncrement.
1945
1946 ScrollLeft ( )
1947 Alias of ScrollUp.
1948
1949 ScrollTo ( number `index` )
1950 Scrolls to a specific place, specified by `index`.
1951 This may be any number; it will be clamped between 0 and TotalSpace.
1952
1953 GetScrollPercent ( )
1954 Returns the scroll index as a percentage between 0 and 1.
1955
1956 SetScrollPercent ( number `percent` )
1957 Sets the ScrollIndex as a percentage between 0 and 1.
1958
1959 Destroy ( )
1960 Releases the resources used by this object.
1961 Run this if you're no longer using this object.
1962
1963 Callbacks:
1964 UpdateCallback ( table `object` )
1965 When Update is called, the function is called before updating.
1966 If the function returns false, the update will be canceled.
1967]==]
1968
1969local function CreateScrollBar(horizontal,size)
1970 size = size or ENTRY_SIZE
1971
1972 -- create row scroll bar
1973 local ScrollFrame = Create'Frame'{
1974 Size = horizontal and UDim2.new(1,0,0,size) or UDim2.new(0,size,1,0);
1975 Position = horizontal and UDim2.new(0,0,1,-size) or UDim2.new(1,-size,0,0);
1976 BackgroundTransparency = 1;
1977 Name = "ScrollFrame";
1978 Create'ImageButton'{
1979 BackgroundColor3 = Color3.new(1,1,1);
1980 BackgroundTransparency = 0.7;
1981 BorderSizePixel = 0;
1982 Size = UDim2.new(0, size, 0, size);
1983 Name = "ScrollDown";
1984 Position = horizontal and UDim2.new(1,-size,0,0) or UDim2.new(0,0,1,-size);
1985 };
1986 Create'ImageButton'{
1987 BackgroundColor3 = Color3.new(1,1,1);
1988 BackgroundTransparency = 0.7;
1989 BorderSizePixel = 0;
1990 Size = UDim2.new(0, size, 0, size);
1991 Name = "ScrollUp";
1992 };
1993 Create'ImageButton'{
1994 AutoButtonColor = false;
1995 Size = horizontal and UDim2.new(1,-size*2,1,0) or UDim2.new(1,0,1,-size*2);
1996 BackgroundColor3 = Color3.new(0,0,0);
1997 BorderSizePixel = 0;
1998 BackgroundTransparency = 0.7;
1999 Position = horizontal and UDim2.new(0,size,0,0) or UDim2.new(0,0,0,size);
2000 Name = "ScrollBar";
2001 Create'ImageButton'{
2002 BorderSizePixel = 0;
2003 BackgroundColor3 = Color3.new(1,1,1);
2004 Size = UDim2.new(0, size, 0, size);
2005 BackgroundTransparency = 0.5;
2006 Name = "ScrollThumb";
2007 };
2008 };
2009 }
2010
2011 local ScrollDownFrame = ScrollFrame.ScrollDown
2012 local ScrollDownGraphic = CreateGraphic(horizontal and "arrow-right" or "arrow-down",Vector2.new(size,size))
2013 ScrollDownGraphic.GUI.Parent = ScrollDownFrame
2014 local ScrollUpFrame = ScrollFrame.ScrollUp
2015 local ScrollUpGraphic = CreateGraphic(horizontal and "arrow-left" or "arrow-up",Vector2.new(size,size))
2016 ScrollUpGraphic.GUI.Parent = ScrollUpFrame
2017 local ScrollBarFrame = ScrollFrame.ScrollBar
2018 local ScrollThumbFrame = ScrollBarFrame.ScrollThumb
2019 local Decal = CreateGraphic(horizontal and "vgrip" or "grip",Vector2.new(4),{BackgroundColor3=Color3.new(0,0,0),BackgroundTransparency=0.5})
2020 Decal.GUI.Position = UDim2.new(0.5,-4,0.5,-4)
2021 Decal.GUI.Parent = ScrollThumbFrame
2022
2023 local MouseDrag = Create'ImageButton'{
2024 Active = false;
2025 Size = UDim2.new(1.5, 0, 1.5, 0);
2026 AutoButtonColor = false;
2027 BackgroundTransparency = 1;
2028 Name = "MouseDrag";
2029 Position = UDim2.new(-0.25, 0, -0.25, 0);
2030 ZIndex = 10;
2031 }
2032
2033 local Class = {
2034 GUI = ScrollFrame;
2035 ScrollIndex = 0;
2036 VisibleSpace = 0;
2037 TotalSpace = 0;
2038 PageIncrement = 1;
2039 }
2040
2041 local function GetScrollPercent()
2042 return Class.ScrollIndex/(Class.TotalSpace-Class.VisibleSpace)
2043 end
2044 Class.GetScrollPercent = GetScrollPercent
2045
2046 local function CanScrollDown()
2047 return Class.ScrollIndex + Class.VisibleSpace < Class.TotalSpace
2048 end
2049 Class.CanScrollDown = CanScrollDown
2050 Class.CanScrollRight = CanScrollDown
2051
2052 local function CanScrollUp()
2053 return Class.ScrollIndex > 0
2054 end
2055 Class.CanScrollUp = CanScrollUp
2056 Class.CanScrollLeft = CanScrollUp
2057
2058 local ScrollStyle = {BackgroundColor3=Color3.new(0,0,0),BackgroundTransparency=0}
2059 local ScrollStyle_ds = {BackgroundColor3=Color3.new(0,0,0),BackgroundTransparency=0.7}
2060
2061 local last_down
2062 local last_up
2063 local UpdateScrollThumb = horizontal
2064 and function()
2065 ScrollThumbFrame.Size = UDim2.new(Class.VisibleSpace/Class.TotalSpace,0,0,size)
2066 if ScrollThumbFrame.AbsoluteSize.x < size then
2067 ScrollThumbFrame.Size = UDim2.new(0,size,0,size)
2068 end
2069 local bar_size = ScrollBarFrame.AbsoluteSize.x
2070 ScrollThumbFrame.Position = UDim2.new(GetScrollPercent()*(bar_size - ScrollThumbFrame.AbsoluteSize.x)/bar_size,0,0,0)
2071 end
2072 or function()
2073 ScrollThumbFrame.Size = UDim2.new(0,size,Class.VisibleSpace/Class.TotalSpace,0)
2074 if ScrollThumbFrame.AbsoluteSize.y < size then
2075 ScrollThumbFrame.Size = UDim2.new(0,size,0,size)
2076 end
2077 local bar_size = ScrollBarFrame.AbsoluteSize.y
2078 ScrollThumbFrame.Position = UDim2.new(0,0,GetScrollPercent()*(bar_size - ScrollThumbFrame.AbsoluteSize.y)/bar_size,0)
2079 end
2080
2081 local function Update()
2082 local t = Class.TotalSpace
2083 local v = Class.VisibleSpace
2084 local s = Class.ScrollIndex
2085 if v <= t then
2086 if s > 0 then
2087 if s + v > t then
2088 Class.ScrollIndex = t - v
2089 end
2090 else
2091 Class.ScrollIndex = 0
2092 end
2093 else
2094 Class.ScrollIndex = 0
2095 end
2096
2097 if Class.UpdateCallback then
2098 if Class.UpdateCallback(Class) == false then
2099 return
2100 end
2101 end
2102
2103 local down = CanScrollDown()
2104 local up = CanScrollUp()
2105 if down ~= last_down then
2106 last_down = down
2107 ScrollDownFrame.Active = down
2108 ScrollDownFrame.AutoButtonColor = down
2109 ScrollDownGraphic.Stylist.SetProperties(down and ScrollStyle or ScrollStyle_ds)
2110 ScrollDownFrame.BackgroundTransparency = down and 0.5 or 0.7
2111 end
2112 if up ~= last_up then
2113 last_up = up
2114 ScrollUpFrame.Active = up
2115 ScrollUpFrame.AutoButtonColor = up
2116 ScrollUpGraphic.Stylist.SetProperties(up and ScrollStyle or ScrollStyle_ds)
2117 ScrollUpFrame.BackgroundTransparency = up and 0.5 or 0.7
2118 end
2119 ScrollThumbFrame.Visible = down or up
2120 UpdateScrollThumb()
2121 end
2122 Class.Update = Update
2123
2124 local function ScrollDown()
2125 Class.ScrollIndex = Class.ScrollIndex + Class.PageIncrement
2126 Update()
2127 end
2128 Class.ScrollDown = ScrollDown
2129 Class.ScrollRight = ScrollDown
2130
2131 local function ScrollUp()
2132 Class.ScrollIndex = Class.ScrollIndex - Class.PageIncrement
2133 Update()
2134 end
2135 Class.ScrollUp = ScrollUp
2136 Class.ScrollLeft = ScrollUp
2137
2138 local function ScrollTo(index)
2139 Class.ScrollIndex = index
2140 Update()
2141 end
2142 Class.ScrollTo = ScrollTo
2143
2144 local function SetScrollPercent(percent)
2145 Class.ScrollIndex = math.floor((Class.TotalSpace - Class.VisibleSpace)*percent + 0.5)
2146 Update()
2147 end
2148 Class.SetScrollPercent = SetScrollPercent
2149
2150 -- fixes AutoButtonColor
2151 local function ResetButtonColor(button)
2152 local active = button.Active
2153 button.Active = not active
2154 button.Active = active
2155 end
2156
2157 SetZIndexOnChanged(ScrollFrame)
2158
2159 local scroll_event_id = 0
2160 ScrollDownFrame.MouseButton1Down:connect(function()
2161 scroll_event_id = tick()
2162 local current = scroll_event_id
2163 local up_con
2164 up_con = MouseDrag.MouseButton1Up:connect(function()
2165 scroll_event_id = tick()
2166 MouseDrag.Parent = nil
2167 ResetButtonColor(ScrollDownFrame)
2168 up_con:disconnect(); drag = nil
2169 end)
2170 MouseDrag.Parent = GetScreen(ScrollFrame)
2171 ScrollDown()
2172 wait(0.2) -- delay before auto scroll
2173 while scroll_event_id == current do
2174 ScrollDown()
2175 if not CanScrollDown() then break end
2176 wait()
2177 end
2178 end)
2179
2180 ScrollDownFrame.MouseButton1Up:connect(function()
2181 scroll_event_id = tick()
2182 end)
2183
2184 ScrollUpFrame.MouseButton1Down:connect(function()
2185 scroll_event_id = tick()
2186 local current = scroll_event_id
2187 local up_con
2188 up_con = MouseDrag.MouseButton1Up:connect(function()
2189 scroll_event_id = tick()
2190 MouseDrag.Parent = nil
2191 ResetButtonColor(ScrollUpFrame)
2192 up_con:disconnect(); drag = nil
2193 end)
2194 MouseDrag.Parent = GetScreen(ScrollFrame)
2195 ScrollUp()
2196 wait(0.2)
2197 while scroll_event_id == current do
2198 ScrollUp()
2199 if not CanScrollUp() then break end
2200 wait()
2201 end
2202 end)
2203
2204 ScrollUpFrame.MouseButton1Up:connect(function()
2205 scroll_event_id = tick()
2206 end)
2207
2208 ScrollBarFrame.MouseButton1Down:connect(horizontal
2209 and function(x,y)
2210 scroll_event_id = tick()
2211 local current = scroll_event_id
2212 local up_con
2213 up_con = MouseDrag.MouseButton1Up:connect(function()
2214 scroll_event_id = tick()
2215 MouseDrag.Parent = nil
2216 ResetButtonColor(ScrollUpFrame)
2217 up_con:disconnect(); drag = nil
2218 end)
2219 MouseDrag.Parent = GetScreen(ScrollFrame)
2220 if x > ScrollThumbFrame.AbsolutePosition.x then
2221 ScrollTo(Class.ScrollIndex + Class.VisibleSpace)
2222 wait(0.2)
2223 while scroll_event_id == current do
2224 if x < ScrollThumbFrame.AbsolutePosition.x + ScrollThumbFrame.AbsoluteSize.x then break end
2225 ScrollTo(Class.ScrollIndex + Class.VisibleSpace)
2226 wait()
2227 end
2228 else
2229 ScrollTo(Class.ScrollIndex - Class.VisibleSpace)
2230 wait(0.2)
2231 while scroll_event_id == current do
2232 if x > ScrollThumbFrame.AbsolutePosition.x then break end
2233 ScrollTo(Class.ScrollIndex - Class.VisibleSpace)
2234 wait()
2235 end
2236 end
2237 end
2238 or function(x,y)
2239 scroll_event_id = tick()
2240 local current = scroll_event_id
2241 local up_con
2242 up_con = MouseDrag.MouseButton1Up:connect(function()
2243 scroll_event_id = tick()
2244 MouseDrag.Parent = nil
2245 ResetButtonColor(ScrollUpFrame)
2246 up_con:disconnect(); drag = nil
2247 end)
2248 MouseDrag.Parent = GetScreen(ScrollFrame)
2249 if y > ScrollThumbFrame.AbsolutePosition.y then
2250 ScrollTo(Class.ScrollIndex + Class.VisibleSpace)
2251 wait(0.2)
2252 while scroll_event_id == current do
2253 if y < ScrollThumbFrame.AbsolutePosition.y + ScrollThumbFrame.AbsoluteSize.y then break end
2254 ScrollTo(Class.ScrollIndex + Class.VisibleSpace)
2255 wait()
2256 end
2257 else
2258 ScrollTo(Class.ScrollIndex - Class.VisibleSpace)
2259 wait(0.2)
2260 while scroll_event_id == current do
2261 if y > ScrollThumbFrame.AbsolutePosition.y then break end
2262 ScrollTo(Class.ScrollIndex - Class.VisibleSpace)
2263 wait()
2264 end
2265 end
2266 end)
2267
2268 ScrollThumbFrame.MouseButton1Down:connect(horizontal
2269 and function(x,y)
2270 scroll_event_id = tick()
2271 local mouse_offset = x - ScrollThumbFrame.AbsolutePosition.x
2272 local drag_con
2273 local up_con
2274 drag_con = MouseDrag.MouseMoved:connect(function(x,y)
2275 local bar_abs_pos = ScrollBarFrame.AbsolutePosition.x
2276 local bar_drag = ScrollBarFrame.AbsoluteSize.x - ScrollThumbFrame.AbsoluteSize.x
2277 local bar_abs_one = bar_abs_pos + bar_drag
2278 x = x - mouse_offset
2279 x = x < bar_abs_pos and bar_abs_pos or x > bar_abs_one and bar_abs_one or x
2280 x = x - bar_abs_pos
2281 SetScrollPercent(x/(bar_drag))
2282 end)
2283 up_con = MouseDrag.MouseButton1Up:connect(function()
2284 scroll_event_id = tick()
2285 MouseDrag.Parent = nil
2286 ResetButtonColor(ScrollThumbFrame)
2287 drag_con:disconnect(); drag_con = nil
2288 up_con:disconnect(); drag = nil
2289 end)
2290 MouseDrag.Parent = GetScreen(ScrollFrame)
2291 end
2292 or function(x,y)
2293 scroll_event_id = tick()
2294 local mouse_offset = y - ScrollThumbFrame.AbsolutePosition.y
2295 local drag_con
2296 local up_con
2297 drag_con = MouseDrag.MouseMoved:connect(function(x,y)
2298 local bar_abs_pos = ScrollBarFrame.AbsolutePosition.y
2299 local bar_drag = ScrollBarFrame.AbsoluteSize.y - ScrollThumbFrame.AbsoluteSize.y
2300 local bar_abs_one = bar_abs_pos + bar_drag
2301 y = y - mouse_offset
2302 y = y < bar_abs_pos and bar_abs_pos or y > bar_abs_one and bar_abs_one or y
2303 y = y - bar_abs_pos
2304 SetScrollPercent(y/(bar_drag))
2305 end)
2306 up_con = MouseDrag.MouseButton1Up:connect(function()
2307 scroll_event_id = tick()
2308 MouseDrag.Parent = nil
2309 ResetButtonColor(ScrollThumbFrame)
2310 drag_con:disconnect(); drag_con = nil
2311 up_con:disconnect(); drag = nil
2312 end)
2313 MouseDrag.Parent = GetScreen(ScrollFrame)
2314 end)
2315
2316 Update()
2317
2318 return Class,ScrollFrame
2319end
2320
2321lib.ScrollBar = CreateScrollBar
2322
2323--[[DEPEND:
2324GetPadding.lua;
2325]]
2326
2327doc["StackingFrame"] = [==[
2328StackingFrame ( GuiObject `frame`, bool `horizontal`, bool `alignment` ) [constructor]
2329 returns: StackingFrame `object`, Frame `stacking_frame`
2330
2331Creates a frame that automatically resizes based on the objects it contains.
2332These objects are automatically positioned to stack next to each other.
2333Objects that have their Visible property set to false become ignored.
2334
2335Arguments:
2336 `frame`
2337 If specified, then it will be used as the StackingFrame.
2338 Children that exist in this object beforehand will be added to the StackingFrame automatically.
2339 Use the AddObject function to add children afterwards.
2340 Optional; defaults to a new Frame.
2341
2342 `horizontal`
2343 If true, objects will be positioned horizontally instead of vertically.
2344 Optional; defaults to false
2345
2346 `alignment`
2347 If true, objects will be aligned to the right if vertical (else left), or the bottom if horizontal (else top).
2348 Optional; defaults to false
2349
2350Returns:
2351 `object`
2352 The StackingFrame object.
2353
2354 `stacking_frame`
2355 The stacking frame GUI.
2356
2357
2358StackingFrame Class:
2359 Contains the following members:
2360
2361 Readonly:
2362 GUI
2363 The stacking frame GUI.
2364
2365 List
2366 The table containing the objects in the stacking frame.
2367 Should only be used for ordering items! Use AddObject and RemoveObject accordingly!
2368
2369 Methods:
2370 AddObject ( GuiObject `object`, number `index` )
2371 Adds `object` to the list.
2372 If `index` is specified, the object is added at that position in the list.
2373 Otherwise, it is added to the end.
2374
2375 RemoveObject ( number `index` )
2376 Removes the object at `index` in the list.
2377 `index` can be an object in the list.
2378 `index` can be nil, in which case the last item in the list is used.
2379 `index` is clamped within the range of the list.
2380
2381 MoveObject ( number `index`, number `to` )
2382 Moves the object at `index` to the new index `to`.
2383 `index` and `to` can be objects in the list.
2384 `index` and `to` can be nil, in which case the last item in the list is used.
2385 `index` and `to` are clamped within the range of the list.
2386
2387 GetIndex ( Instance `object` )
2388 Returns the index of `object` in the stacking frame, if it exists there.
2389
2390 SetPadding ( number `padding`, number `border` )
2391 Sets the amount of space between and around children, in pixels.
2392 `padding` is the amount of space between each child.
2393 `border` is the amount of space around all children.
2394
2395 Update ( )
2396 Updates the object.
2397
2398 Destroy ( )
2399 Releases the resources used by this object.
2400 Run this if you're no longer using this object.
2401]==]
2402
2403local function CreateStackingFrame(Frame,horizontal,alignment)
2404 Frame = Frame or Instance.new("Frame")
2405 local children = {}
2406 local connections = {}
2407 local border = 0
2408 local padding = 0
2409 local style_pad = 0
2410 local event_id = 0
2411 local Update
2412
2413 if horizontal then
2414 if alignment then
2415 Update = function()
2416 event_id = event_id + 1; local eid = event_id
2417 local height = 0
2418 local length = 0
2419 for i,child in pairs(children) do
2420 if event_id ~= eid then return end
2421 if child.Visible then
2422 local abs = child.AbsoluteSize
2423 child.Position = UDim2.new(0,length + border,1,-abs.y - border)
2424 height = abs.y > height and abs.y or height
2425 length = length + abs.x + padding
2426 end
2427 end
2428 if event_id ~= eid then return end
2429 if #children > 0 then
2430 Frame.Size = UDim2.new(0,length - padding + border*2 + style_pad,0,height + border*2 + style_pad)
2431 else
2432 Frame.Size = UDim2.new(0,border*2 + style_pad,0,border*2 + style_pad)
2433 end
2434 end
2435 else
2436 Update = function()
2437 event_id = event_id + 1; local eid = event_id
2438 local height = 0
2439 local length = 0
2440 for i,child in pairs(children) do
2441 if event_id ~= eid then return end
2442 if child.Visible then
2443 local abs = child.AbsoluteSize
2444 child.Position = UDim2.new(0,length + border,0,border)
2445 height = abs.y > height and abs.y or height
2446 length = length + abs.x + padding
2447 end
2448 end
2449 if event_id ~= eid then return end
2450 if #children > 0 then
2451 Frame.Size = UDim2.new(0,length - padding + border*2 + style_pad,0,height + border*2 + style_pad)
2452 else
2453 Frame.Size = UDim2.new(0,border*2 + style_pad,0,border*2 + style_pad)
2454 end
2455 end
2456 end
2457 else
2458 if alignment then
2459 Update = function()
2460 event_id = event_id + 1; local eid = event_id
2461 local width = 0
2462 local length = 0
2463 for i,child in pairs(children) do
2464 if event_id ~= eid then return end
2465 if child.Visible then
2466 local abs = child.AbsoluteSize
2467 child.Position = UDim2.new(1,-abs.x - border,0,length + border)
2468 width = abs.x > width and abs.x or width
2469 length = length + abs.y + padding
2470 end
2471 end
2472 if event_id ~= eid then return end
2473 if #children > 0 then
2474 Frame.Size = UDim2.new(0,width + border*2 + style_pad,0,length - padding + border*2 + style_pad)
2475 else
2476 Frame.Size = UDim2.new(0,border*2 + style_pad,0,border*2 + style_pad)
2477 end
2478 end
2479 else
2480 Update = function()
2481 event_id = event_id + 1; local eid = event_id
2482 local width = 0
2483 local length = 0
2484 for i,child in pairs(children) do
2485 if event_id ~= eid then return end
2486 if child.Visible then
2487 local abs = child.AbsoluteSize
2488 child.Position = UDim2.new(0,border,0,length + border)
2489 width = abs.x > width and abs.x or width
2490 length = length + abs.y + padding
2491 end
2492 end
2493 if event_id ~= eid then return end
2494 if #children > 0 then
2495 Frame.Size = UDim2.new(0,width + border*2 + style_pad,0,length - padding + border*2 + style_pad)
2496 else
2497 Frame.Size = UDim2.new(0,border*2 + style_pad,0,border*2 + style_pad)
2498 end
2499 end
2500 end
2501 end
2502
2503 local function SetPadding(pad,bor)
2504 padding = pad or padding
2505 border = bor or border
2506 Update()
2507 end
2508
2509 local function AddObject(object,index)
2510 if object:IsA"GuiObject" then
2511 if type(index) == "number" then
2512 table.insert(children,index,object)
2513 else
2514 table.insert(children,object)
2515 end
2516 connections[object] = object.Changed:connect(function(p)
2517 if p == "AbsoluteSize" or p == "Visible" then
2518 Update()
2519 end
2520 end)
2521 object.Parent = Frame
2522 Update()
2523 end
2524 end
2525
2526 local function RemoveObject(index)
2527 if index == nil then
2528 index = #children
2529 elseif type(index) ~= "number" then
2530 index = GetIndex(children,index)
2531 end
2532 if index then
2533 index = ClampIndex(children,index)
2534 local object = table.remove(children,index)
2535 if connections[object] then
2536 connections[object]:disconnect()
2537 connections[object] = nil
2538 end
2539 object.Parent = nil
2540 Update()
2541 return object
2542 end
2543 end
2544
2545 local function MoveObject(index,to)
2546 if index == nil then
2547 index = #children
2548 elseif type(index) ~= "number" then
2549 index = GetIndex(children,index)
2550 end
2551 if to == nil then
2552 to = #children
2553 elseif type(to) ~= "number" then
2554 to = GetIndex(children,to)
2555 end
2556 if index and to then
2557 index = ClampIndex(children,index)
2558 to = ClampIndex(children,to)
2559 local child = table.remove(children,index)
2560 table.insert(children,to,child)
2561 Update()
2562 end
2563 end
2564
2565 local Class = {
2566 GUI = Frame;
2567 List = children;
2568 Update = Update;
2569 SetPadding = SetPadding;
2570 AddObject = AddObject;
2571 RemoveObject = RemoveObject;
2572 MoveObject = MoveObject;
2573 GetIndex = function(object)
2574 return GetIndex(children,object)
2575 end;
2576 }
2577
2578 local function Destroy()
2579 for i,v in pairs(children) do
2580 if connections[v] then
2581 connections[v]:disconnect()
2582 connections[v] = nil
2583 end
2584 v.Parent = nil
2585 children[i] = nil
2586 end
2587 for i,con in pairs(connections) do
2588 con:disconnect()
2589 connections[i] = nil
2590 end
2591 for k in pairs(Class) do
2592 Class[k] = nil
2593 end
2594 Frame:Destroy()
2595 end
2596 Class.Destroy = Destroy
2597
2598 for i,child in pairs(Frame:GetChildren()) do
2599 AddObject(child,i)
2600 end
2601
2602 Update()
2603
2604 Frame.Changed:connect(function(p)
2605 if p == "AbsoluteSize" or p == "Style" then
2606 local old = style_pad
2607 style_pad = GetPadding(Frame)*2
2608 if style_pad ~= old then
2609 Update()
2610 end
2611 end
2612 end)
2613
2614 return Class,Frame
2615end
2616
2617lib.StackingFrame = CreateStackingFrame
2618
2619--[[DEPEND:
2620ScrollBar.lua;
2621]]
2622
2623doc["ScrollingList"] = [==[
2624ScrollingList ( table `list`, number `entry_height` ) [constructor]
2625 returns: ScrollingList `object`, Frame `scrolling_frame`
2626
2627Creates a scrollable list designed to display a large number of items.
2628
2629Arguments:
2630 `list`
2631 Contains the items to display in the list.
2632 Items will be converted to a string before being displayed, so this may contain any type of value.
2633 Optional; defaults to an empty table
2634
2635 `entry_height`
2636 Specifies the height, in pixels, of each displayed entry.
2637 Optional; defaults to ]==]..ENTRY_SIZE..[==[
2638
2639Returns:
2640 `object`
2641 The ScrollingList object.
2642
2643 `scrolling_frame`
2644 The scrolling list GUI.
2645
2646
2647ScrollingList Class:
2648 This class contains the folloing members.
2649 Readonly:
2650 List
2651 The `list` table.
2652
2653 GUI
2654 The scrolling list GUI.
2655
2656 Scroll
2657 A object for the list's scroll bar.
2658
2659 EntryStylist
2660 A stylist containing every displayed entry.
2661
2662 Methods:
2663 AddEntry ( * `item`, number `index` )
2664 Add an entry to the list and updates automatically.
2665 `item` is the value to add to the list.
2666 If `index` is specified, `item` will be added to the list at `index`.
2667 Otherwise, it will be added to the end.
2668
2669 AddEntries ( table `items`, number `index` )
2670 Adds a group of entries to the list.
2671 `items` is a table of values that will be added to the list.
2672 If `index` is specified, the items will be inserted into the list starting at `index.
2673 Otherwise, they will be added to the end.
2674
2675 RemoveEntry ( * `item` )
2676 Removes the first occurance of `item` in the list.
2677 If `item` is a number, the item at that index in the list will be removed.
2678 If `item` is not specified, then the list item in the list will be removed.
2679
2680 Update ( )
2681 Updates the display to reflect the list.
2682
2683 Destroy ( )
2684 Releases the resources used by this object.
2685 Run this if you're no longer using this object.
2686]==]
2687
2688local function CreateScrollingList(List,entryHeight)
2689 List = List or {}
2690 entryHeight = entyHeight or ENTRY_SIZE
2691
2692 local ScrollingListFrame = Instance.new("Frame")
2693 ScrollingListFrame.Size = UDim2.new(0,300,0,200)
2694 ScrollingListFrame.Style = Enum.FrameStyle.RobloxRound
2695 ScrollingListFrame.Active = true
2696 ScrollingListFrame.Name = "ScrollingListFrame"
2697
2698 local ListViewFrame = Instance.new("Frame",ScrollingListFrame)
2699 ListViewFrame.Name = "ListViewFrame"
2700 ListViewFrame.BackgroundTransparency = 1
2701 ListViewFrame.Size = UDim2.new(1,-entryHeight,1,0)
2702
2703 local EntryStylist = Stylist{
2704 Name = "ListEntry";
2705 Font = "ArialBold";
2706 FontSize = "Size14";
2707 TextColor3 = Color3.new(1,1,1);
2708 BackgroundTransparency = 1;
2709 TextXAlignment = "Left";
2710 }
2711 local EntryTemplate = Instance.new("TextLabel")
2712
2713 local EntryFrames = {}
2714
2715 local Scroll,ScrollBarFrame = CreateScrollBar(false,entryHeight)
2716 ScrollBarFrame.Size = UDim2.new(0,entryHeight,1,0)
2717 ScrollBarFrame.Position = UDim2.new(1,-entryHeight,0,0)
2718 ScrollBarFrame.Parent = ScrollingListFrame
2719
2720 local Update = Scroll.Update
2721
2722 local Class = {
2723 List = List;
2724 GUI = ScrollingListFrame;
2725 Scroll = Scroll;
2726 Update = Update;
2727 EntryStylist = EntryStylist;
2728 }
2729
2730 Scroll.UpdateCallback = function()
2731 local visible_space = Scroll.VisibleSpace
2732 -- update current entries
2733 for i = 1,visible_space do
2734 local item = List[i + Scroll.ScrollIndex]
2735 if item then
2736 local entry = EntryFrames[i]
2737 if not entry then
2738 entry = EntryTemplate:Clone()
2739 EntryStylist.AddObject(entry)
2740 EntryFrames[i] = entry
2741 entry.Parent = ListViewFrame
2742 entry.ZIndex = ScrollingListFrame.ZIndex
2743 end
2744 entry.Text = tostring(item)
2745 entry.Position = UDim2.new(0,0,0,(i-1)*entryHeight)
2746 entry.Size = UDim2.new(1,0,0,entryHeight)
2747 else
2748 local entry = EntryFrames[i]
2749 if entry then
2750 EntryStylist.RemoveObject(entry)
2751 entry:Destroy()
2752 EntryFrames[i] = nil
2753 end
2754 end
2755 end
2756 -- remove extra entries (occurs only when #EntryFrames > VisibleSpace)
2757 for i = Scroll.VisibleSpace+1,#EntryFrames do
2758 local entry = EntryFrames[i]
2759 if entry then
2760 EntryStylist.RemoveObject(entry)
2761 entry:Destroy()
2762 end
2763 EntryFrames[i] = nil
2764 end
2765 end
2766
2767 -- add an item to the list; optional list index
2768 local function AddEntry(item,index)
2769 if index then
2770 table.insert(List,index,item)
2771 else
2772 table.insert(List,item)
2773 end
2774 Scroll.TotalSpace = #List
2775 Update()
2776 end
2777 Class.AddEntry = AddEntry
2778
2779 -- add multiple items to list
2780 local function AddEntries(items,index)
2781 if index then
2782 for i = 1,#items do
2783 table.insert(List,index+i-1,items[i])
2784 end
2785 else
2786 for i = 1,#items do
2787 table.insert(List,items[i])
2788 end
2789 end
2790 Scroll.TotalSpace = #List
2791 Update()
2792 end
2793 Class.AddEntries = AddEntries
2794
2795 -- remove entry from list; may be a list index or an item in the list
2796 local function RemoveEntry(item)
2797 if type(item) == "number" or type(item) == "nil" then
2798 table.remove(List,item)
2799 else
2800 for i,v in pairs(List) do
2801 if v == item then
2802 table.remove(List,i)
2803 break
2804 end
2805 end
2806 end
2807 Scroll.TotalSpace = #List
2808 Update()
2809 end
2810 Class.RemoveEntry = RemoveEntry
2811
2812 SetZIndexOnChanged(ScrollingListFrame)
2813
2814 ListViewFrame.Changed:connect(function(p)
2815 if p == "AbsoluteSize" then
2816 Scroll.VisibleSpace = math.floor(ListViewFrame.AbsoluteSize.y/entryHeight)
2817 Update()
2818 end
2819 end)
2820
2821 function Class.Destroy()
2822 for i in pairs(Class) do
2823 Class[i] = nil
2824 end
2825 for i,v in pairs(EntryFrames) do
2826 v:Destroy()
2827 EntryFrames[i] = nil
2828 end
2829 EntryStylist.Destroy()
2830 Scroll.Destroy()
2831 ScrollingListFrame:Destroy()
2832 end
2833
2834 return Class,ScrollingListFrame
2835end
2836
2837lib.ScrollingList = CreateScrollingList
2838
2839--[[DEPEND:
2840ScrollBar.lua;
2841]]
2842
2843doc["ScrollingContainer"] = [==[
2844ScrollingContainer ( bool `v_scroll_bar`, bool `h_scroll_bar`, number `scroll_width` ) [constructor]
2845 returns: ScrollingContainer `object`, Frame `scrolling_container`
2846
2847Creates a container that can be scrolled with one or more scroll bars.
2848The scroll bars update dynamically based on the size of the boundary and container.
2849Objects in the container are automatically clipped to display only within the boundary.
2850
2851Arguments:
2852 `v_scroll_bar`
2853 Indicates whether the container should have a vertical scroll bar.
2854 Optional; defaults to true
2855
2856 `h_scroll_bar`
2857 Indicates whether the container should have a horizontal scroll bar.
2858 Optional; defaults to false
2859
2860 `scroll_width`
2861 Indicates the width the scrollbar(s).
2862 Optional; defaults to ]==]..ENTRY_SIZE..[==[
2863
2864Returns:
2865 `object`
2866 The ScrollingContainer object.
2867
2868 `scrolling_container`
2869 The scrolling container GUI.
2870
2871
2872ScrollingContainer Class:
2873 This class contains the following members:
2874
2875 Readonly:
2876 Boundary
2877 A Frame that represents the visible area, clipping off any overflowing content.
2878
2879 Container
2880 A Frame that will contain other items to be displayed in the scrolling container.
2881
2882 GUI
2883 The scrolling container GUI.
2884
2885 HScroll
2886 The horizontal scroll bar object (if available).
2887
2888 VScroll
2889 The vertical scroll bar object (if available).
2890
2891 Methods:
2892 Update ( )
2893 Updates the scroll bar (or both, if present).
2894]==]
2895
2896local function CreateScrollingContainer(v_scroll,h_scroll,scroll_width)
2897 if v_scroll == nil then v_scroll = true end
2898 scroll_width = scroll_width or ENTRY_SIZE
2899
2900 local ParentFrame = Create'Frame'{
2901 Name = "ScrollingContainer";
2902 Size = UDim2.new(0,300,0,200);
2903 BackgroundTransparency = 1;
2904 }
2905
2906 local Boundary = Create'Frame'{
2907 Name = "Boundary";
2908 BackgroundColor3 = Color3.new(0,0,0);
2909 BorderColor3 = Color3.new(1,1,1);
2910 ClipsDescendants = true;
2911 Parent = ParentFrame;
2912 }
2913
2914 local Container = Create'Frame'{
2915 Name = "Container";
2916 BackgroundTransparency = 1;
2917 Parent = Boundary;
2918 }
2919
2920 local Class = {
2921 GUI = ParentFrame;
2922 Boundary = Boundary;
2923 Container = Container;
2924 }
2925
2926 if v_scroll and h_scroll then
2927 local VScroll = CreateScrollBar(false,scroll_width)
2928 VScroll.PageIncrement = scroll_width
2929 VScroll.GUI.Position = UDim2.new(1,-scroll_width,0,0)
2930 VScroll.GUI.Size = UDim2.new(0,scroll_width,1,-scroll_width)
2931 VScroll.GUI.Parent = ParentFrame
2932 local VUpdate = VScroll.Update
2933 VScroll.UpdateCallback = function()
2934 Container.Position = UDim2.new(0,Container.Position.X.Offset,0,-VScroll.ScrollIndex)
2935 end
2936
2937 local HScroll = CreateScrollBar(true,scroll_width)
2938 HScroll.PageIncrement = scroll_width
2939 HScroll.GUI.Position = UDim2.new(0,0,1,-scroll_width)
2940 HScroll.GUI.Size = UDim2.new(1,-scroll_width,0,scroll_width)
2941 HScroll.GUI.Parent = ParentFrame
2942 local HUpdate = HScroll.Update
2943 HScroll.UpdateCallback = function()
2944 Container.Position = UDim2.new(0,-HScroll.ScrollIndex,0,Container.Position.Y.Offset)
2945 end
2946
2947 Boundary.Size = UDim2.new(1,-scroll_width,1,-scroll_width)
2948
2949 local function Update()
2950 VUpdate()
2951 HUpdate()
2952 end
2953
2954 local function SizeChanged(p)
2955 if p == "AbsoluteSize" then
2956 VScroll.TotalSpace = Container.AbsoluteSize.y
2957 VScroll.VisibleSpace = Boundary.AbsoluteSize.y
2958 HScroll.TotalSpace = Container.AbsoluteSize.x
2959 HScroll.VisibleSpace = Boundary.AbsoluteSize.x
2960 Update()
2961 end
2962 end
2963 Boundary.Changed:connect(SizeChanged)
2964 Container.Changed:connect(SizeChanged)
2965 Class.VScroll = VScroll
2966 Class.HScroll = HScroll
2967 Class.Update = Update
2968 SizeChanged("AbsoluteSize")
2969 Update()
2970 elseif v_scroll then
2971 local Scroll = CreateScrollBar(false,scroll_width)
2972 Scroll.PageIncrement = scroll_width
2973 Scroll.GUI.Position = UDim2.new(1,-scroll_width,0,0)
2974 Scroll.GUI.Size = UDim2.new(0,scroll_width,1,0)
2975 Scroll.GUI.Parent = ParentFrame
2976 local Update = Scroll.Update
2977 Scroll.UpdateCallback = function()
2978 Container.Position = UDim2.new(0,Container.Position.X.Offset,0,-Scroll.ScrollIndex)
2979 end
2980 local function SizeChanged(p)
2981 if p == "AbsoluteSize" then
2982 Scroll.TotalSpace = Container.AbsoluteSize.y
2983 Scroll.VisibleSpace = Boundary.AbsoluteSize.y
2984 Update()
2985 end
2986 end
2987 Boundary.Changed:connect(SizeChanged)
2988 Container.Changed:connect(SizeChanged)
2989 Class.VScroll = Scroll
2990 Class.Update = Update
2991 SizeChanged("AbsoluteSize")
2992 Update()
2993 elseif h_scroll then
2994 local Scroll = CreateScrollBar(true,scroll_width)
2995 Scroll.PageIncrement = scroll_width
2996 Scroll.GUI.Position = UDim2.new(0,0,1,-scroll_width)
2997 Scroll.GUI.Size = UDim2.new(1,0,0,scroll_width)
2998 Scroll.GUI.Parent = ParentFrame
2999 local Update = Scroll.Update
3000 Scroll.UpdateCallback = function()
3001 Container.Position = UDim2.new(0,-Scroll.ScrollIndex,0,Container.Position.Y.Offset)
3002 end
3003 local function SizeChanged(p)
3004 if p == "AbsoluteSize" then
3005 Scroll.TotalSpace = Container.AbsoluteSize.x
3006 Scroll.VisibleSpace = Boundary.AbsoluteSize.x
3007 Update()
3008 end
3009 end
3010 Boundary.Changed:connect(SizeChanged)
3011 Container.Changed:connect(SizeChanged)
3012 Class.HScroll = Scroll
3013 Class.Update = Update
3014 SizeChanged("AbsoluteSize")
3015 Update()
3016 end
3017
3018 return Class,ParentFrame
3019end
3020
3021lib.ScrollingContainer = CreateScrollingContainer
3022
3023--[[DEPEND:
3024SetZIndex.lua;
3025Stylist.lua;
3026Graphic.lua;
3027ScrollBar.lua;
3028]]
3029
3030doc["DetailedList"] = [==[
3031DetailedList ( table `row_data_list`, table `column_scheme`, number `row_height` ) [constructor]
3032 returns: DetailedList `object`, Frame `list_frame`
3033
3034Creates a customizable list for displaying data.
3035
3036Arguments:
3037 `row_data_list`
3038 Holds all the data to be displayed.
3039 It contains tables that hold data for each row in the list (see Row Data).
3040 Optional; defaults to an empty table.
3041
3042 `column_scheme`
3043 Contains information for how each column will be displayed (see Column Scheme).
3044
3045 `row_height`
3046 Sets the height of each row, in pixels.
3047 Optional; defaults to ]==]..ENTRY_SIZE..[==[
3048
3049Returns:
3050 `object`
3051 The DetailedList object.
3052
3053 `list_frame`
3054 The DetailedList GUI.
3055
3056
3057DetailedList Class:
3058 This class contains the following members:
3059
3060 Readonly:
3061 GUI
3062 The DetailedList GUI.
3063
3064 Data
3065 The `row_data_list` table.
3066
3067 Scroll
3068 A class for the list's scroll bar.
3069
3070 Stylist
3071 A table that contains Stylist classes for controlling the appearance of the DetailedList.
3072 It contains the following values:
3073 Global: Every object in the DetailedList
3074 Cell: Every cell in list
3075 Header: Each cell the top (header) row of the list.
3076 RowSpan: Each cell container of each row.
3077 Rows: A table that contains Stylists for each row in the list, referenced by the row's data table.
3078 Columns: A table that contains Stylists for each column in the list.
3079
3080 Methods:
3081 AddRow ( table `row_data`, number `index`, table `style` )
3082 Adds a new row to the list.
3083 `row_data` is the data to display in the row.
3084 If `index` is specified, then the row will be added to that place in the list, instead of the end.
3085 If `style` is specified, then the row's Stylist will be created with it.
3086 Returns the row's data table.
3087
3088 RemoveRow ( number `index` )
3089 Removes a row from the list.
3090 `index` may be a numerical index in the list, or a row data table in the list.
3091 Returns the removed row's data table.
3092
3093 UpdateRow ( number `index` )
3094 Updates the specified row to reflect the data in `row_data_list`
3095 `index` may be a numerical index in the list, or a row data table in the list.
3096
3097 Update ( )
3098 Updates the display.
3099
3100 Destroy ( )
3101 Releases the resources used by this object.
3102 Run this if you're no longer using this object.
3103
3104
3105Column Scheme:
3106 The column scheme is a list of tables, each representing a column that will appear in the DetailedList.
3107 Each of these tables contain the following entries:
3108 ["type"] = (string)
3109 Indicates the data type of the column.
3110 More entries may be required depending on the type (see Column Scheme Types).
3111 ["name"] = (string)
3112 The name of the column. This will appear in the header at the top.
3113 ["width"] = (UDim)
3114 The width of the column.
3115 ["style"] = (table)
3116 Optional. Defines a custom style for the column.
3117 If defined, then it will become the corresponding stylist in `object`.Stylist.Columns.
3118
3119
3120Row Data:
3121 A row data table holds the data for each cell of a row.
3122 Each entry corresponds to each cell in the row.
3123 Their types should match up with the column scheme (see Column Scheme Types).
3124
3125 Example (for scheme {"check-box", "image", "text"}):
3126 row_data_list = {
3127 {true, "flower.png", "Flowers"};
3128 {false, "beehive.png", "Bees"};
3129 }
3130
3131
3132Column Scheme Types:
3133 Here are the possible data types, and their extra entries:
3134 text
3135 Row Data: string
3136 The text to display in the cell.
3137 Entries: none
3138 image
3139 Row Data: string
3140 The Content string of the image to display in the cell.
3141 Entries: none
3142 text-button
3143 Row Data: string
3144 The text to display in the cell.
3145 Entries:
3146 ["callback"] = function (table `row_data`, table `object`)
3147 Called when the button is clicked.
3148 `row_data` is the button's row data.
3149 `object` is the DetailedList object.
3150 image-button
3151 Row Data: string
3152 The Content string of the image to display in the cell.
3153 Entries:
3154 ["callback"] = function (table `row_data`, table `object`)
3155 Called when the button is clicked.
3156 `row_data` is the button's row data.
3157 `object` is the DetailedList object.
3158 text-field
3159 Row Data: string
3160 The value displayed in the text field.
3161 Entries:
3162 ["callback"] = function (string `text`, table `row_data`, table `object`, bool `entered`)
3163 Called when the field loses focus.
3164 `text` is the field's current text.
3165 `row_data` is the field's row data.
3166 `object` is the DetailedList object.
3167 `entered` is whether the user pressed enter to lose focus.
3168 This function should return a string (usually same as `text`).
3169 If nil or false is returned, then the field will be reverted to the text before the change.
3170 check-box
3171 Row Data: bool
3172 The state of the check box.
3173 Entries:
3174 ["checked"] = string, table
3175 The image (Content string) to display when the box is checked.
3176 If a table, its entries are the arguments to make a new Graphic.
3177 ["unchecked"] = string, table
3178 The image (Content string) to display when the box is unchecked.
3179 If a table, its entries are the arguments to make a new Graphic.
3180 ["callback"] = function (table `row_data`, table `object`)
3181 Called when the check box is clicked.
3182 `row_data` is the button's row data.
3183 `object` is the DetailedList object.
3184 This function should return a bool, indicating if the check box should toggle its state.
3185 drop-list (NOT IMPLEMENTED)
3186 Row Data: string
3187 The value displayed in the drop list.
3188 Entries:
3189 ["items"] = table
3190 A list of items to appear in the drop list.
3191]==]
3192
3193--[==[EXCLUDE:
3194detailed list notes
3195
3196 updating will take much more processing, due to amount of info per row
3197 there aren't going to be millions of rows
3198 so, go back to having frames for each row
3199 but, still use row indexing
3200 on update, stop displaying displayed rows, query new rows, display them
3201
3202 row frames in the RowFramesList table are referenced by data rows in the RowDataList table
3203
3204 PANIC: columns have a fixed width
3205 when updating, resize each cell width using one of the following options:
3206 -> basic: columns have fixed, perminent width, defined by column scheme, calculated once on row addition
3207 detailed view-like: columns have fixed width regardless of row content, recalculated when column tab is resized
3208 table-like: each column width is recalculated based on its content, when a row is added/removed (expensive)
3209
3210 entry scheme:
3211 a table that describes an entry to the list
3212 keys are sequential, describing a part of the entry
3213
3214 Entry Data:
3215 For a cell type, the acceptable entry data type is defined below.
3216 If the entry is a table instead, the first value in that table is the original data,
3217 while other keys defined will set the corresponding property of the cell.
3218 Example:
3219 As Data Type: "A string"
3220 As Table: {"A string", TextColor3 = Color3.new(1,0,0)}
3221 Types:
3222 Column Scheme Definition: Entry Data:
3223 {"text"}; string
3224 {"image"}; string
3225 {"text-button", callback = function}; string
3226 {"image-button", callback = function}; string
3227 {"text-field", callback = function}; string
3228 {"check-box", checked = string, unchecked = string, callback}; bool
3229 {"drop-list", items = table}; string
3230
3231 Each scheme has a 'name' key (string) and a 'width' key (UDim), which define he name and width of the column
3232 Each scheme can also have a 'style' key, who's value is a table that defines properties for each cell as columns
3233]==]
3234
3235local function CreateDetailedList(RowDataList,ColumnScheme,rowHeight)
3236 RowDataList = RowDataList or {}
3237 rowHeight = rowHeight or ENTRY_SIZE
3238 local viewHeight = 0
3239 local numRows = math.floor(viewHeight/rowHeight)
3240 local scrollIndex = 0
3241
3242 local RowFramesList = {} -- holds a list of frames associated with RowDataList entries. This list is independent of RowDataList, and may be resorted
3243 local RowFrameLookup = {} -- [data]=frame references
3244 local RowDataLookup = {} -- [frame]=data references; it would be nice if this didn't have to exist
3245 local DisplayedRows = {} -- a list of currently displayed row frames
3246 local CellMetadata = {} -- extra data associated with a cell
3247
3248 local DetailedListFrame = Create'Frame'{
3249 Size = UDim2.new(0,300,0,200);
3250 BackgroundTransparency = 1;
3251 Create'Frame'{
3252 Name = "ListViewFrame";
3253 BackgroundTransparency = 1;
3254 Size = UDim2.new(1,-rowHeight,1,-rowHeight);
3255 Position = UDim2.new(0,0,0,rowHeight);
3256 };
3257 Create'Frame'{
3258 Name = "ColumnHeaderFrame";
3259 BackgroundTransparency = 1;
3260 Size = UDim2.new(1,-rowHeight,0,rowHeight);
3261 Position = UDim2.new(0,0,0,0);
3262 };
3263 }
3264
3265 local ListViewFrame = DetailedListFrame.ListViewFrame
3266 local ColumnHeaderFrame = DetailedListFrame.ColumnHeaderFrame
3267
3268---- Stylists
3269 local GlobalStylist = CreateStylist{ -- for all objects
3270 TextColor3 = Color3.new(1,1,1);
3271 -- TextXAlignment = Enum.TextXAlignment.Center;
3272 -- TextYAlignment = Enum.TextYAlignment.Center;
3273 TextTransparency = 0;
3274 Font = Enum.Font.ArialBold;
3275 FontSize = Enum.FontSize.Size14;
3276 }
3277 local CellStylist = CreateStylist{ -- for all cells
3278 BackgroundColor3 = Color3.new(0,0,0);
3279 BorderColor3 = Color3.new(1,1,1);
3280 BorderSizePixel = 1;
3281 BackgroundTransparency = 0.7;
3282 }
3283 GlobalStylist.AddStylist(CellStylist)
3284 local HeaderStylist = CreateStylist{ -- for all column headers
3285 BackgroundColor3 = Color3.new(1,1,1);
3286 BorderColor3 = Color3.new(1,1,1);
3287 BorderSizePixel = 1;
3288 BackgroundTransparency = 0.8;
3289 }
3290 GlobalStylist.AddStylist(HeaderStylist)
3291 local RowSpanStylist = CreateStylist{ -- for cell container of each row
3292 BackgroundTransparency = 1;
3293 }
3294 GlobalStylist.AddStylist(RowSpanStylist)
3295 local RowStylists = {} -- list of stylists for each row
3296 local ColumnStylists = {} -- list of stylists for each column
3297
3298 local Scroll,ScrollBarFrame = CreateScrollBar(false,rowHeight)
3299 Modify(ScrollBarFrame){
3300 Size = UDim2.new(0,rowHeight,1,-rowHeight);
3301 Position = UDim2.new(1,-rowHeight,0,rowHeight);
3302 Parent = DetailedListFrame;
3303 }
3304
3305 local Update = Scroll.Update
3306
3307---- DetailedList Class
3308 local Class = {
3309 Data = RowDataList;
3310 GUI = DetailedListFrame;
3311 Stylist = {
3312 Global = GlobalStylist;
3313 Cell = CellStylist;
3314 Header = HeaderStylist;
3315 RowSpan = RowSpanStylist;
3316 Rows = RowStylists;
3317 Columns = ColumnStylists;
3318 };
3319 Update = Update;
3320 }
3321
3322 -- update row display
3323 local event_id = 0
3324 Scroll.UpdateCallback = function()
3325 event_id = event_id + 1
3326 local current_id = event_id
3327 -- stop displaying previous rows
3328 for i,row in pairs(DisplayedRows) do
3329 if event_id ~= current_id then return end
3330 row.Visible = false
3331 DisplayedRows[i] = nil
3332 end
3333 -- query and display current rows
3334 for i = 1,Scroll.VisibleSpace do
3335 if event_id ~= current_id then return end
3336 local row = RowFramesList[i + Scroll.ScrollIndex]
3337 if row then
3338 DisplayedRows[#DisplayedRows+1] = row
3339 row.Position = UDim2.new(0,0,0,(i-1)*rowHeight)
3340 row.Size = UDim2.new(1,0,0,rowHeight)
3341 row.Visible = true
3342 end
3343 end
3344 end
3345
3346 ListViewFrame.Changed:connect(function(p)
3347 if p == "AbsoluteSize" then
3348 Scroll.VisibleSpace = math.floor(ListViewFrame.AbsoluteSize.y/rowHeight)
3349 Update()
3350 end
3351 end)
3352
3353---- Row Sorting
3354 local SortGraphic = Create'Frame'{
3355 Name = "SortGraphic";
3356 BackgroundTransparency = 1;
3357 Size = UDim2.new(0,rowHeight,0,rowHeight);
3358 Position = UDim2.new(1,-rowHeight*0.75,0.5,-rowHeight/8);
3359 }
3360 local GraphicTextAlias = {["TextColor3"]="BackgroundColor3";["TextTransparency"]="BackgroundTransparency";["BorderSizePixel"]=""}
3361
3362 local SortUpG,SortUp = CreateGraphic("arrow-up",Vector2.new(rowHeight,rowHeight))
3363 GlobalStylist.AddStylist(SortUpG.Stylist,GraphicTextAlias)
3364 SortUp.Visible = false
3365 SortUp.Parent = SortGraphic
3366
3367 local SortDownG,SortDown = CreateGraphic("arrow-down",Vector2.new(rowHeight,rowHeight))
3368 GlobalStylist.AddStylist(SortDownG.Stylist,GraphicTextAlias)
3369 SortDown.Visible = false
3370 SortDown.Parent = SortGraphic
3371
3372 -- sets the direction (up or down) and parent (column header)
3373 local function SetSortGraphic(direction,parent)
3374 if parent then
3375 if parent.TextXAlignment == Enum.TextXAlignment.Right then
3376 SortGraphic.Position = UDim2.new(0,0,0,0)
3377 else
3378 SortGraphic.Position = UDim2.new(1,-rowHeight,0,0)
3379 end
3380 end
3381 if direction > 0 then
3382 SortUp.Visible = true
3383 SortDown.Visible = false
3384 if SortGraphic.ZIndex ~= parent.ZIndex then
3385 SetZIndex(SortGraphic,parent.ZIndex)
3386 end
3387 SortGraphic.Parent = parent
3388 elseif direction < 0 then
3389 SortUp.Visible = false
3390 SortDown.Visible = true
3391 if SortGraphic.ZIndex ~= parent.ZIndex then
3392 SetZIndex(SortGraphic,parent.ZIndex)
3393 end
3394 SortGraphic.Parent = parent
3395 else
3396 SortUp.Visible = false
3397 SortDown.Visible = false
3398 SortGraphic.Parent = nil
3399 end
3400 end
3401
3402 -- sorts a column (at index) by a sort type (ascending/descending/none)
3403 -- will eventually be added to class
3404 local function SortColumn(index,sort_type)
3405 -- re-sort to original sorting
3406 for i,data in pairs(RowDataList) do
3407 RowFramesList[i] = RowFrameLookup[data]
3408 end
3409 local header = ColumnHeaderFrame:GetChildren()[index] -- eww
3410 SetSortGraphic(0)
3411 -- sort depending on type, if provided
3412 if sort_type == SORT.ASCENDING then
3413 table.sort(RowFramesList,function(a,b)
3414 local adata,bdata = RowDataLookup[a][index],RowDataLookup[b][index]
3415 -- a and b should always have the same type
3416 local t = type(adata)
3417 if t == "table" then
3418 adata,bdata = adata[1],bdata[1]
3419 t = type(adata)
3420 end
3421 if t == "boolean" then
3422 return tostring(adata) > tostring(bdata)
3423 elseif t == "number" or t == "string" then
3424 return adata < bdata
3425 else
3426 return tostring(adata) < tostring(bdata)
3427 end
3428 end)
3429 SetSortGraphic(1,header)
3430 elseif sort_type == SORT.DESCENDING then
3431 table.sort(RowFramesList,function(a,b)
3432 local adata,bdata = RowDataLookup[a][index],RowDataLookup[b][index]
3433 -- a and b should always have the same type
3434 local t = type(adata)
3435 if t == "table" then
3436 adata,bdata = adata[1],bdata[1]
3437 t = type(adata)
3438 end
3439 if t == "boolean" then
3440 return tostring(adata) < tostring(bdata)
3441 elseif t == "number" or t == "string" then
3442 return adata > bdata
3443 else
3444 return tostring(adata) > tostring(bdata)
3445 end
3446 end)
3447 local header = ColumnHeaderFrame:GetChildren()[index]
3448 SetSortGraphic(-1,header)
3449 end
3450 Update()
3451 end
3452
3453---- Initialize column scheme
3454 local RowTemplate = Create'Frame'{
3455 Name = "Row";
3456 Visible = false;
3457 }
3458
3459 -- appends a space character to aligned text as cheap padding
3460 local function SetText(frame,text)
3461 if text == nil then
3462 frame.Text = "";
3463 else
3464 text = tostring(text)
3465 if #text > 0 and frame.TextXAlignment ~= Enum.TextXAlignment.Center then
3466 if frame.TextXAlignment == Enum.TextXAlignment.Left then
3467 frame.Text = " " .. text
3468 elseif frame.TextXAlignment == Enum.TextXAlignment.Right then
3469 frame.Text = text .. " "
3470 end
3471 else
3472 frame.Text = text
3473 end
3474 end
3475 end
3476
3477 -- used by check-box, which uses either an image or a Graphic.
3478 local function SetImageOrGraphic(cell,active)
3479 local md = CellMetadata[cell]
3480 local checked,unchecked = md.Checked,md.Unchecked
3481 if type(unchecked) == "string" then
3482 cell.Image = active and "" or unchecked
3483 elseif type(unchecked) == "table" then
3484 if active then
3485 unchecked.GUI.Parent = nil
3486 else
3487 if unchecked.GUI.ZIndex ~= cell.ZIndex then
3488 SetZIndex(unchecked.GUI,cell.ZIndex)
3489 end
3490 unchecked.GUI.Parent = cell
3491 end
3492 end
3493 if type(checked) == "string" then
3494 cell.Image = active and checked or ""
3495 elseif type(checked) == "table" then
3496 if active then
3497 if checked.GUI.ZIndex ~= cell.ZIndex then
3498 SetZIndex(checked.GUI,cell.ZIndex)
3499 end
3500 checked.GUI.Parent = cell
3501 else
3502 checked.GUI.Parent = nil
3503 end
3504 end
3505 end
3506
3507 local current_sort_header = nil
3508 local current_sort_type = SORT.NONE
3509
3510 -- generate a template for rows
3511 local ColumnHeaderPos = UDim.new()
3512 for i,cell_scheme in pairs(ColumnScheme) do
3513 local columnStylist = CreateStylist(cell_scheme.style)
3514 ColumnStylists[i] = columnStylist
3515 -- CellStylist.AddStylist(columnStylist)
3516 -- columnStylist.AddStylist(HeaderStylist)
3517 local cell_type = cell_scheme.type
3518 local template
3519 if cell_type == "text" then
3520 template = Instance.new("TextLabel",RowTemplate)
3521 template.Name = "Text"
3522 elseif cell_type == "image" then
3523 template = Instance.new("ImageLabel",RowTemplate)
3524 template.Name = "Image"
3525 elseif cell_type == "text-button" then
3526 template = Instance.new("TextButton",RowTemplate)
3527 template.Name = "TextButton"
3528 elseif cell_type == "image-button" then
3529 template = Instance.new("ImageButton",RowTemplate)
3530 template.Name = "ImageButton"
3531 elseif cell_type == "text-field" then
3532 template = Instance.new("TextBox",RowTemplate)
3533 template.Name = "TextField"
3534 template.ClearTextOnFocus = false
3535 elseif cell_type == "check-box" then
3536 template = Instance.new("ImageButton",RowTemplate)
3537 template.Name = "CheckBox"
3538 end
3539
3540 -- create the header row
3541 local ColumnHeader = Create'TextButton'{
3542 Name = "ColumnHeader";
3543 Parent = ColumnHeaderFrame;
3544 }
3545 HeaderStylist.AddObject(ColumnHeader)
3546 columnStylist.AddObject(ColumnHeader)
3547 SetText(ColumnHeader,cell_scheme.name)
3548 -- sort on click
3549 ColumnHeader.MouseButton1Click:connect(function()
3550 if current_sort_header == ColumnHeader then
3551 -- cycle between ascending, descending, and none
3552 if current_sort_type == SORT.ASCENDING then
3553 current_sort_type = SORT.DESCENDING
3554 elseif current_sort_type == SORT.DESCENDING then
3555 current_sort_type = SORT.NONE
3556 else
3557 current_sort_type = SORT.ASCENDING
3558 end
3559 else
3560 current_sort_type = SORT.ASCENDING
3561 end
3562 current_sort_header = ColumnHeader
3563 SortColumn(i,current_sort_type)
3564 end)
3565 local Width = cell_scheme.width
3566 ColumnHeader.Size = UDim2.new(Width.Scale,Width.Offset,1,0)
3567 ColumnHeader.Position = UDim2.new(ColumnHeaderPos.Scale,ColumnHeaderPos.Offset,0,0)
3568 ColumnHeaderPos = ColumnHeaderPos + Width
3569 end
3570 ColumnHeaderPos = nil
3571
3572---- Class functions
3573
3574 -- update the row frame to reflect the row data
3575 function Class.UpdateRow(index)
3576 local RowData
3577 if type(index) == "number" then
3578 RowData = RowDataList[index]
3579 else
3580 RowData = index
3581 end
3582
3583 local Row = RowFrameLookup[RowData]
3584 local Cells = Row:GetChildren() -- eww
3585 local CellColPos = UDim.new()
3586 for i,cell_scheme in pairs(ColumnScheme) do
3587 local cell_type = cell_scheme.type
3588 local Cell = Cells[i]
3589 local CellData = RowData[i]
3590 local Width = cell_scheme.width
3591 if cell_type == "text" then
3592 SetText(Cell,CellData)
3593 elseif cell_type == "image" then
3594 Cell.Image = CellData
3595 elseif cell_type == "text-button" then
3596 SetText(Cell,CellData)
3597 elseif cell_type == "image-button" then
3598 Cell.Image = CellData
3599 elseif cell_type == "text-field" then
3600 SetText(Cell,CellData)
3601 elseif cell_type == "check-box" then
3602 SetImageOrGraphic(Cell,CellData)
3603 end
3604 Cell.Size = UDim2.new(Width.Scale,Width.Offset,1,0) -- this would be a lot easier if UDim2.new accepts UDims
3605 Cell.Position = UDim2.new(CellColPos.Scale,CellColPos.Offset,0,0)
3606 CellColPos = CellColPos + Width
3607 end
3608 end
3609
3610 -- add a new row to the list; optional list index
3611 function Class.AddRow(RowData,index,style)
3612 -- TODO: verify that data matches column scheme
3613 local NewRow = RowTemplate:Clone()
3614 if index then
3615 index = index > #RowDataList+1 and #RowDataList+1 or index < 1 and 1 or index
3616 table.insert(RowDataList,index,RowData)
3617 table.insert(RowFramesList,index,NewRow)
3618 else
3619 table.insert(RowDataList,RowData)
3620 table.insert(RowFramesList,NewRow)
3621 end
3622 Scroll.TotalSpace = #RowDataList
3623 RowSpanStylist.AddObject(NewRow)
3624 NewRow.Size = UDim2.new(1,0,0,rowHeight)
3625 NewRow.ZIndex = DetailedListFrame.ZIndex
3626 NewRow.Parent = ListViewFrame
3627 local Cells = NewRow:GetChildren()
3628 local CellColPos = UDim.new()
3629 local rowStylist = CreateStylist(style)
3630 RowStylists[RowData] = rowStylist
3631 -- CellStylist.AddStylist(rowStylist)
3632 for i,cell_scheme in pairs(ColumnScheme) do
3633 local cell_type = cell_scheme.type
3634 local Cell = Cells[i]
3635 Cell.ZIndex = DetailedListFrame.ZIndex
3636 CellStylist.AddObject(Cell)
3637 ColumnStylists[i].AddObject(Cell)
3638 rowStylist.AddObject(Cell)
3639 local CellData = RowData[i]
3640 local Width = cell_scheme.width
3641 if cell_type == "text" then
3642 SetText(Cell,CellData)
3643 elseif cell_type == "image" then
3644 Cell.Image = CellData
3645 elseif cell_type == "text-button" then
3646 SetText(Cell,CellData)
3647 Cell.MouseButton1Click:connect(function()
3648 cell_scheme.callback(RowData,Class)
3649 end)
3650 elseif cell_type == "image-button" then
3651 Cell.Image = CellData
3652 Cell.MouseButton1Click:connect(function()
3653 cell_scheme.callback(RowData,Class)
3654 end)
3655 elseif cell_type == "text-field" then
3656 SetText(Cell,CellData)
3657 local last_text = CellData
3658 Cell.FocusLost:connect(function(enter)
3659 local text = cell_scheme.callback(Cell.Text,RowData,Class,enter)
3660 if text then
3661 RowData[i] = text
3662 SetText(Cell,text)
3663 last_text = text
3664 else
3665 SetText(Cell,last_text)
3666 end
3667 end)
3668 elseif cell_type == "check-box" then
3669 CellMetadata[Cell] = {}
3670 if type(cell_scheme.checked) == "table" then
3671 local graphic = CreateGraphic(cell_scheme.checked[1],cell_scheme.checked[2])
3672 SetZIndex(graphic.GUI,DetailedListFrame.ZIndex)
3673 GlobalStylist.AddStylist(graphic.Stylist,GraphicTextAlias)
3674 CellMetadata[Cell].Checked = graphic
3675 else
3676 CellMetadata[Cell].Checked = cell_scheme.checked
3677 end
3678 if type(cell_scheme.unchecked) == "table" then
3679 local graphic = CreateGraphic(cell_scheme.unchecked[1],cell_scheme.unchecked[2])
3680 SetZIndex(graphic.GUI,DetailedListFrame.ZIndex)
3681 GlobalStylist.AddStylist(graphic.Stylist,GraphicTextAlias)
3682 CellMetadata[Cell].Unchecked = graphic
3683 else
3684 CellMetadata[Cell].Unchecked = cell_scheme.unchecked
3685 end
3686 SetImageOrGraphic(Cell,CellData)
3687 Cell.MouseButton1Click:connect(function()
3688 local continue = true
3689 if cell_scheme.callback then
3690 continue = cell_scheme.callback(RowData,Class)
3691 end
3692 if continue then
3693 RowData[i] = not RowData[i]
3694 SetImageOrGraphic(Cell,RowData[i])
3695 end
3696 end)
3697 end
3698 Cell.Size = UDim2.new(Width.Scale,Width.Offset,1,0) -- this would be a lot easier if UDim2.new accepts UDims
3699 Cell.Position = UDim2.new(CellColPos.Scale,CellColPos.Offset,0,0)
3700 CellColPos = CellColPos + Width
3701 end
3702 RowFrameLookup[RowData] = NewRow
3703 RowDataLookup[NewRow] = RowData
3704 Update()
3705 return RowData
3706 end
3707
3708 -- remove entry from the list; may be a list index or an item in the list
3709 function Class.RemoveRow(index)
3710 local RowData
3711 if type(index) == "number" or type(index) == "nil" then
3712 RowData = table.remove(RowDataList,index)
3713 else
3714 for i,v in pairs(RowDataList) do
3715 if v == index then
3716 RowData = table.remove(RowDataList,i)
3717 break
3718 end
3719 end
3720 end
3721 if RowData then
3722 local stylist = RowStylists[RowData]
3723 stylist.Destroy()
3724 RowStylists[RowData] = nil
3725
3726 local frame = RowFrameLookup[RowData]
3727 RowDataLookup[frame] = nil
3728 RowFrameLookup[RowData] = nil
3729 for i,rowframe in pairs(RowFramesList) do
3730 if rowframe == frame then
3731 for i,cell in pairs(rowframe:GetChildren()) do
3732 GlobalStylist.RemoveObject(cell)
3733 ColumnStylists[i].RemoveObject(cell)
3734 CellStylist.RemoveObject(cell)
3735 end
3736 table.remove(RowFramesList,i)
3737 break
3738 end
3739 end
3740 Scroll.TotalSpace = #RowDataList
3741 for i,rowframe in pairs(DisplayedRows) do
3742 if rowframe == frame then
3743 table.remove(DisplayedRows,i)
3744 break
3745 end
3746 end
3747 frame:Destroy()
3748 end
3749 Update()
3750 return RowData
3751 end
3752
3753 do
3754 local tmp = {}
3755 for k,v in pairs(RowDataList) do
3756 tmp[k] = v
3757 RowDataList[k] = nil
3758 end
3759 for i=1,#tmp do
3760 Class.AddRow(tmp[i])
3761 end
3762 end
3763
3764---- Finish
3765
3766 -- when the list's Zindex changes, update the ZIndex of everything
3767 SetZIndexOnChanged(DetailedListFrame)
3768
3769 -- attempts to free resources
3770 function Class.Destroy()
3771 local function empty_table(t)
3772 for k in pairs(t) do t[k] = nil end
3773 end
3774 empty_table(Class.Stylist)
3775 empty_table(Class)
3776 empty_table(RowFramesList)
3777 empty_table(RowFrameLookup)
3778 empty_table(RowDataLookup)
3779 empty_table(DisplayedRows)
3780 empty_table(CellMetadata)
3781 GlobalStylist.Destroy()
3782 CellStylist.Destroy()
3783 HeaderStylist.Destroy()
3784 RowSpanStylist.Destroy()
3785 for k,v in pairs(RowStylists) do
3786 v.Destroy()
3787 RowStylists[k] = nil
3788 end
3789 for k,v in pairs(ColumnStylists) do
3790 v.Destroy()
3791 ColumnStylists[k] = nil
3792 end
3793 Scroll.Destroy()
3794 RowTemplate:Destroy()
3795 DetailedListFrame:Destroy()
3796 end
3797
3798 Class.Update()
3799
3800 return Class,DetailedListFrame
3801end
3802
3803lib.DetailedList = CreateDetailedList
3804
3805--[[DEPEND:
3806SetZIndex.lua;
3807Stylist.lua;
3808AutoSizeLabel.lua;
3809StackingFrame.lua;
3810]]
3811
3812doc["TabContainer"] = [==[
3813TabContainer ( table `content_list`, number `selected_height`, number `tab_height` ) [constructor]
3814 returns: TabContainer `object`, Frame `tab_container`
3815
3816Creates a container that can hold multiple GuiObjects in a single space by using tabs.
3817A GuiObject added to the container gets its own tab, which shows the GuiObject's Name, and displays the GuiObject when clicked.
3818
3819Arguments:
3820 `content_list`
3821 A list of GuiObjects to be initially added to the container.
3822 Optional; defaults to an empty table
3823
3824 `selected_height`
3825 The height of a selected tab.
3826 Optional; defaults to 24
3827
3828 `tab_height`
3829 The height, in pixels, of a tab that is not selected.
3830 Optional; defaults to 20
3831
3832Returns:
3833 `object`
3834 The TabContainer object.
3835 `tab_container`
3836 The container GUI.
3837
3838
3839TabContainer Class:
3840 This class contains the following members:
3841
3842 Readonly:
3843 GUI
3844 The container GUI.
3845
3846 TabStylist
3847 A Stylist object that controls the appearance of unselected tabs.
3848 Also controls the appearance of the content border.
3849
3850 SelectedTabStylist
3851 A Stylist object that controls the appearance of the selected tab.
3852
3853 Methods:
3854 AddTab ( GuiObject `content`, number `index` )
3855 Adds `content` to the container at `index`.
3856 if `index` is not specified, then it will be added to the end.
3857
3858 RemoveTab ( number `index` )
3859 Removes the tab at `index`, and returns the content of that tab.
3860 `index` can also be a GUI in the container.
3861
3862 MoveTab ( number `index`, number `to` )
3863 Moves the object at `index` to the index of `to`.
3864 `index` and `to` can also be GUIs in the container.
3865
3866 SelectTab ( number `index` )
3867 Selects the tab at `index`.
3868 `index` can also be a GUI in the container.
3869
3870 GetIndex ( GuiObject `content` )
3871 Returns the index of `content`.
3872 If `content` isn't in the container, this returns nil.
3873
3874 GetSelectedIndex ( )
3875 Returns the index of the selected tab, and its GUI.
3876
3877 Destroy ( )
3878 Releases the resources used by this object.
3879 Run this if you're no longer using this object.
3880
3881]==]
3882
3883local function CreateTabContainer(ContentList,SelectedTabHeight,TabHeight)
3884 SelectedTabHeight = SelectedTabHeight or 24
3885 TabHeight = TabHeight or 20
3886
3887 local selected_index = 0
3888 local content_list = {}
3889 local tab_lookup = {}
3890 local con = {}
3891
3892 local TabContainerFrame = Create'Frame'{
3893 Name = "TabContainer";
3894 Size = UDim2.new(0,300,0,200);
3895 BackgroundTransparency = 1;
3896 Create'Frame'{
3897 Name = "Content";
3898 Size = UDim2.new(1,0,1,-SelectedTabHeight);
3899 Position = UDim2.new(0,0,0,SelectedTabHeight);
3900 BackgroundColor3 = Color3.new();
3901 BorderColor3 = Color3.new(1,1,1);
3902 };
3903 Create'Frame'{
3904 Parent = TabContainerFrame;
3905 Name = "Tabs";
3906 BackgroundTransparency = 1;
3907 };
3908 }
3909
3910 local TabContentFrame = TabContainerFrame.Content
3911 local TabHeaderFrame = TabContainerFrame.Tabs
3912 local TabHeaderClass = CreateStackingFrame(TabHeaderFrame,true,true)
3913
3914 local TabStylist = CreateStylist({
3915 BackgroundColor3 = Color3.new();
3916 BackgroundTransparency = 0.5;
3917 BorderColor3 = Color3.new(1,1,1);
3918 TextColor3 = Color3.new(1,1,1);
3919 Font = "ArialBold";
3920 FontSize = "Size14";
3921 })
3922 TabStylist.AddObject(TabContentFrame)
3923
3924 local SelectedTabStylist = CreateStylist({
3925 BackgroundColor3 = Color3.new();
3926 BackgroundTransparency = 0.5;
3927 BorderColor3 = Color3.new(1,1,1);
3928 TextColor3 = Color3.new(1,1,1);
3929 Font = "ArialBold";
3930 FontSize = "Size14";
3931 })
3932
3933 local function GetIndex(content)
3934 for index,c in pairs(content_list) do
3935 if c == content then
3936 return index
3937 end
3938 end
3939 end
3940
3941 local function GetSelectedIndex()
3942 return selected_index,content_list[selected_index]
3943 end
3944
3945 local function ClampIndex(index,i)
3946 local max = #content_list + (i or 0)
3947 index = math.floor(index)
3948 return index < 1 and 1 or index > max and max or index
3949 end
3950
3951 local function SelectTab(index)
3952 if #content_list > 0 then
3953 if type(index) ~= "number" then
3954 index = GetIndex(index)
3955 end
3956 if index then
3957 index = ClampIndex(index)
3958 if selected_index > 0 then
3959 local content = content_list[selected_index]
3960 content.Visible = false
3961 local Tab = tab_lookup[content]
3962 Tab.LockAxis(nil,TabHeight)
3963 SelectedTabStylist.RemoveObject(Tab,GUI)
3964 TabStylist.AddObject(Tab.GUI)
3965 end
3966 local content = content_list[index]
3967 content.Visible = true
3968 local Tab = tab_lookup[content]
3969 Tab.LockAxis(nil,SelectedTabHeight)
3970 TabStylist.RemoveObject(Tab.GUI)
3971 SelectedTabStylist.AddObject(Tab.GUI)
3972 selected_index = index
3973 end
3974 else
3975 selected_index = 0
3976 end
3977 end
3978
3979 local function AddTab(content,index)
3980 if index then
3981 index = ClampIndex(index,1)
3982 table.insert(content_list,index,content)
3983 else
3984 table.insert(content_list,content)
3985 index = #content_list
3986 end
3987 content.Visible = false
3988 content.Parent = TabContentFrame
3989
3990 local TabFrame = Create'TextButton'{
3991 Name = "Tab";
3992 Text = content.Name;
3993 }
3994 local Tab = CreateAutoSizeLabel(TabFrame)
3995 tab_lookup[content] = Tab
3996 Tab.SetPadding(0,4)
3997 Tab.LockAxis(nil,TabHeight)
3998 TabStylist.AddObject(TabFrame)
3999 TabHeaderClass.AddObject(TabFrame,index)
4000 TabFrame.MouseButton1Click:connect(function()
4001 SelectTab(content)
4002 end)
4003 con[content] = content.Changed:connect(function(p)
4004 if p == "Name" then
4005 TabFrame.Text = content.Name
4006 end
4007 end)
4008 if selected_index == 0 then
4009 SelectTab(index)
4010 elseif index <= selected_index then
4011 selected_index = selected_index + 1
4012 end
4013 end
4014
4015 local function RemoveTab(index)
4016 if #content_list > 0 then
4017 if type(index) ~= "number" then
4018 if index == nil then
4019 index = #content_list
4020 else
4021 index = GetIndex(index)
4022 end
4023 end
4024 if index then
4025 index = ClampIndex(index)
4026 local content = table.remove(content_list,index)
4027 content.Parent = nil
4028 con[content]:disconnect()
4029 con[content] = nil
4030 local tab = tab_lookup[content]
4031 TabHeaderClass.RemoveObject(index)
4032 tab_lookup[content] = nil
4033 tab.Destroy()
4034 if index == selected_index then
4035 SelectTab(index)
4036 elseif index < selected_index then
4037 selected_index = selected_index - 1
4038 end
4039 return content
4040 end
4041 end
4042 end
4043
4044 local function MoveTab(index,to)
4045 if #content_list > 0 then
4046 if type(index) ~= "number" then
4047 index = GetIndex(index)
4048 end
4049 if type(to) ~= "number" then
4050 to = GetIndex(to)
4051 end
4052 if index and to then
4053 index = ClampIndex(index)
4054 to = ClampIndex(to)
4055 local content = table.remove(content_list,index)
4056 table.insert(content_list,to,content)
4057 TabHeaderClass.MoveObject(index,to)
4058 if index == selected_index then
4059 selected_index = to
4060 elseif index > selected_index and to <= selected_index then
4061 selected_index = selected_index + 1
4062 elseif index < selected_index and to >= selected_index then
4063 selected_index = selected_index - 1
4064 end
4065 end
4066 end
4067 end
4068
4069 local Class = {
4070 GUI = TabContainerFrame;
4071 GetIndex = GetIndex;
4072 GetSelectedIndex = GetSelectedIndex;
4073 SelectTab = SelectTab;
4074 AddTab = AddTab;
4075 RemoveTab = RemoveTab;
4076 MoveTab = MoveTab;
4077 TabStylist = TabStylist;
4078 SelectedTabStylist = SelectedTabStylist;
4079 }
4080
4081 SetZIndexOnChanged(TabContainerFrame)
4082
4083 local function Destroy()
4084 for k in pairs(Class) do
4085 Class[k] = nil
4086 end
4087 SelectedTabStylist.Destroy()
4088 TabStylist.Destroy()
4089 TabHeaderClass.Destroy()
4090 for k,v in pairs(con) do
4091 v:disconnect()
4092 con[k] = nil
4093 end
4094 for i,content in pairs(content_list) do
4095 content.Parent = nil
4096 content_list[i] = nil
4097 end
4098 for k,tab in pairs(tab_lookup) do
4099 tab_lookup[k] = nil
4100 tab.Destroy()
4101 end
4102 TabContainerFrame:Destroy()
4103 end
4104 Class.Destroy = Destroy
4105
4106 if ContentList then
4107 for i,content in pairs(ContentList) do
4108 AddTab(content,i)
4109 end
4110 end
4111
4112 return Class,TabContainerFrame
4113end
4114
4115lib.TabContainer = CreateTabContainer
4116
4117local dialog = {}
4118doc["dialog"] = [==[
4119 A library of various user-input dialogs.
4120]==]
4121
4122--[[DEPEND:
4123Stylist.lua;
4124]]
4125
4126doc["dialog.Confirm"] = [==[
4127dialog.Confirm ( GuiBase `parent`, string `message`, Stylist `style`, Stylist `button_style` ) [function]
4128 returns bool `result`
4129
4130Displays a confirmation message and waits for the user to click Yes, No, or Cancel.
4131
4132Arguments:
4133 `parent`
4134 The object to display the dialog in.
4135
4136 `message`
4137 The message to display to the user.
4138 Optional; defaults to "Are you sure?"
4139
4140 `stylist`
4141 A stylist to apply to the dialog.
4142 Optional; defaults to a premade stylist
4143
4144 `button_style`
4145 A stylist to apply to buttons.
4146 Automatically inherits from `stylist`.
4147 Optional; defaults to a premade stylist
4148
4149Returns:
4150 `result`
4151 The result of the dialog.
4152 If the user clicked Yes, returns true.
4153 If the user clicked No, returns false.
4154 If the user clicked Cancel, returns nil.
4155]==]
4156
4157function dialog.Confirm(parent,message,global,buttons)
4158 GlobalStylist = global or Stylist{
4159 BackgroundColor3 = Color3.new(0,0,0);
4160 BorderColor3 = Color3.new(1,1,1);
4161 TextColor3 = Color3.new(1,1,1);
4162 Font = "ArialBold";
4163 FontSize = "Size14";
4164 }
4165 ButtonStylist = buttons or Stylist{
4166 Style = "RobloxButton";
4167 }
4168
4169 local GlobalStylist = CreateStylist(style)
4170 local ButtonStylist = CreateStylist(button_style)
4171 local style_in = GlobalStylist.StylistIn(ButtonStylist)
4172 GlobalStylist.AddStylist(ButtonStylist)
4173
4174 local Dialog = Create'Frame'{
4175 Name = "ConfirmDialog";
4176 Size = UDim2.new(1.5,0,1.5,0);
4177 Position = UDim2.new(-0.25,0,-0.25,0);
4178 BorderSizePixel = 0;
4179 BackgroundTransparency = 0.5;
4180 BackgroundColor3 = Color3.new(0,0,0);
4181 Active = true;
4182 GlobalStylist.AddObject(Create'Frame'{
4183 Name = "DialogBox";
4184 Size = UDim2.new(0,250,0,150);
4185 Position = UDim2.new(0.5,-125,0.5,-75);
4186 Create'Frame'{
4187 Name = "MarginBox";
4188 BackgroundTransparency = 1;
4189 Size = UDim2.new(1,-16,1,-16);
4190 Position = UDim2.new(0,8,0,8);
4191 GlobalStylist.AddObject(Create'TextLabel'{
4192 BackgroundTransparency = 1;
4193 TextScaled = true;
4194 Text = message or "";
4195 Size = UDim2.new(1,-16,0.8,-24);
4196 Position = UDim2.new(0,8,0,8);
4197 });
4198 Create'Frame'{
4199 Name = "Buttons";
4200 BackgroundTransparency = 1;
4201 Size = UDim2.new(1,0,0.2,0);
4202 Position = UDim2.new(0,0,0.8,0);
4203---- Buttons
4204 ButtonStylist.AddObject(Create'TextButton'{
4205 Name = "YesButton";
4206 Text = "Yes";
4207 Size = UDim2.new(1/3,0,1,0);
4208 Position = UDim2.new(0/3,0,0,0);
4209 });
4210 ButtonStylist.AddObject(Create'TextButton'{
4211 Name = "NoButton";
4212 Text = "No";
4213 Size = UDim2.new(1/3,0,1,0);
4214 Position = UDim2.new(1/3,0,0,0);
4215 });
4216 ButtonStylist.AddObject(Create'TextButton'{
4217 Name = "CancelButton";
4218 Text = "Cancel";
4219 Size = UDim2.new(1/3,0,1,0);
4220 Position = UDim2.new(2/3,0,0,0);
4221 });
4222----/Buttons
4223 };
4224 };
4225 });
4226 }
4227 local Buttons = Dialog.DialogBox.MarginBox.Buttons
4228
4229 local Result = nil
4230 local Event = Instance.new("BindableEvent")
4231
4232 Buttons.YesButton.MouseButton1Click:connect(function()
4233 Result = true
4234 Event:Fire()
4235 end)
4236 Buttons.NoButton.MouseButton1Click:connect(function()
4237 Result = false
4238 Event:Fire()
4239 end)
4240 Buttons.CancelButton.MouseButton1Click:connect(function()
4241 Result = nil
4242 Event:Fire()
4243 end)
4244
4245 SetZIndex(Dialog,10)
4246
4247 Dialog.Parent = parent
4248 Event.Event:wait()
4249 Dialog:Destroy()
4250 Event:Destroy()
4251
4252 if global == nil then
4253 GlobalStylist.Destroy()
4254 else
4255 if not style_in then
4256 GlobalStylist.RemoveStylist(ButtonStylist)
4257 end
4258 end
4259 if buttons == nil then
4260 ButtonStylist.Destroy()
4261 end
4262
4263 return Result
4264end
4265
4266lib.dialog = dialog
4267
4268--[[DEPEND:]]
4269
4270doc["Help"] = [==[
4271Help ( string `query`, bool `no_print` ) [function]
4272 returns: string `message`
4273
4274Returns help information for the library.
4275
4276Arguments:
4277 `query`
4278 The name of the entry to display help for (case-insensitive).
4279 If unspecified, a list of entries will be displayed.
4280
4281 `no_print`
4282 If set to true, the returned message will NOT also be printed.
4283 Note than when printed this way, the message is automatically formatted to maintain readability.
4284
4285Returns:
4286 `message`
4287 The resulting message.
4288
4289
4290Documentation Remarks:
4291 Argument and return values follow this format:
4292 type `reference`
4293
4294 "type" is the argument's value type (string, bool, table, etc).
4295 Some arguments may accept more than one type. If so, these types will be indicated in the argument's description.
4296 An asterisk (*) as the type indicates that the argument may be of any type.
4297 Types can also be Roblox Instances, or objects defined by this library.
4298 The type "GuiText" refers to TextLabels, TextButtons, and TextBoxes.
4299
4300 In the documentation, a word enclosed in grave accents (i.e. `example`) refers to the indicated value.
4301
4302 Each entry has one of various types, which are indicated between square brackets (i.e. [type] ):
4303 [const]
4304 A constant, unchanging value.
4305 [enum]
4306 An enumerated type.
4307 [function]
4308 A function; receives arguments and returns values.
4309 [constructor]
4310 Similar to a function, buts returns an instantiated object.
4311
4312 Documentation of a constructor will also contain a definition of the class it creates.
4313 Classes contain various member types:
4314 Readonly
4315 A value that is meant only to be read, not written to.
4316 Field
4317 A value that can be changed, affecting the state of the object.
4318 Method
4319 A function that does something internally when called.
4320 NOTE: Currently, methods must be called with ".", not ":". This may change in future versions.
4321 Callback
4322 Similar to a field in that it can be set, but the value is a function.
4323 When set, the function will be invoked at some point, and may have arguments passed to it.
4324 What the function returns can affect the object's behavior.
4325 Event
4326 Allows the user to connect functions (called "listeners").
4327 At some point, the event will fire, which then calls each listener connected to it.
4328 Values may be passed to each listener, received as "parameters".
4329]==]
4330
4331local default_help = [==[
4332Use ]==]..PROJECT_NAME..[==[.Help("EntryName") for more information about a specific entry.
4333
4334Entries:
4335]==]
4336
4337do
4338 local sorted = {}
4339 for name,ref in pairs(doc) do
4340 if type(name) == "string" then
4341 table.insert(sorted,name)
4342 end
4343 end
4344 table.sort(sorted)
4345 for i,name in pairs(sorted) do
4346 default_help = default_help .. "\t" .. name .. "\n"
4347 end
4348end
4349
4350doc["EntryName"] = [==[
4351That was an example, silly.
4352
4353]==]..default_help
4354
4355
4356local DocumentationLookup = {}
4357DocumentationLookup[false] = default_help
4358
4359function lib.Help(query,no_print)
4360 if type(query) == "string" then
4361 query = query:lower()
4362 end
4363 local output = DocumentationLookup[query] or DocumentationLookup[false]
4364 if not no_print then
4365 print(string.rep("_",80))
4366 for c in output:gsub("\r\n?","\n"):gmatch("(.-)\n") do
4367 c = c:gsub(" ","\160")
4368 c = c:gsub("\t",string.rep("\160",8))
4369 print(#c == 0 and "\160" or c)
4370 end
4371 print("\160")
4372 end
4373 return output
4374end
4375
4376for name,ref in pairs(doc) do
4377 if type(name) == "string" then
4378 DocumentationLookup[name:lower()] = ref
4379 local f = lib[name]
4380 if type(f) == "function" then
4381 DocumentationLookup[f] = ref
4382 end
4383 end
4384end
4385
4386setmetatable(lib,{
4387 __tostring = function()
4388 return ("%s GUI Library [v%s] (use %s.Help() for help)"):format(PROJECT_NAME, version, PROJECT_NAME)
4389 end;
4390})
4391
4392_G.gloo = lib
4393print(("Loaded %s library. Type _G.%s.Help() for help."):format(PROJECT_NAME, PROJECT_NAME))