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:
PawanYr
2025-04-26 17:36:09 +00:00
committed by waddlesplash
parent ce9a033cd8
commit 1de993d223
12 changed files with 518 additions and 45 deletions
+1
View File
@@ -7,6 +7,7 @@ UsePrivateHeaders interface shared ;
Application Screenshot :
ScreenshotApp.cpp
ScreenshotWindow.cpp
SelectAreaView.cpp
Utility.cpp
: be localestub tracker translation [ TargetLibsupc++ ]
: ScreenshotApp.rdef
+11 -5
View File
@@ -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;
}
+1
View File
@@ -31,6 +31,7 @@ private:
private:
Utility* fUtility;
bool fLaunchGui;
bool fSelectArea;
};
+43 -2
View File
@@ -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);
}
+4 -1
View File
@@ -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;
};
+85 -25
View File
@@ -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);
+14 -7
View File
@@ -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;
};
+272
View File
@@ -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);
}
}
+49
View File
@@ -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;
};
+15 -3
View File
@@ -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);
+13 -2
View File
@@ -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;
+10
View File
@@ -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);