commands/FBDisplayCommands.py (292 lines of code) (raw):
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as runtimeHelpers
import fbchisellldbviewcontrollerhelpers as viewControllerHelpers
import fbchisellldbviewhelpers as viewHelpers
import lldb
def lldbcommands():
return [
FBCoreAnimationFlushCommand(),
FBDrawBorderCommand(),
FBRemoveBorderCommand(),
FBMaskViewCommand(),
FBUnmaskViewCommand(),
FBShowViewCommand(),
FBHideViewCommand(),
FBPresentViewControllerCommand(),
FBDismissViewControllerCommand(),
FBSlowAnimationCommand(),
FBUnslowAnimationCommand(),
]
class FBDrawBorderCommand(fb.FBCommand):
colors = [
"black",
"gray",
"red",
"green",
"blue",
"cyan",
"yellow",
"magenta",
"orange",
"purple",
"brown",
]
def name(self):
return "border"
def description(self):
return "Draws a border around <viewOrLayer>. Color and width can be optionally provided. Additionally depth can be provided in order to recursively border subviews."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to border. NSViews must be layer-backed.",
)
]
def options(self):
return [
fb.FBCommandArgument(
short="-c",
long="--color",
arg="color",
type="string",
default="red",
help="A color name such as 'red', 'green', 'magenta', etc.",
),
fb.FBCommandArgument(
short="-w",
long="--width",
arg="width",
type="CGFloat",
default=2.0,
help="Desired width of border.",
),
fb.FBCommandArgument(
short="-d",
long="--depth",
arg="depth",
type="int",
default=0,
help="Number of levels of subviews to border. Each level gets a different color beginning with the provided or default color",
),
]
def run(self, args, options):
def setBorder(layer, width, color, colorClass):
fb.evaluateEffect("[%s setBorderWidth:(CGFloat)%s]" % (layer, width))
fb.evaluateEffect(
"[%s setBorderColor:(CGColorRef)[(id)[%s %sColor] CGColor]]"
% (layer, colorClass, color)
)
obj = fb.evaluateInputExpression(args[0])
depth = int(options.depth)
isMac = runtimeHelpers.isMacintoshArch()
color = options.color
assert color in self.colors, "Color must be one of the following: {}".format(
" ".join(self.colors)
)
colorClassName = "UIColor"
if isMac:
colorClassName = "NSColor"
if viewHelpers.isView(obj):
prevLevel = 0
for view, level in viewHelpers.subviewsOfView(obj):
if level > depth:
break
if prevLevel != level:
color = self.nextColorAfterColor(color)
prevLevel = level
layer = viewHelpers.convertToLayer(view)
setBorder(layer, options.width, color, colorClassName)
else:
# `obj` is not a view, make sure recursive bordering is not requested
assert (
depth <= 0
), "Recursive bordering is only supported for UIViews or NSViews"
layer = viewHelpers.convertToLayer(obj)
setBorder(layer, options.width, color, colorClassName)
lldb.debugger.HandleCommand("caflush")
def nextColorAfterColor(self, color):
assert color in self.colors, "{} is not a supported color".format(color)
return self.colors[(self.colors.index(color) + 1) % len(self.colors)]
class FBRemoveBorderCommand(fb.FBCommand):
def name(self):
return "unborder"
def description(self):
return "Removes border around <viewOrLayer>."
def options(self):
return [
fb.FBCommandArgument(
short="-d",
long="--depth",
arg="depth",
type="int",
default=0,
help="Number of levels of subviews to unborder.",
)
]
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to unborder.",
)
]
def run(self, args, options):
def setUnborder(layer):
fb.evaluateEffect("[%s setBorderWidth:(CGFloat)%s]" % (layer, 0))
obj = args[0]
depth = int(options.depth)
if viewHelpers.isView(obj):
for view, level in viewHelpers.subviewsOfView(obj):
if level > depth:
break
layer = viewHelpers.convertToLayer(view)
setUnborder(layer)
else:
# `obj` is not a view, make sure recursive unbordering is not requested
assert (
depth <= 0
), "Recursive unbordering is only supported for UIViews or NSViews"
layer = viewHelpers.convertToLayer(obj)
setUnborder(layer)
lldb.debugger.HandleCommand("caflush")
class FBMaskViewCommand(fb.FBCommand):
def name(self):
return "mask"
def description(self):
return "Add a transparent rectangle to the window to reveal a possibly obscured or hidden view or layer's bounds"
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to mask.",
)
]
def options(self):
return [
fb.FBCommandArgument(
short="-c",
long="--color",
arg="color",
type="string",
default="red",
help="A color name such as 'red', 'green', 'magenta', etc.",
),
fb.FBCommandArgument(
short="-a",
long="--alpha",
arg="alpha",
type="CGFloat",
default=0.5,
help="Desired alpha of mask.",
),
]
def run(self, args, options):
viewOrLayer = fb.evaluateObjectExpression(args[0])
viewHelpers.maskView(viewOrLayer, options.color, options.alpha)
class FBUnmaskViewCommand(fb.FBCommand):
def name(self):
return "unmask"
def description(self):
return "Remove mask from a view or layer"
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/CALayer *",
help="The view/layer to mask.",
)
]
def run(self, args, options):
viewOrLayer = fb.evaluateObjectExpression(args[0])
viewHelpers.unmaskView(viewOrLayer)
class FBCoreAnimationFlushCommand(fb.FBCommand):
def name(self):
return "caflush"
def description(self):
return "Force Core Animation to flush. This will 'repaint' the UI but also may mess with ongoing animations."
def run(self, arguments, options):
viewHelpers.flushCoreAnimationTransaction()
class FBShowViewCommand(fb.FBCommand):
def name(self):
return "show"
def description(self):
return "Show a view or layer."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to show.",
)
]
def run(self, args, options):
viewHelpers.setViewHidden(args[0], False)
class FBHideViewCommand(fb.FBCommand):
def name(self):
return "hide"
def description(self):
return "Hide a view or layer."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer",
type="UIView/NSView/CALayer *",
help="The view/layer to hide.",
)
]
def run(self, args, options):
viewHelpers.setViewHidden(args[0], True)
class FBPresentViewControllerCommand(fb.FBCommand):
def name(self):
return "present"
def description(self):
return "Present a view controller."
def args(self):
return [
fb.FBCommandArgument(
arg="viewController",
type="UIViewController *",
help="The view controller to present.",
)
]
def run(self, args, option):
viewControllerHelpers.presentViewController(args[0])
class FBDismissViewControllerCommand(fb.FBCommand):
def name(self):
return "dismiss"
def description(self):
return "Dismiss a presented view controller."
def args(self):
return [
fb.FBCommandArgument(
arg="viewController",
type="UIViewController *",
help="The view controller to dismiss.",
)
]
def run(self, args, option):
viewControllerHelpers.dismissViewController(args[0])
class FBSlowAnimationCommand(fb.FBCommand):
def name(self):
return "slowanim"
def description(self):
return "Slows down animations. Works on the iOS Simulator and a device."
def args(self):
return [
fb.FBCommandArgument(
arg="speed",
type="float",
default=0.1,
help="Animation speed (default 0.1).",
)
]
def run(self, args, option):
viewHelpers.slowAnimation(args[0])
class FBUnslowAnimationCommand(fb.FBCommand):
def name(self):
return "unslowanim"
def description(self):
return "Turn off slow animations."
def run(self, args, option):
viewHelpers.slowAnimation()