# encoding: ascii
# api: powershell
# title: Start-Presentation
# description: My current (WPF 4 compatible) PowerBoots-based Presentation Module. REQUIRES PresentationFrame.xaml and, of course, PowerBoots
# version: 1.0
# type: module
# author: Joel Bennett
# license: CC0
# function: Start-Zoom
# x-poshcode-id: 2106
# x-archived: 2010-08-28T11:18:34
#
# This isn’t really ready to be shared, but I always tell people if you wait until after you have time to clean it up, you’ll never share it — so since I’m unlikely to actually finish cleaning this up any time soon — here it is. See the screenshots for an example
#
if(-not(get-command New-System.Windows.Window -EA 0)){ Import-Module PowerBoots }
Add-BootsTemplate $PSScriptRoot\PresentationFrame.xaml
$name = [System.Windows.Navigation.JournalEntry]::NameProperty
[double]$global:zoomLevel = 1
$SlideCount = 0
function Start-Zoom {
PARAM(
[Parameter(Position=0)]
$Zoom = 0
,
[Parameter(ValueFromPipeline=$true)]
$Root = $($global:Frame)
,
[Parameter()]
$Window = $presentationTitle
)
$null = Invoke-BootsWindow $Window {
Param($Window,$Root)
if(!$Root.RenderTransform.Children) {
$Root.RenderTransform = TransformGroup {
ScaleTransform -ScaleX $zoomLevel -ScaleY $zoomLevel
TranslateTransform
}
$BootsWindow.Add_MouseDown( {
if($zoomLevel -ne 1) {
trap { Write-Host $( $_ | fl * | out-string ) }
$global:MouseDownPoint = [System.Windows.Input.Mouse]::GetPosition($BootsWindow)
$this.CaptureMouse()
}
} )
$BootsWindow.Add_MouseUp( {
if($zoomLevel -ne 1) {
trap { Write-Host $( $_ | fl * | out-string ) }
$this.ReleaseMouseCapture()
}
} )
$BootsWindow.Add_MouseMove( {
if($zoomLevel -ne 1) {
trap { Write-Host $( $_ | fl * | out-string ) }
if($_.LeftButton -eq "Pressed") {
$newPos = [System.Windows.Input.Mouse]::GetPosition($BootsWindow)
$this.Content.RenderTransform.Children[1].SetValue(
[System.Windows.Media.TranslateTransform]::XProperty,
$this.Content.RenderTransform.Children[1].Value.OffsetX + ($newPos.X - $MouseDownPoint.X) )
$this.Content.RenderTransform.Children[1].SetValue(
[System.Windows.Media.TranslateTransform]::YProperty,
$this.Content.RenderTransform.Children[1].Value.OffsetY + ($newPos.Y - $MouseDownPoint.Y) )
$global:MouseDownPoint = $newPos;
}
}
} )
}
} $Root
}
function Set-Zoom {
PARAM(
[Parameter(Position=0)]
$Zoom = 0
,
[Parameter()]
$Root = $global:Frame
,
[Parameter()]
$Window = $presentationTitle
)
if($Zoom -gt 0) {
$global:zoomLevel *= (1+($Zoom/10))
} elseif($Zoom -lt 0) {
$global:zoomLevel /= (1+($Zoom/-10))
} else {
$global:zoomLevel = 1.0
}
$null = Invoke-BootsWindow $Window {
Param($Window,$Root)
$Root.RenderTransform.Children[0].SetValue(
[System.Windows.Media.ScaleTransform]::ScaleXProperty, $zoomLevel )
$Root.RenderTransform.Children[0].SetValue(
[System.Windows.Media.ScaleTransform]::ScaleYProperty, $zoomLevel )
## Note that we have to account for rounding errors ;)
if([Math]::Round($zoomLevel, 10) -eq 1.0 ) {
$Root.NavigationUIVisibility = "Visible"
$Root.RenderTransform.Children[1].SetValue(
[System.Windows.Media.TranslateTransform]::XProperty, 0.0 )
$Root.RenderTransform.Children[1].SetValue(
[System.Windows.Media.TranslateTransform]::YProperty, 0.0 )
} else {
$Root.NavigationUIVisibility = "Hidden"
}
} $Root
}
function Show-Url {
[CmdletBinding()]
PARAM(
[Parameter(Position=0, ValueFromPipeline=$true)]
[Uri]$Content
,
[Parameter()]
$Window = $presentationTitle
)
Invoke-BootsWindow $Window {
$global:Frame.Navigate( $Content )
}
}
New-Alias Navigate Show-Url # backwards compat
function Add-Slide {
#.Synopsis
# Add a slide to the presentation
#.Description
# Add any .net object as a slide to a presentation, wrapping it in a Page to set the title.
[CmdletBinding(DefaultParameterSetName="Script")]
PARAM(
[Parameter(Position=0,ValueFromPipelineByPropertyName=$true)]
[Alias("Name")]
[String]$Title = "Slide $SlideCount"
, [Parameter(Position=1, ValueFromPipeline=$true, Mandatory=$true)]
$Content
, [Parameter()]
$Window = $presentationTitle
)
PROCESS {
$SlideCount++
Invoke-BootsWindow $Window {
Param($Frame, $Content, $Title)
$Frame.Content = Page -Content $Content -Title $Title
} $global:Frame $Content $Title
}
}
function Add-TextSlide {
#.Synopsis
# Add a text slide to the presentation
#.Description
# Add a textual slide to the presentation with a header and several points
[CmdletBinding()]
PARAM(
[Parameter(Position=0)]
$Title = "Slide $SlideCount"
, [Parameter(Position=1, ValueFromPipeline=$true)]
[string[]]$Content = @("Several bullet points", "Which are equally important")
, [Parameter()]
$Window = $presentationTitle
)
BEGIN { [string[]]$global:Points = @() }
PROCESS { [string[]]$global:Points += $Content }
END {
Add-Slide -Title $Title -Window $Window -Content {
StackPanel -Margin 10 {
grid {
TextBlock $Title -FontSize 85 -FontFamily Impact -Foreground White -Effect { BlurEffect -Radius 15 } -TextAlign Center
TextBlock $Title -FontSize 85 -FontFamily Impact -Foreground $global:TitleBrush -TextAlign Center
}
write-host "Points: $Points"
$Points | % {
TextBlock $_ -FontSize 64 -FontWeight Bold -Foreground "White" -TextWrapping Wrap -Effect { DropShadowEffect -BlurRadius 15 }
}
}
}.GetNewClosure()
}
}
function Add-TitleSlide {
#.Synopsis
# Add a text slide to the presentation
#.Description
# Add a textual slide to the presentation with a header and several points
[CmdletBinding()]
PARAM(
[Parameter(Position=0)]
$Title
, [Parameter()]
$Window = $presentationTitle
)
BEGIN { [string[]]$Points = @() }
PROCESS { [string[]]$Points += $Content }
END {
Add-Slide -Title $Title -Window $Window -Content {
Grid {
TextBlock $Title `
-FontSize 125 -FontFamily Impact -Foreground White `
-VerticalAlignment Center -TextAlignment Center `
-HorizontalAlignment Stretch -TextWrapping Wrap `
-Effect { BlurEffect -Radius 20 }
TextBlock $Title `
-FontSize 125 -FontFamily Impact -Foreground White `
-VerticalAlignment Center -TextAlignment Center `
-HorizontalAlignment Stretch -TextWrapping Wrap `
-Effect { BlurEffect -Radius 20 }
TextBlock $Title `
-FontSize 125 -FontFamily Impact -Foreground $global:TitleBrush `
-VerticalAlignment Center -TextAlignment Center `
-HorizontalAlignment Stretch -TextWrapping Wrap `
-Effect {DropShadowEffect -Color White -Radius 15}
}
}
}
}
function Remove-Slide {
#.Synopsis
# Removes the specified slide from the deck (history)
#.Description
# Takes an index or name, and removes that slide, except that you CANNOT remove the last slide.
PARAM(
[Parameter(ParameterSetName="ByIndex", Mandatory=$true, Position=0)]
[int]$index
, [Parameter(ParameterSetName="ByTitle", Mandatory=$true, Position=0)]
[Alias("Name")]
[string]$Title
, [Parameter()]
$Window = $presentationTitle
)
$null = Invoke-BootsWindow $Window {
# Figure out where we are now, because we'll need to come back
$global:currentSlide = @($global:Frame.BackStack.GetEnumerator()).Count
# Activate the slide they want to delete ...
[int]$removable = Set-Slide @PsBoundParameters -passthru
}
# Separate Invoke's are required for the window to register what's going on?
$global:removable = $true
$null = Invoke-BootsWindow $Window {
if($global:Frame.CanGoForward) {
$global:Frame.GoForward()
} else {
$global:removable = $false
Write-Host "Sorry, you can't remove the last slide in the deck." -Fore Red
}
}
if($global:removable) {
# Separate Invoke's are required for the window to register what's going on?
Invoke-BootsWindow $Window {
$null = $global:Frame.RemoveBackEntry()
Set-Slide $currentSlide
}
}
}
function Show-Slide {
#.Synopsis
# Set the active slide in a deck
#.Description
# Sets the active slide by index or name (title)
[CmdletBinding(DefaultParameterSetName="ByIndex")]
PARAM(
[Parameter(ParameterSetName="ByIndex", Mandatory=$true, Position=0)]
[int]$index
, [Parameter(ParameterSetName="ByTitle", Mandatory=$true, Position=0)]
[Alias("Name")]
[string]$Title
, [Parameter()]
$Window = $presentationTitle
, [Parameter()]
[Switch]$Passthru
)
switch($PSCmdlet.ParameterSetName) {
"ByIndex" {
$null = Invoke-BootsWindow $Window {
# Figure out where we are now...
[int]$global:current = @($global:Frame.BackStack.GetEnumerator()).Count
# Then go where we need to
for(;$current -lt $index -and $global:Frame.CanGoForward ;$global:current++) {
$global:Frame.GoForward()
}
for(;$current -gt $index -and $global:Frame.CanGoBack ;$global:current--) {
$global:Frame.GoBack()
}
}
if($Passthru) { return $global:current }
}
"ByTitle" {
[int]$global:result = -1
[int]$global:current = -1
$null = Invoke-BootsWindow $Window {
# Figure out where we are now...
$global:current = @($global:Frame.BackStack.GetEnumerator()).Count
# Start with backward history ('cause it's more likely)
[int]$i = 0;
foreach($page in $global:Frame.BackStack) {
if($page.Name -eq $Title) {
$global:result = $current - $i - 1
for(;$i -gt $0 -and $global:Frame.CanGoBack;$i--) {
$global:Frame.GoBack()
}
break;
}
$i++
}
# Failing that, try forward history
[int]$i = 0;
foreach($page in $global:Frame.ForwardStack) {
if($page.Name -eq $Title) {
$global:result = $current + $i + 1
for(;$i -gt $0 -and $global:Frame.CanGoForward ;$i--) {
$global:Frame.GoForward()
}
break;
}
$i++
}
}
if($Passthru) {
if($global:result -ge 0) {
return $global:result
} else {
return $global:current
}
}
}
}
}
New-Alias Set-Slide Show-Slide # backwards compat
function Move-NextSlide {
#.Synopsis
# Navigates to the next slide (if there is another)
PARAM(
[Parameter()]
$Window = $presentationTitle
,
[Parameter()]
[Switch]$Passthru
)
$DidGo = Invoke-BootsWindow $Window {
Write-Output $global:Frame.CanGoForward
if($global:Frame.CanGoForward) {
$global:Frame.GoForward()
}
}
if($Passthru){ $DidGo }
}
New-Alias Next-Slide Move-NextSlide # backwards compat
function Move-PreviousSlide {
#.Synopsis
# Navigates to the previous slide (if there is another)
PARAM(
[Parameter()]
$Window = $presentationTitle
,
[Parameter()]
[Switch]$Passthru
)
$DidGo = Invoke-BootsWindow $Window {
Write-Output $global:Frame.CanGoForward
if($global:Frame.CanGoBack) {
$global:Frame.GoBack()
}
}
if($Passthru){ $DidGo }
}
New-Alias Prev-Slide Move-PreviousSlide
New-Alias Previous-Slide Move-PreviousSlide # backwards compat
function Start-Presentation {
#.Synopsis
# Start a presentation window
#.Description
# This is the kick-off function you have to call to create the presentation window
PARAM(
$PresentationTitle = "PowerShell Presentation"
, $BackgroundImagePath = $(Get-ItemProperty "HKCU:\Control Panel\Desktop" | Select -Expand Wallpaper | Get-ChildItem)
)
$BackgroundImagePath = Convert-Path (Resolve-Path $BackgroundImagePath)
$global:PresentationTitle = $PresentationTitle
## Finally, make the slide window
Boots -Title $PresentationTitle `
-Height $([System.Windows.SystemParameters]::PrimaryScreenHeight) `
-Width $([System.Windows.SystemParameters]::PrimaryScreenWidth) `
{
$global:TitleBrush = LinearGradientBrush -Start "0.5,0" -End "0.5,1" {
GradientStop -Color "#FF084670" -Offset 0
GradientStop -Color "#FF084670" -Offset 0.6
GradientStop -Color "#FF000000" -Offset 0.6
}
$global:Frame = Frame -Content {
Page -Title "Welcome" {
Grid {
TextBlock $PresentationTitle `
-FontSize 125 -FontFamily Impact -Foreground White `
-VerticalAlignment Center -TextAlignment Center `
-HorizontalAlignment Stretch -TextWrapping Wrap `
-Effect { BlurEffect -Radius 30 }
TextBlock $PresentationTitle `
-FontSize 125 -FontFamily Impact -Foreground White `
-VerticalAlignment Center -TextAlignment Center `
-HorizontalAlignment Stretch -TextWrapping Wrap `
-Effect { BlurEffect -Radius 30 }
TextBlock $PresentationTitle `
-FontSize 125 -FontFamily Impact -Foreground $global:TitleBrush `
-VerticalAlignment Center -TextAlignment Center `
-HorizontalAlignment Stretch -TextWrapping Wrap `
}
}
}
#Start-Zoom -Root $global:Frame
$global:Frame
} -Async -On_SourceInitialized {
if(Test-Path $BackgroundImagePath) {
$this.Background = ImageBrush -ImageSource $BackgroundImagePath
}
$this.WindowState = "Maximized"
} -On_KeyDown {
trap { Write-Host $( $_ | fl * | Out-String ) }
switch -regex ($_.Key) {
"Escape" {
Set-Zoom 0; break;
}
"OemPlus|Add" {
Set-Zoom +1; break;
}
"OemMinus|Subtract" {
Set-Zoom -1; break;
}
"MediaNextTrack|BrowserForward|Right" {
Next-Slide; break;
}
"MediaPreviousTrack|BrowserBack|Left" {
Previous-Slide; break;
}
}
} -On_MouseWheel {
if($_.Delta -gt 0 ) {
Set-Zoom +1
} else {
Set-Zoom -1
}
}
# -WindowStyle None -AllowsTransparency $true
}
function Test-Presentation {
Start-Presentation "PowerShell for Developers"
Add-TextSlide "Overview" "What is PowerShell?", "How can it help me?", "Should I code for it?", "How do I ..."
Add-TextSlide "What is PowerShell?" "A .Net Console","A new .Net Language","An admin scripting tool","An app automation engine", "All of the above"
ls ~\Pictures\Comix | Add-Slide
Set-Slide 0
## The simple, automatic, force-my-hand slide timer.
## One slide, every 20 seconds:
## go forward and check if we can go forward again
while( Move-NextSlide -Passthru ) {
# note that we have to sleep outside the invoke...
# otherwise the window can't update
sleep 20
}
}
Export-ModuleMember -Function * -Alias *