diff --git a/src/apps/screenshot/Jamfile b/src/apps/screenshot/Jamfile index 575a5768d1..7195132816 100644 --- a/src/apps/screenshot/Jamfile +++ b/src/apps/screenshot/Jamfile @@ -7,6 +7,7 @@ UsePrivateHeaders interface shared ; Application Screenshot : ScreenshotApp.cpp ScreenshotWindow.cpp + SelectAreaView.cpp Utility.cpp : be localestub tracker translation [ TargetLibsupc++ ] : ScreenshotApp.rdef diff --git a/src/apps/screenshot/Screenshot.cpp b/src/apps/screenshot/Screenshot.cpp index 505d5257cc..fe80e9d573 100644 --- a/src/apps/screenshot/Screenshot.cpp +++ b/src/apps/screenshot/Screenshot.cpp @@ -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; } diff --git a/src/apps/screenshot/Screenshot.h b/src/apps/screenshot/Screenshot.h index c92685c5a4..39cd41a572 100644 --- a/src/apps/screenshot/Screenshot.h +++ b/src/apps/screenshot/Screenshot.h @@ -31,6 +31,7 @@ private: private: Utility* fUtility; bool fLaunchGui; + bool fSelectArea; }; diff --git a/src/apps/screenshot/ScreenshotApp.cpp b/src/apps/screenshot/ScreenshotApp.cpp index 0b5449d57a..815ec3bc55 100644 --- a/src/apps/screenshot/ScreenshotApp.cpp +++ b/src/apps/screenshot/ScreenshotApp.cpp @@ -15,17 +15,22 @@ #include #include #include +#include +#include #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); } diff --git a/src/apps/screenshot/ScreenshotApp.h b/src/apps/screenshot/ScreenshotApp.h index a4302fdb39..18d0ad568a 100644 --- a/src/apps/screenshot/ScreenshotApp.h +++ b/src/apps/screenshot/ScreenshotApp.h @@ -13,9 +13,9 @@ #include +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; }; diff --git a/src/apps/screenshot/ScreenshotWindow.cpp b/src/apps/screenshot/ScreenshotWindow.cpp index 4c7fcaf252..b22a1c585f 100644 --- a/src/apps/screenshot/ScreenshotWindow.cpp +++ b/src/apps/screenshot/ScreenshotWindow.cpp @@ -9,6 +9,7 @@ * Fredrik Modéen * Christophe Huriaux * Wim van der Meer + * Pawan Yerramilli */ @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +45,7 @@ #include #include +#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); diff --git a/src/apps/screenshot/ScreenshotWindow.h b/src/apps/screenshot/ScreenshotWindow.h index 0db27ff7c7..ece08c2d5f 100644 --- a/src/apps/screenshot/ScreenshotWindow.h +++ b/src/apps/screenshot/ScreenshotWindow.h @@ -12,6 +12,9 @@ #define SCREENSHOT_WINDOW_H +#include "Rect.h" +#include "Utility.h" +#include #include #include #include @@ -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; }; diff --git a/src/apps/screenshot/SelectAreaView.cpp b/src/apps/screenshot/SelectAreaView.cpp new file mode 100644 index 0000000000..acb7209ab4 --- /dev/null +++ b/src/apps/screenshot/SelectAreaView.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2025, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Authors: + * Pawan Yerramilli + */ + + +#include "SelectAreaView.h" + +#include +#include +#include +#include +#include + +#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); + } +} diff --git a/src/apps/screenshot/SelectAreaView.h b/src/apps/screenshot/SelectAreaView.h new file mode 100644 index 0000000000..c3323ad695 --- /dev/null +++ b/src/apps/screenshot/SelectAreaView.h @@ -0,0 +1,49 @@ +/* + * Copyright 2025, Haiku, Inc. + * Authors: + * Pawan Yerramilli + * All rights reserved. Distributed under the terms of the MIT license. + */ + + +#include "Point.h" +#include +#include +#include + + +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; +}; diff --git a/src/apps/screenshot/Utility.cpp b/src/apps/screenshot/Utility.cpp index 1c0b3b297c..bdf9c07896 100644 --- a/src/apps/screenshot/Utility.cpp +++ b/src/apps/screenshot/Utility.cpp @@ -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); diff --git a/src/apps/screenshot/Utility.h b/src/apps/screenshot/Utility.h index bfd10fd71a..ad4d0e8343 100644 --- a/src/apps/screenshot/Utility.h +++ b/src/apps/screenshot/Utility.h @@ -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; diff --git a/src/kits/interface/Window.cpp b/src/kits/interface/Window.cpp index 863758e0c1..8adf142e7c 100644 --- a/src/kits/interface/Window.cpp +++ b/src/kits/interface/Window.cpp @@ -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);