Screenshot: Add select area option
Add 'Select area' button to Screenshot that when clicked, allows the use of crosshairs to select a region to screenshot; functionally similar to and inspired by Snipping Tool/Win+Shift+S on Windows and Cmd+Ctrl+4 on Mac when selecting using the primary mouse button. When using the secondary mouse button or clicking while shift is held, the selected area can be resized and moved, and confirmed by pressing enter or double clicking. Also, add a shortcut such that if the super key is held when prtscrn is pressed, Screenshot will be launched in area select mode. Also, add radio buttons to select between whole screen, active window, and selected area, and disable active window, window border, and selected area options if inapplicable. See #18568 and #13456 Change-Id: Ida0f441e65154c9cd7788de32a606ab4ca8dc31e Reviewed-on: https://review.haiku-os.org/c/haiku/+/9069 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
@@ -7,6 +7,7 @@ UsePrivateHeaders interface shared ;
|
||||
Application Screenshot :
|
||||
ScreenshotApp.cpp
|
||||
ScreenshotWindow.cpp
|
||||
SelectAreaView.cpp
|
||||
Utility.cpp
|
||||
: be localestub tracker translation [ TargetLibsupc++ ]
|
||||
: ScreenshotApp.rdef
|
||||
|
||||
@@ -98,6 +98,9 @@ Screenshot::ArgvReceived(int32 argc, char** argv)
|
||||
} else if (strcmp(argv[i], "-c") == 0
|
||||
|| strcmp(argv[i], "--clipboard") == 0)
|
||||
copyToClipboard = true;
|
||||
else if (strcmp(argv[i], "-a") == 0
|
||||
|| strcmp(argv[i], "--area") == 0)
|
||||
fSelectArea = true;
|
||||
else if (i == argc - 1)
|
||||
outputFilename = argv[i];
|
||||
}
|
||||
@@ -107,8 +110,8 @@ Screenshot::ArgvReceived(int32 argc, char** argv)
|
||||
if (copyToClipboard || saveScreenshotSilent) {
|
||||
fLaunchGui = false;
|
||||
|
||||
BBitmap* screenshot = fUtility->MakeScreenshot(includeCursor,
|
||||
grabActiveWindow, includeBorder);
|
||||
BBitmap* screenshot = fUtility->MakeScreenshot(includeCursor, includeBorder,
|
||||
grabActiveWindow ? kActiveWindow : kWholeScreen);
|
||||
|
||||
if (screenshot == NULL)
|
||||
return;
|
||||
@@ -153,6 +156,8 @@ Screenshot::ReadyToRun()
|
||||
message.AddRect("tabFrame", fUtility->tabFrame);
|
||||
message.AddFloat("borderSize", fUtility->borderSize);
|
||||
|
||||
message.AddBool("selectArea", fSelectArea);
|
||||
|
||||
be_roster->Launch("application/x-vnd.haiku-screenshot", &message);
|
||||
}
|
||||
|
||||
@@ -185,9 +190,10 @@ Screenshot::_ShowHelp()
|
||||
printf(" -c, --clipboard Copies the screenshot to the system "
|
||||
"clipboard without\n showing the application "
|
||||
"window\n");
|
||||
printf("\n");
|
||||
printf("Note: OPTION -b, --border takes only effect when used with -w, "
|
||||
"--window\n");
|
||||
printf(" -a, --area Select an area of the screen to capture \n");
|
||||
printf("Note:\nOption -b, --border only takes effect when used with -w, "
|
||||
"--window\nOption -a, --area does not take effect with -s, "
|
||||
"--silent or -c, --clipboard\n");
|
||||
|
||||
fLaunchGui = false;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ private:
|
||||
private:
|
||||
Utility* fUtility;
|
||||
bool fLaunchGui;
|
||||
bool fSelectArea;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -15,17 +15,22 @@
|
||||
#include <Catalog.h>
|
||||
#include <Locale.h>
|
||||
#include <Roster.h>
|
||||
#include <Screen.h>
|
||||
#include <WindowPrivate.h>
|
||||
|
||||
#include "ScreenshotWindow.h"
|
||||
#include "SelectAreaView.h"
|
||||
#include "Utility.h"
|
||||
|
||||
|
||||
ScreenshotApp::ScreenshotApp()
|
||||
:
|
||||
BApplication("application/x-vnd.haiku-screenshot"),
|
||||
fScreenshotWindow(NULL),
|
||||
fUtility(new Utility),
|
||||
fSilent(false),
|
||||
fClipboard(false)
|
||||
fClipboard(false),
|
||||
fLaunchWithAreaSelect(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -80,6 +85,39 @@ ScreenshotApp::MessageReceived(BMessage* message)
|
||||
if (status != B_OK)
|
||||
break;
|
||||
|
||||
status = message->FindBool("selectArea", &fLaunchWithAreaSelect);
|
||||
if (status != B_OK)
|
||||
break;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SS_SELECT_AREA_FRAME:
|
||||
{
|
||||
BRect frame;
|
||||
status_t status = message->FindRect("selectAreaFrame", &frame);
|
||||
if (status != B_OK)
|
||||
break;
|
||||
|
||||
if (!frame.IsValid() && fLaunchWithAreaSelect)
|
||||
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||
else if (fScreenshotWindow == NULL) {
|
||||
fScreenshotWindow = new ScreenshotWindow(*fUtility, fSilent, fClipboard);
|
||||
fLaunchWithAreaSelect = false;
|
||||
}
|
||||
|
||||
fScreenshotWindow->SetSelectedArea(frame);
|
||||
break;
|
||||
}
|
||||
|
||||
case SS_LAUNCH_AREA_SELECTOR:
|
||||
{
|
||||
BWindow* selectAreaWindow = new BWindow(BScreen().Frame(),
|
||||
"Area Window", kWindowScreenWindow,
|
||||
B_ASYNCHRONOUS_CONTROLS|B_NOT_RESIZABLE|B_NOT_CLOSABLE|B_NOT_ZOOMABLE);
|
||||
|
||||
selectAreaWindow->AddChild(new SelectAreaView(fUtility->wholeScreen));
|
||||
selectAreaWindow->Show();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -110,7 +148,10 @@ ScreenshotApp::ArgvReceived(int32 argc, char** argv)
|
||||
void
|
||||
ScreenshotApp::ReadyToRun()
|
||||
{
|
||||
new ScreenshotWindow(*fUtility, fSilent, fClipboard);
|
||||
if (fLaunchWithAreaSelect)
|
||||
be_app->PostMessage(new BMessage(SS_LAUNCH_AREA_SELECTOR));
|
||||
else
|
||||
fScreenshotWindow = new ScreenshotWindow(*fUtility, fSilent, fClipboard);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
#include <Catalog.h>
|
||||
|
||||
|
||||
class ScreenshotWindow;
|
||||
class Utility;
|
||||
|
||||
|
||||
class ScreenshotApp : public BApplication {
|
||||
public:
|
||||
ScreenshotApp();
|
||||
@@ -26,9 +26,12 @@ public:
|
||||
void ReadyToRun();
|
||||
|
||||
private:
|
||||
ScreenshotWindow*
|
||||
fScreenshotWindow;
|
||||
Utility* fUtility;
|
||||
bool fSilent;
|
||||
bool fClipboard;
|
||||
bool fLaunchWithAreaSelect;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Fredrik Modéen
|
||||
* Christophe Huriaux
|
||||
* Wim van der Meer
|
||||
* Pawan Yerramilli
|
||||
*/
|
||||
|
||||
|
||||
@@ -34,6 +35,7 @@
|
||||
#include <MenuItem.h>
|
||||
#include <MessageFilter.h>
|
||||
#include <Path.h>
|
||||
#include <RadioButton.h>
|
||||
#include <Roster.h>
|
||||
#include <SeparatorView.h>
|
||||
#include <SpaceLayoutItem.h>
|
||||
@@ -43,6 +45,7 @@
|
||||
#include <TranslationUtils.h>
|
||||
#include <TranslatorRoster.h>
|
||||
|
||||
#include "Control.h"
|
||||
#include "Utility.h"
|
||||
|
||||
|
||||
@@ -51,7 +54,6 @@
|
||||
|
||||
|
||||
enum {
|
||||
kActiveWindow,
|
||||
kIncludeBorder,
|
||||
kIncludeCursor,
|
||||
kNewScreenshot,
|
||||
@@ -60,7 +62,8 @@ enum {
|
||||
kChooseLocation,
|
||||
kSaveScreenshot,
|
||||
kSettings,
|
||||
kCloseTranslatorSettings
|
||||
kCloseTranslatorSettings,
|
||||
kSelectArea
|
||||
};
|
||||
|
||||
|
||||
@@ -123,7 +126,7 @@ ScreenshotWindow::ScreenshotWindow(const Utility& utility, bool silent,
|
||||
fDelay(0),
|
||||
fIncludeBorder(false),
|
||||
fIncludeCursor(false),
|
||||
fGrabActiveWindow(false),
|
||||
fShotType(kWholeScreen),
|
||||
fOutputFilename(NULL),
|
||||
fExtension(""),
|
||||
fImageFileType(B_PNG_FORMAT)
|
||||
@@ -144,20 +147,32 @@ ScreenshotWindow::ScreenshotWindow(const Utility& utility, bool silent,
|
||||
return;
|
||||
}
|
||||
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fGrabActiveWindow,
|
||||
fIncludeBorder);
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fIncludeBorder, fShotType);
|
||||
|
||||
fActiveWindow = new BCheckBox(B_TRANSLATE("Capture active window"),
|
||||
fWholeScreen = new BRadioButton(B_TRANSLATE("Whole screen"),
|
||||
new BMessage(kWholeScreen));
|
||||
|
||||
fActiveWindow = new BRadioButton(B_TRANSLATE("Active window"),
|
||||
new BMessage(kActiveWindow));
|
||||
if (fGrabActiveWindow)
|
||||
|
||||
fAreaSelect = new BRadioButton(B_TRANSLATE("Selected area"),
|
||||
new BMessage(kShowSelectedArea));
|
||||
fAreaSelect->SetEnabled(false);
|
||||
fActiveWindow->SetEnabled(fUtility.activeWindowFrame.IsValid());
|
||||
|
||||
if (fShotType == kShowSelectedArea && fAreaSelect->IsEnabled())
|
||||
fAreaSelect->SetValue(B_CONTROL_ON);
|
||||
else if (fShotType == kActiveWindow && fUtility.activeWindowFrame.IsValid())
|
||||
fActiveWindow->SetValue(B_CONTROL_ON);
|
||||
else
|
||||
fWholeScreen->SetValue(B_CONTROL_ON);
|
||||
|
||||
fWindowBorder = new BCheckBox(B_TRANSLATE("Include window border"),
|
||||
new BMessage(kIncludeBorder));
|
||||
if (fIncludeBorder)
|
||||
fWindowBorder->SetValue(B_CONTROL_ON);
|
||||
if (!fGrabActiveWindow)
|
||||
if (fShotType != kActiveWindow || !fUtility.activeWindowFrame.IsValid())
|
||||
fWindowBorder->SetEnabled(false);
|
||||
if (fIncludeBorder && fUtility.activeWindowFrame.IsValid())
|
||||
fWindowBorder->SetValue(B_CONTROL_ON);
|
||||
|
||||
fShowCursor = new BCheckBox(B_TRANSLATE("Include mouse pointer"),
|
||||
new BMessage(kIncludeCursor));
|
||||
@@ -206,7 +221,10 @@ ScreenshotWindow::ScreenshotWindow(const Utility& utility, bool silent,
|
||||
B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING)
|
||||
.Add(previewBox)
|
||||
.AddGroup(B_VERTICAL, 0)
|
||||
.Add(fWholeScreen)
|
||||
.Add(fActiveWindow)
|
||||
.Add(fAreaSelect)
|
||||
.AddStrut(kSpacing)
|
||||
.Add(fWindowBorder)
|
||||
.Add(fShowCursor)
|
||||
.AddStrut(kSpacing)
|
||||
@@ -236,6 +254,8 @@ ScreenshotWindow::ScreenshotWindow(const Utility& utility, bool silent,
|
||||
new BMessage(B_COPY)))
|
||||
.Add(new BButton("", B_TRANSLATE("New screenshot"),
|
||||
new BMessage(kNewScreenshot)))
|
||||
.Add(new BButton("", B_TRANSLATE("Select area"),
|
||||
new BMessage(kSelectArea)))
|
||||
.AddGlue()
|
||||
.Add(saveScreenshot);
|
||||
|
||||
@@ -264,31 +284,45 @@ ScreenshotWindow::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case kActiveWindow:
|
||||
fGrabActiveWindow = false;
|
||||
if (fActiveWindow->Value() == B_CONTROL_ON)
|
||||
fGrabActiveWindow = true;
|
||||
|
||||
fWindowBorder->SetEnabled(fGrabActiveWindow);
|
||||
fShotType = kActiveWindow;
|
||||
fWindowBorder->SetEnabled(true);
|
||||
|
||||
delete fScreenshot;
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor,
|
||||
fGrabActiveWindow, fIncludeBorder);
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fIncludeBorder, fShotType);
|
||||
_UpdatePreviewPanel();
|
||||
break;
|
||||
|
||||
case kWholeScreen:
|
||||
fShotType = kWholeScreen;
|
||||
fWindowBorder->SetEnabled(false);
|
||||
|
||||
delete fScreenshot;
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fIncludeBorder, fShotType);
|
||||
_UpdatePreviewPanel();
|
||||
break;
|
||||
|
||||
case kShowSelectedArea:
|
||||
fShotType = kShowSelectedArea;
|
||||
fWindowBorder->SetEnabled(false);
|
||||
|
||||
delete fScreenshot;
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fIncludeBorder, fShotType,
|
||||
fSelectedArea);
|
||||
_UpdatePreviewPanel();
|
||||
break;
|
||||
|
||||
case kIncludeBorder:
|
||||
fIncludeBorder = (fWindowBorder->Value() == B_CONTROL_ON);
|
||||
delete fScreenshot;
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor,
|
||||
fGrabActiveWindow, fIncludeBorder);
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fIncludeBorder, fShotType);
|
||||
_UpdatePreviewPanel();
|
||||
break;
|
||||
|
||||
case kIncludeCursor:
|
||||
fIncludeCursor = (fShowCursor->Value() == B_CONTROL_ON);
|
||||
delete fScreenshot;
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor,
|
||||
fGrabActiveWindow, fIncludeBorder);
|
||||
fScreenshot = fUtility.MakeScreenshot(fIncludeCursor, fIncludeBorder, fShotType,
|
||||
fSelectedArea);
|
||||
_UpdatePreviewPanel();
|
||||
break;
|
||||
|
||||
@@ -374,6 +408,10 @@ ScreenshotWindow::MessageReceived(BMessage* message)
|
||||
fSettingsWindow = NULL;
|
||||
break;
|
||||
|
||||
case kSelectArea:
|
||||
be_app->PostMessage(new BMessage(SS_LAUNCH_AREA_SELECTOR));
|
||||
break;
|
||||
|
||||
default:
|
||||
BWindow::MessageReceived(message);
|
||||
break;
|
||||
@@ -391,7 +429,21 @@ ScreenshotWindow::Quit()
|
||||
|
||||
|
||||
void
|
||||
ScreenshotWindow::_NewScreenshot(bool silent, bool clipboard, bool ignoreDelay)
|
||||
ScreenshotWindow::SetSelectedArea(BRect frame)
|
||||
{
|
||||
if (frame.IsValid()) {
|
||||
Lock();
|
||||
fSelectedArea = frame;
|
||||
fAreaSelect->SetEnabled(true);
|
||||
fAreaSelect->SetValue(B_CONTROL_ON);
|
||||
PostMessage(new BMessage(kShowSelectedArea));
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScreenshotWindow::_NewScreenshot(bool silent, bool clipboard, bool ignoreDelay, bool selectArea)
|
||||
{
|
||||
BMessage message(B_ARGV_RECEIVED);
|
||||
int32 argc = 1;
|
||||
@@ -405,6 +457,11 @@ ScreenshotWindow::_NewScreenshot(bool silent, bool clipboard, bool ignoreDelay)
|
||||
message.AddString("argv", delay);
|
||||
}
|
||||
|
||||
if (selectArea) {
|
||||
argc++;
|
||||
message.AddString("argv", "--select");
|
||||
}
|
||||
|
||||
if (silent || clipboard) {
|
||||
if (silent) {
|
||||
argc++;
|
||||
@@ -422,7 +479,7 @@ ScreenshotWindow::_NewScreenshot(bool silent, bool clipboard, bool ignoreDelay)
|
||||
argc++;
|
||||
message.AddString("argv", "--mouse-pointer");
|
||||
}
|
||||
if (fGrabActiveWindow) {
|
||||
if (fShotType == kActiveWindow) {
|
||||
argc++;
|
||||
message.AddString("argv", "--window");
|
||||
}
|
||||
@@ -805,11 +862,14 @@ ScreenshotWindow::_ReadSettings()
|
||||
|
||||
if (settings.FindInt32("type", &fImageFileType) != B_OK)
|
||||
fImageFileType = B_PNG_FORMAT;
|
||||
bool activeWindow = false;
|
||||
settings.FindBool("includeBorder", &fIncludeBorder);
|
||||
settings.FindBool("includeCursor", &fIncludeCursor);
|
||||
settings.FindBool("grabActiveWindow", &fGrabActiveWindow);
|
||||
settings.FindBool("grabActiveWindow", &activeWindow);
|
||||
settings.FindInt64("delay", &fDelay);
|
||||
settings.FindString("outputFilename", &fOutputFilename);
|
||||
if (activeWindow)
|
||||
fShotType = kActiveWindow;
|
||||
|
||||
_SetupOutputPathMenu(settings);
|
||||
}
|
||||
@@ -826,7 +886,7 @@ ScreenshotWindow::_WriteSettings()
|
||||
settings.AddInt32("type", fImageFileType);
|
||||
settings.AddBool("includeBorder", fIncludeBorder);
|
||||
settings.AddBool("includeCursor", fIncludeCursor);
|
||||
settings.AddBool("grabActiveWindow", fGrabActiveWindow);
|
||||
settings.AddBool("grabActiveWindow", fShotType == kActiveWindow);
|
||||
settings.AddInt64("delay", fDelay);
|
||||
settings.AddString("outputFilename", fOutputFilename);
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#define SCREENSHOT_WINDOW_H
|
||||
|
||||
|
||||
#include "Rect.h"
|
||||
#include "Utility.h"
|
||||
#include <RadioButton.h>
|
||||
#include <String.h>
|
||||
#include <TranslationDefs.h>
|
||||
#include <TranslatorFormats.h>
|
||||
@@ -26,22 +29,22 @@ class BPath;
|
||||
class BTextControl;
|
||||
class BTextView;
|
||||
|
||||
class Utility;
|
||||
|
||||
|
||||
class ScreenshotWindow : public BWindow {
|
||||
public:
|
||||
ScreenshotWindow(const Utility& utility,
|
||||
bool silent, bool clipboard);
|
||||
ScreenshotWindow(const Utility& utility, bool silent,
|
||||
bool clipboard);
|
||||
~ScreenshotWindow();
|
||||
|
||||
void MessageReceived(BMessage* message);
|
||||
void Quit();
|
||||
void SetSelectedArea(BRect frame);
|
||||
|
||||
private:
|
||||
void _NewScreenshot(bool silent = false,
|
||||
bool clipboard = false,
|
||||
bool ignoreDelay = false);
|
||||
bool ignoreDelay = false,
|
||||
bool selectArea = false);
|
||||
void _UpdatePreviewPanel();
|
||||
void _DisallowChar(BTextView* textView);
|
||||
void _SetupOutputPathMenu(const BMessage& settings);
|
||||
@@ -61,7 +64,9 @@ private:
|
||||
const Utility& fUtility;
|
||||
|
||||
BView* fPreview;
|
||||
BCheckBox* fActiveWindow;
|
||||
BRadioButton* fActiveWindow;
|
||||
BRadioButton* fAreaSelect;
|
||||
BRadioButton* fWholeScreen;
|
||||
BTextControl* fDelayControl;
|
||||
BCheckBox* fWindowBorder;
|
||||
BCheckBox* fShowCursor;
|
||||
@@ -72,14 +77,16 @@ private:
|
||||
BFilePanel* fOutputPathPanel;
|
||||
BMenuItem* fLastSelectedPath;
|
||||
BWindow* fSettingsWindow;
|
||||
BWindow* fSelectAreaWindow;
|
||||
|
||||
bigtime_t fDelay;
|
||||
bool fIncludeBorder;
|
||||
bool fIncludeCursor;
|
||||
bool fGrabActiveWindow;
|
||||
ShotType fShotType;
|
||||
BString fOutputFilename;
|
||||
BString fExtension;
|
||||
int32 fImageFileType;
|
||||
BRect fSelectedArea;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright 2025, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT license.
|
||||
*
|
||||
* Authors:
|
||||
* Pawan Yerramilli <me@pawanyerramilli.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "SelectAreaView.h"
|
||||
|
||||
#include <Application.h>
|
||||
#include <Bitmap.h>
|
||||
#include <ControlLook.h>
|
||||
#include <Screen.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "Cursor.h"
|
||||
#include "GraphicsDefs.h"
|
||||
#include "InterfaceDefs.h"
|
||||
#include "Point.h"
|
||||
#include "Rect.h"
|
||||
#include "Utility.h"
|
||||
|
||||
|
||||
SelectAreaView::SelectAreaView(BBitmap* frame)
|
||||
:
|
||||
BView(BScreen().Frame(), "", B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE),
|
||||
fScreenShot(frame),
|
||||
fCursor(BCursor(B_CURSOR_ID_CROSS_HAIR)),
|
||||
fIsCurrentlyDragging(false),
|
||||
fStartCorner(B_ORIGIN),
|
||||
fEndCorner(B_ORIGIN),
|
||||
fResizable(false),
|
||||
fCurrentHandle(DRAG_NO_HANDLE),
|
||||
fMoveDelta(-1, -1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::AttachedToWindow()
|
||||
{
|
||||
MakeFocus(true);
|
||||
|
||||
BBitmap* darkenedShot = new BBitmap(Bounds(), fScreenShot->ColorSpace(), true);
|
||||
BView* darkenedView = new BView(darkenedShot->Bounds(), "", B_FOLLOW_NONE, 0);
|
||||
darkenedShot->AddChild(darkenedView);
|
||||
darkenedShot->Lock();
|
||||
darkenedView->DrawBitmap(fScreenShot);
|
||||
darkenedView->SetDrawingMode(B_OP_ALPHA);
|
||||
if (ui_color(B_PANEL_BACKGROUND_COLOR).IsDark()) {
|
||||
darkenedView->SetHighColor(255, 255, 255, 128);
|
||||
SetHighColor(255, 255, 255, 255);
|
||||
} else {
|
||||
darkenedView->SetHighColor(0, 0, 0, 128);
|
||||
SetHighColor(0, 0, 0, 0);
|
||||
}
|
||||
darkenedView->FillRect(Bounds());
|
||||
darkenedView->Sync();
|
||||
SetViewBitmap(darkenedShot);
|
||||
darkenedShot->RemoveChild(darkenedView);
|
||||
delete darkenedView;
|
||||
delete darkenedShot;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::Draw(BRect updateRect)
|
||||
{
|
||||
BRect frame = _CurrentFrame();
|
||||
SetViewCursor(&fCursor);
|
||||
DrawBitmap(fScreenShot, frame, frame);
|
||||
|
||||
rgb_color currentLow = LowColor();
|
||||
SetLowColor(255, 0, 255, 255);
|
||||
StrokeRect(frame, B_MIXED_COLORS);
|
||||
if (fResizable && frame.Width() > 20 && frame.Height() > 20) {
|
||||
FillRect(_GetHandle(DRAG_TOP_LEFT), B_MIXED_COLORS);
|
||||
FillRect(_GetHandle(DRAG_TOP_RIGHT), B_MIXED_COLORS);
|
||||
FillRect(_GetHandle(DRAG_BOTTOM_LEFT), B_MIXED_COLORS);
|
||||
FillRect(_GetHandle(DRAG_BOTTOM_RIGHT), B_MIXED_COLORS);
|
||||
}
|
||||
SetLowColor(currentLow);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::MessageReceived(BMessage* message)
|
||||
{
|
||||
int32 clicks = 0;
|
||||
if (message->what == B_MOUSE_DOWN && message->FindInt32("clicks", &clicks) == B_OK
|
||||
&& clicks == 2) {
|
||||
_SaveSelection();
|
||||
} else {
|
||||
BView::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::KeyDown(const char* bytes, int32 numBytes)
|
||||
{
|
||||
if (bytes[0] == B_ESCAPE) {
|
||||
BMessage message(SS_SELECT_AREA_FRAME);
|
||||
message.AddRect("selectAreaFrame", BRect(0, 0, -1, -1));
|
||||
be_app->PostMessage(&message);
|
||||
Window()->Quit();
|
||||
} else if (bytes[0] == B_ENTER)
|
||||
_SaveSelection();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::MouseDown(BPoint point)
|
||||
{
|
||||
BPoint mouseLoc;
|
||||
uint32 buttons;
|
||||
GetMouse(&mouseLoc, &buttons);
|
||||
uint32 shiftDown = modifiers() & B_SHIFT_KEY;
|
||||
Handle handle = _FindSelectedHandle(point);
|
||||
|
||||
if (fStartCorner == fEndCorner) {
|
||||
fResizable = (buttons & B_SECONDARY_MOUSE_BUTTON) || shiftDown;
|
||||
fIsCurrentlyDragging = true;
|
||||
fStartCorner = fEndCorner = point;
|
||||
} else if (fResizable && handle != DRAG_NO_HANDLE) {
|
||||
fCurrentHandle = handle;
|
||||
} else if (fResizable && _CurrentFrame().Contains(point)) {
|
||||
fMoveDelta = point;
|
||||
fCursor = B_CURSOR_ID_GRABBING;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
|
||||
{
|
||||
if (fIsCurrentlyDragging && point != fEndCorner)
|
||||
fEndCorner = point;
|
||||
|
||||
Handle hoveredHandle = fCurrentHandle == DRAG_NO_HANDLE ?
|
||||
_FindSelectedHandle(point) : fCurrentHandle;
|
||||
if (fResizable && (hoveredHandle != DRAG_NO_HANDLE)) {
|
||||
switch(hoveredHandle) {
|
||||
case DRAG_TOP_LEFT:
|
||||
fCursor = B_CURSOR_ID_RESIZE_NORTH_WEST;
|
||||
break;
|
||||
case DRAG_TOP_RIGHT:
|
||||
fCursor = B_CURSOR_ID_RESIZE_NORTH_EAST;
|
||||
break;
|
||||
case DRAG_BOTTOM_LEFT:
|
||||
fCursor = B_CURSOR_ID_RESIZE_SOUTH_WEST;
|
||||
break;
|
||||
case DRAG_BOTTOM_RIGHT:
|
||||
fCursor = B_CURSOR_ID_RESIZE_SOUTH_EAST;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (fResizable && !_CurrentFrame().Contains(point))
|
||||
fCursor = B_CURSOR_ID_SYSTEM_DEFAULT;
|
||||
else if (fMoveDelta != BPoint(-1, -1))
|
||||
fCursor = B_CURSOR_ID_GRABBING;
|
||||
else if (fResizable && _CurrentFrame().Contains(point))
|
||||
fCursor = B_CURSOR_ID_GRAB;
|
||||
else
|
||||
fCursor = B_CURSOR_ID_CROSS_HAIR;
|
||||
|
||||
if (fCurrentHandle != DRAG_NO_HANDLE) {
|
||||
switch(fCurrentHandle) {
|
||||
case DRAG_TOP_LEFT:
|
||||
fStartCorner = point;
|
||||
break;
|
||||
case DRAG_TOP_RIGHT:
|
||||
fStartCorner.y = point.y;
|
||||
fEndCorner.x = point.x;
|
||||
break;
|
||||
case DRAG_BOTTOM_LEFT:
|
||||
fStartCorner.x = point.x;
|
||||
fEndCorner.y = point.y;
|
||||
break;
|
||||
case DRAG_BOTTOM_RIGHT:
|
||||
fEndCorner = point;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fMoveDelta != BPoint(-1, -1)) {
|
||||
fStartCorner += (point - fMoveDelta);
|
||||
fEndCorner += (point - fMoveDelta);
|
||||
fStartCorner.ConstrainTo(Window()->Frame());
|
||||
fEndCorner.ConstrainTo(Window()->Frame());
|
||||
fMoveDelta = point;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::MouseUp(BPoint point)
|
||||
{
|
||||
if (!fResizable && fIsCurrentlyDragging && fStartCorner != fEndCorner) {
|
||||
_SaveSelection();
|
||||
} else if (fResizable) {
|
||||
fIsCurrentlyDragging = false;
|
||||
BRect frame = _CurrentFrame();
|
||||
fStartCorner = frame.LeftTop();
|
||||
fEndCorner = frame.RightBottom();
|
||||
fCurrentHandle = DRAG_NO_HANDLE;
|
||||
if (fMoveDelta != BPoint(-1, -1))
|
||||
fCursor = B_CURSOR_ID_GRAB;
|
||||
fMoveDelta = BPoint(-1, -1);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SelectAreaView::_SaveSelection()
|
||||
{
|
||||
BMessage message(SS_SELECT_AREA_FRAME);
|
||||
message.AddRect("selectAreaFrame", _CurrentFrame());
|
||||
be_app->PostMessage(&message);
|
||||
Window()->Quit();
|
||||
}
|
||||
|
||||
|
||||
BRect
|
||||
SelectAreaView::_CurrentFrame()
|
||||
{
|
||||
BPoint topLeft = BPoint(min_c(fStartCorner.x, fEndCorner.x),
|
||||
min_c(fStartCorner.y, fEndCorner.y));
|
||||
BPoint bottomRight = BPoint(max_c(fStartCorner.x, fEndCorner.x),
|
||||
max_c(fStartCorner.y, fEndCorner.y));
|
||||
return BRect(topLeft, bottomRight);
|
||||
}
|
||||
|
||||
|
||||
Handle
|
||||
SelectAreaView::_FindSelectedHandle(BPoint point)
|
||||
{
|
||||
for (int i = DRAG_TOP_LEFT; i < DRAG_NO_HANDLE; i++) {
|
||||
if (_GetHandle((Handle)i).Contains(point))
|
||||
return (Handle)i;
|
||||
}
|
||||
|
||||
return DRAG_NO_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
BRect
|
||||
SelectAreaView::_GetHandle(Handle handle)
|
||||
{
|
||||
BRect frame = _CurrentFrame();
|
||||
int size = be_control_look->ComposeIconSize(8).IntegerWidth();
|
||||
switch (handle) {
|
||||
case DRAG_TOP_LEFT:
|
||||
return BRect(frame.LeftTop(), frame.LeftTop() + BPoint(size, size));
|
||||
case DRAG_TOP_RIGHT:
|
||||
return BRect(frame.RightTop() - BPoint(size, 0), frame.RightTop() + BPoint(0, size));
|
||||
case DRAG_BOTTOM_LEFT:
|
||||
return BRect(frame.LeftBottom() - BPoint(0, size), frame.LeftBottom() + BPoint(size, 0));
|
||||
case DRAG_BOTTOM_RIGHT:
|
||||
return BRect(frame.RightBottom() - BPoint(size, size), frame.RightBottom());
|
||||
default:
|
||||
return BRect(0, 0, -1, -1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2025, Haiku, Inc.
|
||||
* Authors:
|
||||
* Pawan Yerramilli <me@pawanyerramilli.com>
|
||||
* All rights reserved. Distributed under the terms of the MIT license.
|
||||
*/
|
||||
|
||||
|
||||
#include "Point.h"
|
||||
#include <Cursor.h>
|
||||
#include <SupportDefs.h>
|
||||
#include <View.h>
|
||||
|
||||
|
||||
enum Handle {
|
||||
DRAG_TOP_LEFT,
|
||||
DRAG_TOP_RIGHT,
|
||||
DRAG_BOTTOM_LEFT,
|
||||
DRAG_BOTTOM_RIGHT,
|
||||
DRAG_NO_HANDLE
|
||||
};
|
||||
|
||||
class SelectAreaView : public BView {
|
||||
public:
|
||||
SelectAreaView(BBitmap* screenshot);
|
||||
|
||||
void AttachedToWindow();
|
||||
void MessageReceived(BMessage* message);
|
||||
void Draw(BRect updateRect);
|
||||
void KeyDown(const char* bytes, int32 numBytes);
|
||||
void MouseDown(BPoint point);
|
||||
void MouseMoved(BPoint point, uint32 transit, const BMessage* message);
|
||||
void MouseUp(BPoint point);
|
||||
|
||||
private:
|
||||
BRect _CurrentFrame();
|
||||
BRect _GetHandle(Handle handle);
|
||||
Handle _FindSelectedHandle(BPoint point);
|
||||
void _SaveSelection();
|
||||
|
||||
BBitmap* fScreenShot;
|
||||
BCursor fCursor;
|
||||
bool fIsCurrentlyDragging;
|
||||
BPoint fStartCorner;
|
||||
BPoint fEndCorner;
|
||||
bool fResizable;
|
||||
Handle fCurrentHandle;
|
||||
BPoint fMoveDelta;
|
||||
};
|
||||
@@ -133,8 +133,8 @@ Utility::Save(BBitmap* screenshot, const char* fileName, uint32 imageType)
|
||||
|
||||
|
||||
BBitmap*
|
||||
Utility::MakeScreenshot(bool includeMouse, bool activeWindow,
|
||||
bool includeBorder) const
|
||||
Utility::MakeScreenshot(bool includeMouse, bool includeBorder, ShotType type,
|
||||
BRect selectedArea) const
|
||||
{
|
||||
if (wholeScreen == NULL)
|
||||
return NULL;
|
||||
@@ -151,7 +151,19 @@ Utility::MakeScreenshot(bool includeMouse, bool activeWindow,
|
||||
|
||||
BBitmap* screenshot = NULL;
|
||||
|
||||
if (activeWindow && activeWindowFrame.IsValid()) {
|
||||
if (type == kShowSelectedArea && selectedArea.IsValid()) {
|
||||
BBitmap* cropShot = new BBitmap(selectedArea.OffsetToCopy(B_ORIGIN),
|
||||
wholeScreen->ColorSpace(), true);
|
||||
BView* cropView = new BView(cropShot->Bounds(), "", B_FOLLOW_NONE, 0);
|
||||
cropShot->AddChild(cropView);
|
||||
cropShot->Lock();
|
||||
cropView->DrawBitmap(wholeScreen, selectedArea, cropView->Bounds());
|
||||
cropView->Sync();
|
||||
screenshot = new BBitmap(cropShot);
|
||||
cropShot->RemoveChild(cropView);
|
||||
delete cropView;
|
||||
delete cropShot;
|
||||
} else if (type == kActiveWindow && activeWindowFrame.IsValid()) {
|
||||
BRect frame(activeWindowFrame);
|
||||
if (includeBorder) {
|
||||
frame.InsetBy(-borderSize, -borderSize);
|
||||
|
||||
@@ -15,6 +15,17 @@
|
||||
|
||||
// Command constant for sending utility data to the GUI app
|
||||
const int32 SS_UTILITY_DATA = 'SSUD';
|
||||
// Command constant for retrieving selected area frame from select view
|
||||
const int32 SS_SELECT_AREA_FRAME = 'SAF';
|
||||
// Command constant for launching area selector
|
||||
const int32 SS_LAUNCH_AREA_SELECTOR = 'LAS';
|
||||
|
||||
|
||||
enum ShotType {
|
||||
kActiveWindow = 'aw',
|
||||
kWholeScreen = 'ws',
|
||||
kShowSelectedArea = 'sa'
|
||||
};
|
||||
|
||||
|
||||
class Utility {
|
||||
@@ -25,8 +36,8 @@ public:
|
||||
void CopyToClipboard(const BBitmap& screenshot) const;
|
||||
status_t Save(BBitmap* screenshot, const char* fileName,
|
||||
uint32 imageType) const;
|
||||
BBitmap* MakeScreenshot(bool includeCursor, bool activeWindow,
|
||||
bool includeBorder) const;
|
||||
BBitmap* MakeScreenshot(bool includeCursor, bool includeBorder,
|
||||
ShotType type, BRect selectedArea = BRect(0, 0, -1, -1)) const;
|
||||
BString FileNameExtension(uint32 imageType) const;
|
||||
status_t FindTranslator(uint32 imageType, translator_id& id,
|
||||
BString* _mimeType = NULL) const;
|
||||
|
||||
@@ -3694,6 +3694,16 @@ BWindow::_HandleKeyDown(BMessage* event)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If option is held, then launch the area selector via CLI
|
||||
if ((modifiers & B_OPTION_KEY) != 0) {
|
||||
BMessage message(B_ARGV_RECEIVED);
|
||||
message.AddString("argv", "screenshot");
|
||||
message.AddString("argv", "--area");
|
||||
message.AddInt32("argc", 2);
|
||||
be_roster->Launch("application/x-vnd.haiku-screenshot-cli", &message);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepare a message based on the modifier keys pressed and launch the
|
||||
// screenshot GUI
|
||||
BMessage message(B_ARGV_RECEIVED);
|
||||
|
||||
Reference in New Issue
Block a user