Skip to content

Commit

Permalink
feature: save screenshots to clipboard option (#257)
Browse files Browse the repository at this point in the history
* adds the ability to save screenshots to clipboard instead

* a little cleanup

* Update screenshot.go

* Update screenshot.go

* Tweak screenshot saving

Move InSelectionMode and ToClipboard setting to config,
so they will persist between user sessions.

* Fix linting

* Call `go mod tidy`

---------

Co-authored-by: SpaiR <klimbetoo@gmail.com>
  • Loading branch information
mc-oofert and SpaiR authored Nov 25, 2023
1 parent 754d662 commit 3a68c7b
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 19 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/sqweek/dialog v0.0.0-20220809060634-e981b270ebbf
github.com/stretchr/testify v1.8.4
golang.design/x/clipboard v0.7.0
)

require (
Expand All @@ -24,7 +25,9 @@ require (
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/image v0.7.0 // indirect
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c // indirect
golang.org/x/sys v0.12.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/SpaiR/imgui-go v1.12.1-0.20220214190844-a0bad21e1c5d h1:QajPUffv+BBqNu54DrL/YrO8RP1BGTfGhPNnoPt1ERk=
github.com/SpaiR/imgui-go v1.12.1-0.20220214190844-a0bad21e1c5d/go.mod h1:bzOQqDbRE58xRB4EGTlUrIhCTWxNvU7YtJckrFf+HpM=
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
Expand Down Expand Up @@ -42,17 +43,28 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.design/x/clipboard v0.7.0 h1:4Je8M/ys9AJumVnl8m+rZnIvstSnYj1fvzqYrU3TXvo=
golang.design/x/clipboard v0.7.0/go.mod h1:PQIvqYO9GP29yINEfsEn5zSQKAz3UgXmZKzDA6dnq2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c h1:Gk61ECugwEHL6IiyyNLXNzmu8XslmRP2dS0xjIYhbb4=
golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
Expand All @@ -63,6 +75,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -86,6 +99,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
Expand Down
6 changes: 5 additions & 1 deletion internal/app/ui/cpwsarea/wsmap/pmap/psettings/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ type psettingsConfig struct {
Version uint

ScreenshotDir string

InSelectionMode bool
ToClipboardMode bool
}

func (psettingsConfig) Name() string {
Expand All @@ -35,7 +38,8 @@ func loadConfig(app App) *psettingsConfig {
cfg := &psettingsConfig{
Version: configVersion,

ScreenshotDir: u.HomeDir,
ScreenshotDir: u.HomeDir,
ToClipboardMode: true,
}
app.ConfigRegister(cfg)
return cfg
Expand Down
98 changes: 80 additions & 18 deletions internal/app/ui/cpwsarea/wsmap/pmap/psettings/screenshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,36 @@ import (
"github.com/SpaiR/imgui-go"
"github.com/rs/zerolog/log"
"github.com/sqweek/dialog"
"golang.design/x/clipboard"
)

type sessionScreenshot struct {
saving bool
inSelectionMode bool
saving bool
}

func (p *Panel) showScreenshot() {
if imgui.CollapsingHeader("Screenshot") {
imgui.BeginDisabledV(cfg.ToClipboardMode)

if imgui.Button(icon.FolderOpen) {
p.selectScreenshotDir()
}

imguiext.SetItemHoveredTooltip("Screenshot Folder")

imgui.SameLine()

imgui.SetNextItemWidth(-1)
imgui.InputText("##screenshot_dir", &cfg.ScreenshotDir)
imguiext.SetItemHoveredTooltip(cfg.ScreenshotDir)

imgui.EndDisabled()

if imgui.Checkbox("Screenshot in Selection", &p.sessionScreenshot.inSelectionMode) {
if imgui.Checkbox("Screenshot in Selection", &cfg.InSelectionMode) {
tools.SetSelected(tools.TNGrab)
}

imgui.Checkbox("To Clipboard", &cfg.ToClipboardMode)

var createBtnLabel string
if p.sessionScreenshot.saving {
createBtnLabel = "Creating" + []string{".", "..", "...", "...."}[int(imgui.Time()/.25)&3] + "###create"
Expand All @@ -64,22 +70,24 @@ func (p *Panel) showScreenshot() {
func (p *Panel) createScreenshot() {
p.sessionScreenshot.saving = true
selectedTool := tools.Selected()
grabCurrentlySelected := selectedTool.Name() == tools.TNGrab

boundX, boundY := float32(0), float32(0)

var width, height int
if p.sessionScreenshot.inSelectionMode {
if !grabCurrentlySelected || !selectedTool.(*tools.ToolGrab).HasSelectedArea() {
if cfg.InSelectionMode {
hasSelectedArea := selectedTool.Name() == tools.TNGrab && selectedTool.(*tools.ToolGrab).HasSelectedArea()
if hasSelectedArea {
bounds := selectedTool.(*tools.ToolGrab).Bounds() //get grab tool bounds, so we can calculate boundX and boundY
width, height = (int(bounds.X2-bounds.X1)+1)*dmmap.WorldIconSize, (int(bounds.Y2-bounds.Y1)+1)*dmmap.WorldIconSize
boundX = -float32((int(bounds.X1) - 1) * dmmap.WorldIconSize) //now change bounds so we can use them in Translate
boundY = -float32((int(bounds.Y1) - 1) * dmmap.WorldIconSize)
} else {
appdialog.Open(appdialog.TypeInformation{
Title: "Nothing selected!",
Information: "Screenshot in Selection is on, but you have nothing selected. Use the grab tool!",
})
p.sessionScreenshot.saving = false
return
} else {
bounds := selectedTool.(*tools.ToolGrab).Bounds() //get grab tool bounds so we can calculate boundX and boundY
width, height = (int(bounds.X2-bounds.X1)+1)*dmmap.WorldIconSize, (int(bounds.Y2-bounds.Y1)+1)*dmmap.WorldIconSize
boundX = -float32((int(bounds.X1) - 1) * dmmap.WorldIconSize) //now change bounds so we can use them in Translate
boundY = -float32((int(bounds.Y1) - 1) * dmmap.WorldIconSize)
}
} else {
width, height = p.editor.Dmm().MaxX*dmmap.WorldIconSize, p.editor.Dmm().MaxY*dmmap.WorldIconSize
Expand All @@ -99,7 +107,7 @@ func (p *Panel) createScreenshot() {
var pixels = c.ReadPixels()

go func() {
if err := saveScreenshot(pixels, width, height); err != nil {
if err := p.saveScreenshot(pixels, width, height); err != nil {
appdialog.Open(appdialog.TypeInformation{
Title: "Error: Screenshot Creation",
Information: fmt.Sprint("Unable to create screenshot:", err),
Expand All @@ -124,14 +132,68 @@ func (p *Panel) ProcessUnit(u unit.Unit) bool {
return p.app.PathsFilter().IsVisiblePath(u.Instance().Prefab().Path())
}

func saveScreenshot(pixels []byte, w, h int) error {
if err := os.MkdirAll(cfg.ScreenshotDir, os.ModeDir); err != nil {
log.Print("unable to create screenshot directory:", err)
func (p *Panel) saveScreenshot(pixels []byte, w, h int) error {
if !cfg.ToClipboardMode {
if err := os.MkdirAll(cfg.ScreenshotDir, os.ModeDir); err != nil {
log.Print("unable to create screenshot directory:", err)
return err
}
}

var directory string
if cfg.ToClipboardMode {
directory = os.TempDir()
} else {
directory = cfg.ScreenshotDir
}

dstFilePath := directory + "/StrongDMM-" + time.Now().Format(util.TimeFormat) + ".png"

if err := saveScreenshotToFile(dstFilePath, pixels, w, h); err != nil {
log.Print("unable to save screenshot to file:", err)
return err
}

if cfg.ToClipboardMode {
if err := saveScreenshotToClipboard(dstFilePath); err != nil {
log.Print("unable to save screenshot to clipboard:", err)
return err
}
}

return nil
}

func saveScreenshotToFile(dstFilePath string, pixels []byte, w, h int) error {
out, err := os.Create(dstFilePath)
if err != nil {
log.Print("unable to create screenshot file:", dstFilePath, err)
return err
}

out, _ := os.Create(cfg.ScreenshotDir + "/" + time.Now().Format(util.TimeFormat) + ".png")
err = png.Encode(out, util.PixelsToRGBA(pixels, w, h))
defer out.Close()
return err
}

func saveScreenshotToClipboard(srcFilePath string) error {
if err := clipboard.Init(); err != nil {
log.Print("unable to init clipboard:", err)
return err
}

contents, err := os.ReadFile(srcFilePath)
if err != nil {
log.Print("unable to read resulting screenshot file:", err)
return err
}

clipboard.Write(clipboard.FmtImage, contents)

if err := os.Remove(srcFilePath); err != nil {
log.Print("unable to delete clipboard screenshot file:", err)
return err
}

return png.Encode(out, util.PixelsToRGBA(pixels, w, h))
return nil
}

0 comments on commit 3a68c7b

Please sign in to comment.