/* * Copyright 2001 Werner Freytag - please read to the LICENSE file * * Copyright 2002-2015, Stephan Aßmus * All rights reserved. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ColorField.h" #include "ColorPreview.h" #include "ColorSlider.h" #include "rgb_hsv.h" #include "ColorPickerView.h" #define round(x) (int)(x+.5) #define hexdec(str, offset) (int)(((str[offset]<60?str[offset]-48:(str[offset]|32)-87)<<4)|(str[offset+1]<60?str[offset+1]-48:(str[offset+1]|32)-87)) ColorPickerView::ColorPickerView(const char* name, rgb_color color, SelectedColorMode mode) : BView(name, 0), h(0.0), s(1.0), v(1.0), r((float)color.red / 255.0), g((float)color.green / 255.0), b((float)color.blue / 255.0), fRequiresUpdate(false) { SetViewUIColor(B_PANEL_BACKGROUND_COLOR); RGB_to_HSV(r, g, b, h, s, v); SetColorMode(mode, false); } ColorPickerView::~ColorPickerView() { } void ColorPickerView::AttachedToWindow() { rgb_color color = { (uint8)(r * 255), (uint8)(g * 255), (uint8)(b * 255), 255 }; BView::AttachedToWindow(); fColorField = new ColorField(fSelectedColorMode, *p); fColorField->SetMarkerToColor(color); fColorField->SetTarget(this); fColorSlider = new ColorSlider(fSelectedColorMode, *p1, *p2); fColorSlider->SetMarkerToColor(color); fColorSlider->SetTarget(this); fColorPreview = new ColorPreview(color); fColorPreview->SetTarget(this); fColorField->SetExplicitMinSize(BSize(256, 256)); fColorField->SetExplicitMaxSize( BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); fColorSlider->SetExplicitMaxSize( BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED)); fColorPreview->SetExplicitMinSize(BSize(B_SIZE_UNSET, 70)); const char* title[] = { "H", "S", "V", "R", "G", "B" }; int32 selectedRadioButton = _NumForMode(fSelectedColorMode); for (int i = 0; i < 6; i++) { fRadioButton[i] = new BRadioButton(NULL, title[i], new BMessage(MSG_RADIOBUTTON + i)); fRadioButton[i]->SetTarget(this); if (i == selectedRadioButton) fRadioButton[i]->SetValue(1); fTextControl[i] = new BTextControl(NULL, NULL, NULL, new BMessage(MSG_TEXTCONTROL + i)); fTextControl[i]->TextView()->SetMaxBytes(3); for (int j = 32; j < 255; ++j) { if (j < '0' || j > '9') fTextControl[i]->TextView()->DisallowChar(j); } } fHexTextControl = new BTextControl(NULL, "#", NULL, new BMessage(MSG_HEXTEXTCONTROL)); fHexTextControl->TextView()->SetMaxBytes(6); for (int j = 32; j < 255; ++j) { if (!((j >= '0' && j <= '9') || (j >= 'a' && j <= 'f') || (j >= 'A' && j <= 'F'))) { fHexTextControl->TextView()->DisallowChar(j); } } const float inset = be_control_look->DefaultLabelSpacing(); BSize separatorSize(B_SIZE_UNSET, inset / 2); BAlignment separatorAlignment(B_ALIGN_LEFT, B_ALIGN_TOP); SetLayout(new BGroupLayout(B_HORIZONTAL)); BLayoutBuilder::Group<>(this, B_HORIZONTAL) .AddGroup(B_HORIZONTAL, 0.0f) .Add(fColorField) .SetInsets(3, 3, 0, 3) .End() .Add(fColorSlider) .AddGroup(B_VERTICAL) .Add(fColorPreview) .AddGrid(inset / 2, inset / 2) .Add(fRadioButton[0], 0, 0) .Add(fTextControl[0], 1, 0) .Add(new BStringView(NULL, "°"), 2, 0) .Add(fRadioButton[1], 0, 1) .Add(fTextControl[1], 1, 1) .Add(new BStringView(NULL, "%"), 2, 1) .Add(fRadioButton[2], 0, 2) .Add(fTextControl[2], 1, 2) .Add(new BStringView(NULL, "%"), 2, 2) .Add(new BSpaceLayoutItem(separatorSize, separatorSize, separatorSize, separatorAlignment), 0, 3, 3) .Add(fRadioButton[3], 0, 4) .Add(fTextControl[3], 1, 4) .Add(fRadioButton[4], 0, 5) .Add(fTextControl[4], 1, 5) .Add(fRadioButton[5], 0, 6) .Add(fTextControl[5], 1, 6) .Add(new BSpaceLayoutItem(separatorSize, separatorSize, separatorSize, separatorAlignment), 0, 7, 3) .AddGroup(B_HORIZONTAL, 0.0f, 0, 8, 2) .Add(fHexTextControl->CreateLabelLayoutItem()) .Add(fHexTextControl->CreateTextViewLayoutItem()) .End() .End() .SetInsets(0, 3, 3, 3) .End() .SetInsets(inset, inset, inset, inset) ; // After the views are attached, configure their target for (int i = 0; i < 6; i++) { fRadioButton[i]->SetTarget(this); fTextControl[i]->SetTarget(this); } fHexTextControl->SetTarget(this); _UpdateTextControls(); } void ColorPickerView::MessageReceived(BMessage *message) { switch (message->what) { case MSG_UPDATE_COLOR_PICKER_VIEW: if (fRequiresUpdate) _UpdateTextControls(); break; case MSG_COLOR_FIELD: { float value1, value2; value1 = message->FindFloat("value"); value2 = message->FindFloat("value", 1); _UpdateColor(-1, value1, value2); fRequiresUpdate = true; break; } case MSG_COLOR_SLIDER: { float value; message->FindFloat("value", &value); _UpdateColor(value, -1, -1); fRequiresUpdate = true; break; } case MSG_COLOR_PREVIEW: { rgb_color* color; ssize_t numBytes; if (message->FindData("color", B_RGB_COLOR_TYPE, (const void**)&color, &numBytes) == B_OK) { color->alpha = 255; SetColor(*color); } break; } case MSG_RADIOBUTTON: SetColorMode(H_SELECTED); break; case MSG_RADIOBUTTON + 1: SetColorMode(S_SELECTED); break; case MSG_RADIOBUTTON + 2: SetColorMode(V_SELECTED); break; case MSG_RADIOBUTTON + 3: SetColorMode(R_SELECTED); break; case MSG_RADIOBUTTON + 4: SetColorMode(G_SELECTED); break; case MSG_RADIOBUTTON + 5: SetColorMode(B_SELECTED); break; case MSG_TEXTCONTROL: case MSG_TEXTCONTROL + 1: case MSG_TEXTCONTROL + 2: case MSG_TEXTCONTROL + 3: case MSG_TEXTCONTROL + 4: case MSG_TEXTCONTROL + 5: { int nr = message->what - MSG_TEXTCONTROL; int value = atoi(fTextControl[nr]->Text()); switch (nr) { case 0: { value %= 360; h = (float)value / 60; } break; case 1: { value = min_c(value, 100); s = (float)value / 100; } break; case 2: { value = min_c(value, 100); v = (float)value / 100; } break; case 3: { value = min_c(value, 255); r = (float)value / 255; } break; case 4: { value = min_c(value, 255); g = (float)value / 255; } break; case 5: { value = min_c(value, 255); b = (float)value / 255; } break; } if (nr < 3) { // hsv-mode HSV_to_RGB(h, s, v, r, g, b); } rgb_color color = { (uint8)round(r * 255), (uint8)round(g * 255), (uint8)round(b * 255), 255 }; SetColor(color); break; } case MSG_HEXTEXTCONTROL: { BString string = _HexTextControlString(); if (string.Length() == 6) { rgb_color color = { (uint8)hexdec(string, 0), (uint8)hexdec(string, 2), (uint8)hexdec(string, 4), 255 }; SetColor(color); } break; } default: BView::MessageReceived(message); } } void ColorPickerView::SetColorMode(SelectedColorMode mode, bool update) { fSelectedColorMode = mode; switch (mode) { case R_SELECTED: p = &r; p1 = &g; p2 = &b; break; case G_SELECTED: p = &g; p1 = &r; p2 = &b; break; case B_SELECTED: p = &b; p1 = &r; p2 = &g; break; case H_SELECTED: p = &h; p1 = &s; p2 = &v; break; case S_SELECTED: p = &s; p1 = &h; p2 = &v; break; case V_SELECTED: p = &v; p1 = &h; p2 = &s; break; } if (!update) return; fColorSlider->SetModeAndValues(fSelectedColorMode, *p1, *p2); fColorField->SetModeAndValue(fSelectedColorMode, *p); } // SetColor void ColorPickerView::SetColor(rgb_color color) { r = (float)color.red / 255; g = (float)color.green / 255; b = (float)color.blue / 255; RGB_to_HSV(r, g, b, h, s, v); fColorSlider->SetModeAndValues(fSelectedColorMode, *p1, *p2); fColorSlider->SetMarkerToColor(color); fColorField->SetModeAndValue(fSelectedColorMode, *p); fColorField->SetMarkerToColor(color); fColorPreview->SetColor(color); _UpdateTextControls(); } rgb_color ColorPickerView::Color() { if (fSelectedColorMode & (R_SELECTED | G_SELECTED | B_SELECTED)) RGB_to_HSV(r, g, b, h, s, v); else HSV_to_RGB(h, s, v, r, g, b); rgb_color color; color.red = (uint8)round(r * 255.0); color.green = (uint8)round(g * 255.0); color.blue = (uint8)round(b * 255.0); color.alpha = 255; return color; } int32 ColorPickerView::_NumForMode(SelectedColorMode mode) const { int32 num = -1; switch (mode) { case H_SELECTED: num = 0; break; case S_SELECTED: num = 1; break; case V_SELECTED: num = 2; break; case R_SELECTED: num = 3; break; case G_SELECTED: num = 4; break; case B_SELECTED: num = 5; break; } return num; } void ColorPickerView::_UpdateColor(float value, float value1, float value2) { if (value != -1) { fColorField->SetFixedValue(value); *p = value; } else if (value1 != -1 && value2 != -1) { fColorSlider->SetOtherValues(value1, value2); *p1 = value1; *p2 = value2; } if (fSelectedColorMode & (R_SELECTED|G_SELECTED|B_SELECTED)) RGB_to_HSV(r, g, b, h, s, v); else HSV_to_RGB(h, s, v, r, g, b); rgb_color color = { (uint8)(r * 255), (uint8)(g * 255), (uint8)(b * 255), 255 }; fColorPreview->SetColor(color); } void ColorPickerView::_UpdateTextControls() { bool updateRequired = false; updateRequired |= _SetTextControlValue(0, round(h * 60)); updateRequired |= _SetTextControlValue(1, round(s * 100)); updateRequired |= _SetTextControlValue(2, round(v * 100)); updateRequired |= _SetTextControlValue(3, round(r * 255)); updateRequired |= _SetTextControlValue(4, round(g * 255)); updateRequired |= _SetTextControlValue(5, round(b * 255)); BString hexString; hexString.SetToFormat("%.6X", (round(r * 255) << 16) | (round(g * 255) << 8) | round(b * 255)); updateRequired |= _SetHexTextControlString(hexString); fRequiresUpdate = updateRequired; if (fRequiresUpdate) { // Couldn't set all values. Try again later. BMessage message(MSG_UPDATE_COLOR_PICKER_VIEW); BMessageRunner::StartSending(this, &message, 500000, 1); } } int ColorPickerView::_TextControlValue(int32 index) { return atoi(fTextControl[index]->Text()); } // Returns whether the value needs to be set later, since it is currently // being edited by the user. bool ColorPickerView::_SetTextControlValue(int32 index, int value) { BString text; text << value; return _SetText(fTextControl[index], text); } BString ColorPickerView::_HexTextControlString() const { return fHexTextControl->TextView()->Text(); } // Returns whether the value needs to be set later, since it is currently // being edited by the user. bool ColorPickerView::_SetHexTextControlString(const BString& text) { return _SetText(fHexTextControl, text); } // Returns whether the value needs to be set later, since it is currently // being edited by the user. bool ColorPickerView::_SetText(BTextControl* control, const BString& text) { if (text == control->Text()) return false; // This textview needs updating, but don't screw with user while she is // typing. if (control->TextView()->IsFocus()) return true; control->SetText(text.String()); return false; }